int cfs_daemonize_ctxt(char *str);
cfs_sigset_t cfs_get_blocked_sigs(void);
cfs_sigset_t cfs_block_allsigs(void);
-cfs_sigset_t cfs_block_sigs(cfs_sigset_t bits);
+cfs_sigset_t cfs_block_sigs(unsigned long sigs);
cfs_sigset_t cfs_block_sigsinv(unsigned long sigs);
void cfs_restore_sigs(cfs_sigset_t);
int cfs_signal_pending(void);
return old;
}
-cfs_sigset_t cfs_block_sigs(sigset_t bit)
+cfs_sigset_t cfs_block_sigs(unsigned long sigs)
{
- cfs_sigset_t old = 0;
+ cfs_sigset_t old = 0;
#ifdef __DARWIN8__
#else
- block_procsigmask(current_proc(), bit);
+ block_procsigmask(current_proc(), sigs);
#endif
- return old;
+ return old;
}
/* Block all signals except for the @sigs. It's only used in
return old;
}
-sigset_t
-cfs_block_sigs(sigset_t bits)
+sigset_t cfs_block_sigs(unsigned long sigs)
{
- unsigned long flags;
- sigset_t old;
+ unsigned long flags;
+ sigset_t old;
- SIGNAL_MASK_LOCK(current, flags);
- old = current->blocked;
- current->blocked = bits;
- RECALC_SIGPENDING;
- SIGNAL_MASK_UNLOCK(current, flags);
- return old;
+ SIGNAL_MASK_LOCK(current, flags);
+ old = current->blocked;
+ sigaddsetmask(¤t->blocked, sigs);
+ RECALC_SIGPENDING;
+ SIGNAL_MASK_UNLOCK(current, flags);
+ return old;
}
/* Block all signals except for the @sigs */
-cfs_sigset_t
-cfs_block_sigsinv(unsigned long sigs)
+sigset_t cfs_block_sigsinv(unsigned long sigs)
{
- unsigned long flags;
- cfs_sigset_t old;
+ unsigned long flags;
+ sigset_t old;
- SIGNAL_MASK_LOCK(current, flags);
- old = current->blocked;
- siginitsetinv(¤t->blocked, sigs);
- RECALC_SIGPENDING;
- SIGNAL_MASK_UNLOCK(current, flags);
+ SIGNAL_MASK_LOCK(current, flags);
+ old = current->blocked;
+ sigaddsetmask(¤t->blocked, ~sigs);
+ RECALC_SIGPENDING;
+ SIGNAL_MASK_UNLOCK(current, flags);
- return old;
+ return old;
}
void
cfs_sigset_t cfs_block_allsigs(void)
{
- cfs_sigset_t all;
- cfs_sigset_t old;
- int rc;
+ cfs_sigset_t all;
+ cfs_sigset_t old;
+ int rc;
- sigfillset(&all);
- rc = sigprocmask(SIG_SETMASK, &all, &old);
- LASSERT(rc == 0);
+ sigfillset(&all);
+ rc = sigprocmask(SIG_BLOCK, &all, &old);
+ LASSERT(rc == 0);
- return old;
+ return old;
}
-cfs_sigset_t cfs_block_sigs(cfs_sigset_t blocks)
+cfs_sigset_t cfs_block_sigs(unsigned long sigs)
{
- cfs_sigset_t old;
- int rc;
+ cfs_sigset_t old;
+ cfs_sigset_t blocks = { { sigs } }; /* kludge */
+ int rc;
- rc = sigprocmask(SIG_SETMASK, &blocks, &old);
- LASSERT (rc == 0);
+ rc = sigprocmask(SIG_BLOCK, &blocks, &old);
+ LASSERT (rc == 0);
- return old;
+ return old;
}
/* Block all signals except for the @sigs. It's only used in
} \
} while (0)
+#define CL_LOCK_ASSERT(expr, env, lock) do { \
+ if (likely(expr)) \
+ break; \
+ \
+ CL_LOCK_DEBUG(D_ERROR, env, lock, "failed at %s.\n", #expr); \
+ LBUG(); \
+} while (0)
+
/** @} cl_lock */
/** \addtogroup cl_page_list cl_page_list
} \
} \
\
- cfs_block_sigs(__blocked); \
+ cfs_restore_sigs(__blocked); \
\
cfs_set_current_state(CFS_TASK_RUNNING); \
cfs_waitq_del(&wq, &__wait); \
#define OBD_FAIL_LLITE 0x1400
#define OBD_FAIL_LLITE_FAULT_TRUNC_RACE 0x1401
+#define OBD_FAIL_LOCK_STATE_WAIT_INTR 0x1402
/* Assign references to moved code to reduce code changes */
#define OBD_FAIL_PRECHECK(id) CFS_FAIL_PRECHECK(id)
struct vvp_io *vio;
struct cl_env_nest nest;
int result;
+ cfs_sigset_t set;
ENTRY;
LASSERT(vmpage != NULL);
vio->u.fault.ft_vma = vma;
vio->u.fault.ft_vmpage = vmpage;
- result = cl_io_loop(env, io);
+ set = cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM));
+ result = cl_io_loop(env, io);
+ cfs_restore_sigs(set);
if (result == -ENODATA) /* peek failed, no lock caching. */
CDEBUG(D_MMAP, "race on page_mkwrite: %lx (%lu %p)\n",
pgoff_t pg_offset;
int result;
const unsigned long writable = VM_SHARED|VM_WRITE;
+ cfs_sigset_t set;
ENTRY;
pg_offset = ((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
vio->u.fault.nopage.ft_type = type;
vio->u.fault.ft_vmpage = NULL;
- result = cl_io_loop(env, io);
- page = vio->u.fault.ft_vmpage;
- if (result != 0 && page != NULL)
- page_cache_release(page);
+ set = cfs_block_sigsinv(sigmask(SIGKILL)|sigmask(SIGTERM));
+ result = cl_io_loop(env, io);
+ cfs_restore_sigs(set);
+
+ page = vio->u.fault.ft_vmpage;
+ if (result != 0 && page != NULL) {
+ page_cache_release(page);
+ page = NOPAGE_SIGBUS;
+ }
out_err:
if (result == -ENOMEM)
}
#else
+
+static inline int to_fault_error(int result)
+{
+ switch(result) {
+ case 0:
+ result = VM_FAULT_LOCKED;
+ break;
+ case -EFAULT:
+ result = VM_FAULT_NOPAGE;
+ break;
+ case -ENOMEM:
+ result = VM_FAULT_OOM;
+ break;
+ default:
+ result = VM_FAULT_SIGBUS;
+ break;
+ }
+ return result;
+}
+
/**
* Lustre implementation of a vm_operations_struct::fault() method, called by
* VM to server page fault (both in kernel and user space).
io = ll_fault_io_init(vma, &env, &nest, vmf->pgoff, &ra_flags);
if (IS_ERR(io))
- RETURN(VM_FAULT_ERROR);
+ RETURN(to_fault_error(PTR_ERR(io)));
result = io->ci_result;
- if (result < 0)
- goto out_err;
-
- vio = vvp_env_io(env);
- vio->u.fault.ft_vma = vma;
- vio->u.fault.ft_vmpage = NULL;
- vio->u.fault.fault.ft_vmf = vmf;
-
- result = cl_io_loop(env, io);
-
- vmpage = vio->u.fault.ft_vmpage;
- if (result != 0 && vmpage != NULL) {
- page_cache_release(vmpage);
- vmf->page = NULL;
+ if (result == 0) {
+ vio = vvp_env_io(env);
+ vio->u.fault.ft_vma = vma;
+ vio->u.fault.ft_vmpage = NULL;
+ vio->u.fault.fault.ft_vmf = vmf;
+
+ result = cl_io_loop(env, io);
+
+ fault_ret = vio->u.fault.fault.ft_flags;
+ vmpage = vio->u.fault.ft_vmpage;
+ if (result != 0 && vmpage != NULL) {
+ page_cache_release(vmpage);
+ vmf->page = NULL;
+ }
}
-
- fault_ret = vio->u.fault.fault.ft_flags;
-
-out_err:
- if (result != 0 && fault_ret == 0)
- fault_ret = VM_FAULT_ERROR;
-
- vma->vm_flags |= ra_flags;
-
cl_io_fini(env, io);
cl_env_nested_put(&nest, env);
+ vma->vm_flags |= ra_flags;
+ if (result != 0 && !(fault_ret & VM_FAULT_RETRY))
+ fault_ret |= to_fault_error(result);
+
CDEBUG(D_MMAP, "%s fault %d/%d\n",
cfs_current()->comm, fault_ret, result);
RETURN(fault_ret);
static int ll_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
- int count = 0;
- bool printed = false;
- int result;
+ int count = 0;
+ bool printed = false;
+ int result;
+ cfs_sigset_t set;
+
+ /* Only SIGKILL and SIGTERM is allowed for fault/nopage/mkwrite
+ * so that it can be killed by admin but not cause segfault by
+ * other signals. */
+ set = cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM));
restart:
result = ll_fault0(vma, vmf);
result |= VM_FAULT_LOCKED;
}
+ cfs_restore_sigs(set);
return result;
}
#endif
switch(sublock->cll_state) {
case CLS_HELD:
- rc = cl_unuse_try(subenv->lse_env,
- sublock);
+ rc = cl_unuse_try(subenv->lse_env, sublock);
lov_sublock_release(env, lck, i, 0, 0);
break;
- case CLS_ENQUEUED:
- /* TODO: it's not a good idea to cancel this
- * lock because it's innocent. But it's
- * acceptable. The better way would be to
- * define a new lock method to unhold the
- * dlm lock. */
- cl_lock_cancel(env, sublock);
default:
lov_sublock_release(env, lck, i, 1, 0);
break;
result = 0;
switch (parent->cll_state) {
+ case CLS_ENQUEUED:
+ /* See LU-1355 for the case that a glimpse lock is
+ * interrupted by signal */
+ LASSERT(parent->cll_flags & CLF_CANCELLED);
+ break;
case CLS_QUEUING:
case CLS_FREEING:
cl_lock_signal(env, parent);
}
}
break;
- case CLS_ENQUEUED:
case CLS_HELD:
CL_LOCK_DEBUG(D_ERROR, env, parent, "Delete CLS_HELD lock\n");
default:
cl_lock_mutex_get(env, lock);
if (lock->cll_state == CLS_INTRANSIT)
cl_lock_state_wait(env, lock); /* Don't care return value. */
- if (lock->cll_state == CLS_CACHED) {
- int result;
- result = cl_use_try(env, lock, 1);
- if (result < 0)
- cl_lock_error(env, lock, result);
- }
- if (lock->cll_state == CLS_HELD) {
- cl_lock_hold_add(env, lock, scope, source);
- cl_lock_user_add(env, lock);
- cl_lock_mutex_put(env, lock);
- cl_lock_lockdep_acquire(env, lock, 0);
- cl_lock_put(env, lock);
- } else {
+ cl_lock_hold_add(env, lock, scope, source);
+ cl_lock_user_add(env, lock);
+ if (lock->cll_state == CLS_CACHED)
+ cl_use_try(env, lock, 1);
+ if (lock->cll_state == CLS_HELD) {
+ cl_lock_mutex_put(env, lock);
+ cl_lock_lockdep_acquire(env, lock, 0);
+ cl_lock_put(env, lock);
+ } else {
+ cl_unuse_try(env, lock);
+ cl_lock_unhold(env, lock, scope, source);
cl_lock_mutex_put(env, lock);
cl_lock_put(env, lock);
lock = NULL;
lu_ref_del(&lock->cll_holders, scope, source);
cl_lock_hold_mod(env, lock, -1);
if (lock->cll_holds == 0) {
- if (lock->cll_descr.cld_mode == CLM_PHANTOM ||
- lock->cll_descr.cld_mode == CLM_GROUP)
+ CL_LOCK_ASSERT(lock->cll_state != CLS_HELD, env, lock);
+ if (lock->cll_descr.cld_mode == CLM_PHANTOM ||
+ lock->cll_descr.cld_mode == CLM_GROUP ||
+ lock->cll_state != CLS_CACHED)
/*
* If lock is still phantom or grouplock when user is
* done with it---destroy the lock.
cl_lock_mutex_put(env, lock);
LASSERT(cl_lock_nr_mutexed(env) == 0);
- cfs_waitq_wait(&waiter, CFS_TASK_INTERRUPTIBLE);
+
+ result = -EINTR;
+ if (likely(!OBD_FAIL_CHECK(OBD_FAIL_LOCK_STATE_WAIT_INTR))) {
+ cfs_waitq_wait(&waiter, CFS_TASK_INTERRUPTIBLE);
+ if (!cfs_signal_pending())
+ result = 0;
+ }
cl_lock_mutex_get(env, lock);
cfs_set_current_state(CFS_TASK_RUNNING);
cfs_waitq_del(&lock->cll_wq, &waiter);
- result = cfs_signal_pending() ? -EINTR : 0;
/* Restore old blocked signals */
cfs_restore_sigs(blocked);
ENTRY;
cl_lock_trace(D_DLMTRACE, env, "enqueue lock", lock);
do {
- result = 0;
-
LINVRNT(cl_lock_is_mutexed(lock));
- if (lock->cll_error != 0)
+ result = lock->cll_error;
+ if (result != 0)
break;
+
switch (lock->cll_state) {
case CLS_NEW:
cl_lock_state_set(env, lock, CLS_QUEUING);
LBUG();
}
} while (result == CLO_REPEAT);
- if (result < 0)
- cl_lock_error(env, lock, result);
- RETURN(result ?: lock->cll_error);
+ RETURN(result);
}
EXPORT_SYMBOL(cl_enqueue_try);
LASSERT(cl_lock_nr_mutexed(env) == 0);
cl_lock_mutex_get(env, conflict);
+ cl_lock_trace(D_DLMTRACE, env, "enqueue wait", conflict);
cl_lock_cancel(env, conflict);
cl_lock_delete(env, conflict);
}
break;
} while (1);
- if (result != 0) {
- cl_lock_user_del(env, lock);
- cl_lock_error(env, lock, result);
- }
+ if (result != 0)
+ cl_unuse_try(env, lock);
LASSERT(ergo(result == 0 && !(enqflags & CEF_AGL),
lock->cll_state == CLS_ENQUEUED ||
lock->cll_state == CLS_HELD));
/**
* Tries to unlock a lock.
*
- * This function is called repeatedly by cl_unuse() until either lock is
- * unlocked, or error occurs.
- * cl_unuse_try is a one-shot operation, so it must NOT return CLO_WAIT.
- *
- * \pre lock->cll_state == CLS_HELD
+ * This function is called to release underlying resource:
+ * 1. for top lock, the resource is sublocks it held;
+ * 2. for sublock, the resource is the reference to dlmlock.
*
- * \post ergo(result == 0, lock->cll_state == CLS_CACHED)
+ * cl_unuse_try is a one-shot operation, so it must NOT return CLO_WAIT.
*
* \see cl_unuse() cl_lock_operations::clo_unuse()
* \see cl_lock_state::CLS_CACHED
ENTRY;
cl_lock_trace(D_DLMTRACE, env, "unuse lock", lock);
- LASSERT(lock->cll_state == CLS_HELD || lock->cll_state == CLS_ENQUEUED);
if (lock->cll_users > 1) {
cl_lock_user_del(env, lock);
RETURN(0);
}
+ /* Only if the lock is in CLS_HELD or CLS_ENQUEUED state, it can hold
+ * underlying resources. */
+ if (!(lock->cll_state == CLS_HELD || lock->cll_state == CLS_ENQUEUED)) {
+ cl_lock_user_del(env, lock);
+ RETURN(0);
+ }
+
/*
* New lock users (->cll_users) are not protecting unlocking
* from proceeding. From this point, lock eventually reaches
result = 0;
} else {
CERROR("result = %d, this is unlikely!\n", result);
+ state = CLS_NEW;
cl_lock_extransit(env, lock, state);
}
-
- result = result ?: lock->cll_error;
- if (result < 0)
- cl_lock_error(env, lock, result);
- RETURN(result);
+ RETURN(result ?: lock->cll_error);
}
EXPORT_SYMBOL(cl_unuse_try);
LASSERT(lock->cll_users > 0);
LASSERT(lock->cll_holds > 0);
- result = 0;
- if (lock->cll_error != 0)
+ result = lock->cll_error;
+ if (result != 0)
break;
if (cl_lock_is_intransit(lock)) {
cl_lock_state_set(env, lock, CLS_HELD);
}
} while (result == CLO_REPEAT);
- RETURN(result ?: lock->cll_error);
+ RETURN(result);
}
EXPORT_SYMBOL(cl_wait_try);
break;
} while (1);
if (result < 0) {
- cl_lock_user_del(env, lock);
- cl_lock_error(env, lock, result);
+ cl_unuse_try(env, lock);
cl_lock_lockdep_release(env, lock);
}
cl_lock_trace(D_DLMTRACE, env, "wait lock", lock);
LINVRNT(cl_lock_invariant(env, lock));
ENTRY;
- cl_lock_trace(D_DLMTRACE, env, "set lock error", lock);
if (lock->cll_error == 0 && error != 0) {
+ cl_lock_trace(D_DLMTRACE, env, "set lock error", lock);
lock->cll_error = error;
cl_lock_signal(env, lock);
cl_lock_cancel(env, lock);
(mnt_get_count(mnt) == 1),
cfs_time_seconds(3),
rc);
- cfs_block_sigs(blocked);
+ cfs_restore_sigs(blocked);
if (rc < 0) {
LCONSOLE_EMERG("Danger: interrupted umount %s with "
"%d refs!\n", mnt->mnt_devname,
LASSERT(!ols->ols_hold);
LASSERT(ols->ols_agl);
return 0;
+ case OLS_ENQUEUED:
case OLS_UPCALL_RECEIVED:
LASSERT(!ols->ols_hold);
ols->ols_state = OLS_NEW;
if (need->cld_enq_flags & CEF_NEVER)
return 0;
+ if (ols->ols_state >= OLS_CANCELLED)
+ return 0;
+
if (need->cld_mode == CLM_PHANTOM) {
if (ols->ols_agl)
return !(ols->ols_state > OLS_RELEASED);
* reentrant from userspace again */
if (cfs_signal_pending())
ptlrpc_interrupted_set(set);
- cfs_block_sigs(blocked_sigs);
+ cfs_restore_sigs(blocked_sigs);
}
LASSERT(rc == 0 || rc == -EINTR || rc == -ETIMEDOUT);
}
run_test 226 "call path2fid and fid2path on files of all type"
+# LU-1299 Executing or running ldd on a truncated executable does not
+# cause an out-of-memory condition.
+test_227() {
+ dd if=`which date` of=$MOUNT/date bs=1k count=1
+ chmod +x $MOUNT/date
+
+ $MOUNT/date > /dev/null
+ ldd $MOUNT/date > /dev/null
+ rm -f $MOUNT/date
+}
+run_test 227 "running truncated executable does not cause OOM"
+
#
# tests that do cleanup/setup should be run at the end
#