diff options
Diffstat (limited to 'fs/dlm/user.c')
| -rw-r--r-- | fs/dlm/user.c | 229 | 
1 files changed, 83 insertions, 146 deletions
diff --git a/fs/dlm/user.c b/fs/dlm/user.c index 66d6c16bf44..142e21655ee 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c @@ -24,6 +24,7 @@  #include "lock.h"  #include "lvb_table.h"  #include "user.h" +#include "ast.h"  static const char name_prefix[] = "dlm";  static const struct file_operations device_fops; @@ -152,19 +153,16 @@ static void compat_output(struct dlm_lock_result *res,     not related to the lifetime of the lkb struct which is managed     entirely by refcount. */ -static int lkb_is_endoflife(struct dlm_lkb *lkb, int sb_status, int type) +static int lkb_is_endoflife(int mode, int status)  { -	switch (sb_status) { +	switch (status) {  	case -DLM_EUNLOCK:  		return 1;  	case -DLM_ECANCEL:  	case -ETIMEDOUT:  	case -EDEADLK: -		if (lkb->lkb_grmode == DLM_LOCK_IV) -			return 1; -		break;  	case -EAGAIN: -		if (type == AST_COMP && lkb->lkb_grmode == DLM_LOCK_IV) +		if (mode == DLM_LOCK_IV)  			return 1;  		break;  	} @@ -174,12 +172,13 @@ static int lkb_is_endoflife(struct dlm_lkb *lkb, int sb_status, int type)  /* we could possibly check if the cancel of an orphan has resulted in the lkb     being removed and then remove that lkb from the orphans list and free it */ -void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode) +void dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode, +		      int status, uint32_t sbflags, uint64_t seq)  {  	struct dlm_ls *ls;  	struct dlm_user_args *ua;  	struct dlm_user_proc *proc; -	int eol = 0, ast_type; +	int rv;  	if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD))  		return; @@ -200,49 +199,29 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode)  	ua = lkb->lkb_ua;  	proc = ua->proc; -	if (type == AST_BAST && ua->bastaddr == NULL) +	if ((flags & DLM_CB_BAST) && ua->bastaddr == NULL)  		goto out; +	if ((flags & DLM_CB_CAST) && lkb_is_endoflife(mode, status)) +		lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; +  	spin_lock(&proc->asts_spin); -	ast_type = lkb->lkb_ast_type; -	lkb->lkb_ast_type |= type; -	if (type == AST_BAST) -		lkb->lkb_bastmode = mode; -	else -		lkb->lkb_castmode = mode; +	rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, seq); +	if (rv < 0) { +		spin_unlock(&proc->asts_spin); +		goto out; +	} -	if (!ast_type) { +	if (list_empty(&lkb->lkb_cb_list)) {  		kref_get(&lkb->lkb_ref); -		list_add_tail(&lkb->lkb_astqueue, &proc->asts); -		lkb->lkb_ast_first = type; +		list_add_tail(&lkb->lkb_cb_list, &proc->asts);  		wake_up_interruptible(&proc->wait);  	} -	if (type == AST_COMP && (ast_type & AST_COMP)) -		log_debug(ls, "ast overlap %x status %x %x", -			  lkb->lkb_id, ua->lksb.sb_status, lkb->lkb_flags); - -	eol = lkb_is_endoflife(lkb, ua->lksb.sb_status, type); -	if (eol) { -		lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; -	} - -	/* We want to copy the lvb to userspace when the completion -	   ast is read if the status is 0, the lock has an lvb and -	   lvb_ops says we should.  We could probably have set_lvb_lock() -	   set update_user_lvb instead and not need old_mode */ - -	if ((lkb->lkb_ast_type & AST_COMP) && -	    (lkb->lkb_lksb->sb_status == 0) && -	    lkb->lkb_lksb->sb_lvbptr && -	    dlm_lvb_operations[ua->old_mode + 1][lkb->lkb_grmode + 1]) -		ua->update_user_lvb = 1; -	else -		ua->update_user_lvb = 0; -  	spin_unlock(&proc->asts_spin); -	if (eol) { +	if (lkb->lkb_flags & DLM_IFL_ENDOFLIFE) { +		/* N.B. spin_lock locks_spin, not asts_spin */  		spin_lock(&proc->locks_spin);  		if (!list_empty(&lkb->lkb_ownqueue)) {  			list_del_init(&lkb->lkb_ownqueue); @@ -413,8 +392,9 @@ static int device_create_lockspace(struct dlm_lspace_params *params)  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	error = dlm_new_lockspace(params->name, strlen(params->name), -				  &lockspace, params->flags, DLM_USER_LVB_LEN); +	error = dlm_new_lockspace(params->name, NULL, params->flags, +				  DLM_USER_LVB_LEN, NULL, NULL, NULL, +				  &lockspace);  	if (error)  		return error; @@ -513,7 +493,6 @@ static ssize_t device_write(struct file *file, const char __user *buf,  {  	struct dlm_user_proc *proc = file->private_data;  	struct dlm_write_request *kbuf; -	sigset_t tmpsig, allsigs;  	int error;  #ifdef CONFIG_COMPAT @@ -523,6 +502,13 @@ static ssize_t device_write(struct file *file, const char __user *buf,  #endif  		return -EINVAL; +	/* +	 * can't compare against COMPAT/dlm_write_request32 because +	 * we don't yet know if is64bit is zero +	 */ +	if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN) +		return -EINVAL; +  	kbuf = kzalloc(count + 1, GFP_NOFS);  	if (!kbuf)  		return -ENOMEM; @@ -570,9 +556,6 @@ static ssize_t device_write(struct file *file, const char __user *buf,  		goto out_free;  	} -	sigfillset(&allsigs); -	sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); -  	error = -EINVAL;  	switch (kbuf->cmd) @@ -580,7 +563,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,  	case DLM_USER_LOCK:  		if (!proc) {  			log_print("no locking on control device"); -			goto out_sig; +			goto out_free;  		}  		error = device_user_lock(proc, &kbuf->i.lock);  		break; @@ -588,7 +571,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,  	case DLM_USER_UNLOCK:  		if (!proc) {  			log_print("no locking on control device"); -			goto out_sig; +			goto out_free;  		}  		error = device_user_unlock(proc, &kbuf->i.lock);  		break; @@ -596,7 +579,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,  	case DLM_USER_DEADLOCK:  		if (!proc) {  			log_print("no locking on control device"); -			goto out_sig; +			goto out_free;  		}  		error = device_user_deadlock(proc, &kbuf->i.lock);  		break; @@ -604,7 +587,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,  	case DLM_USER_CREATE_LOCKSPACE:  		if (proc) {  			log_print("create/remove only on control device"); -			goto out_sig; +			goto out_free;  		}  		error = device_create_lockspace(&kbuf->i.lspace);  		break; @@ -612,7 +595,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,  	case DLM_USER_REMOVE_LOCKSPACE:  		if (proc) {  			log_print("create/remove only on control device"); -			goto out_sig; +			goto out_free;  		}  		error = device_remove_lockspace(&kbuf->i.lspace);  		break; @@ -620,7 +603,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,  	case DLM_USER_PURGE:  		if (!proc) {  			log_print("no locking on control device"); -			goto out_sig; +			goto out_free;  		}  		error = device_user_purge(proc, &kbuf->i.purge);  		break; @@ -630,9 +613,6 @@ static ssize_t device_write(struct file *file, const char __user *buf,  			  kbuf->cmd);  	} - out_sig: -	sigprocmask(SIG_SETMASK, &tmpsig, NULL); -	recalc_sigpending();   out_free:  	kfree(kbuf);  	return error; @@ -673,15 +653,11 @@ static int device_close(struct inode *inode, struct file *file)  {  	struct dlm_user_proc *proc = file->private_data;  	struct dlm_ls *ls; -	sigset_t tmpsig, allsigs;  	ls = dlm_find_lockspace_local(proc->lockspace);  	if (!ls)  		return -ENOENT; -	sigfillset(&allsigs); -	sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); -  	set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags);  	dlm_clear_proc_locks(ls, proc); @@ -699,14 +675,12 @@ static int device_close(struct inode *inode, struct file *file)  	/* FIXME: AUTOFREE: if this ls is no longer used do  	   device_remove_lockspace() */ -	sigprocmask(SIG_SETMASK, &tmpsig, NULL); -	recalc_sigpending(); -  	return 0;  } -static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, -			       int mode, char __user *buf, size_t count) +static int copy_result_to_user(struct dlm_user_args *ua, int compat, +			       uint32_t flags, int mode, int copy_lvb, +			       char __user *buf, size_t count)  {  #ifdef CONFIG_COMPAT  	struct dlm_lock_result32 result32; @@ -730,7 +704,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,  	   notes that a new blocking AST address and parameter are set even if  	   the conversion fails, so maybe we should just do that. */ -	if (type == AST_BAST) { +	if (flags & DLM_CB_BAST) {  		result.user_astaddr = ua->bastaddr;  		result.user_astparam = ua->bastparam;  		result.bast_mode = mode; @@ -750,8 +724,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,  	/* copy lvb to userspace if there is one, it's been updated, and  	   the user buffer has space for it */ -	if (ua->update_user_lvb && ua->lksb.sb_lvbptr && -	    count >= len + DLM_USER_LVB_LEN) { +	if (copy_lvb && ua->lksb.sb_lvbptr && count >= len + DLM_USER_LVB_LEN) {  		if (copy_to_user(buf+len, ua->lksb.sb_lvbptr,  				 DLM_USER_LVB_LEN)) {  			error = -EFAULT; @@ -801,13 +774,12 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,  	struct dlm_user_proc *proc = file->private_data;  	struct dlm_lkb *lkb;  	DECLARE_WAITQUEUE(wait, current); -	int error = 0, removed; -	int ret_type, ret_mode; -	int bastmode, castmode, do_bast, do_cast; +	struct dlm_callback cb; +	int rv, resid, copy_lvb = 0;  	if (count == sizeof(struct dlm_device_version)) { -		error = copy_version_to_user(buf, count); -		return error; +		rv = copy_version_to_user(buf, count); +		return rv;  	}  	if (!proc) { @@ -854,92 +826,57 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,  		}  	} -	/* there may be both completion and blocking asts to return for -	   the lkb, don't remove lkb from asts list unless no asts remain */ - -	lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue); - -	removed = 0; -	ret_type = 0; -	ret_mode = 0; -	do_bast = lkb->lkb_ast_type & AST_BAST; -	do_cast = lkb->lkb_ast_type & AST_COMP; -	bastmode = lkb->lkb_bastmode; -	castmode = lkb->lkb_castmode; - -	/* when both are queued figure out which to do first and -	   switch first so the other goes in the next read */ - -	if (do_cast && do_bast) { -		if (lkb->lkb_ast_first == AST_COMP) { -			ret_type = AST_COMP; -			ret_mode = castmode; -			lkb->lkb_ast_type &= ~AST_COMP; -			lkb->lkb_ast_first = AST_BAST; -		} else { -			ret_type = AST_BAST; -			ret_mode = bastmode; -			lkb->lkb_ast_type &= ~AST_BAST; -			lkb->lkb_ast_first = AST_COMP; -		} -	} else { -		ret_type = lkb->lkb_ast_first; -		ret_mode = (ret_type == AST_COMP) ? castmode : bastmode; -		lkb->lkb_ast_type &= ~ret_type; -		lkb->lkb_ast_first = 0; -	} - -	/* if we're doing a bast but the bast is unnecessary, then -	   switch to do nothing or do a cast if that was needed next */ +	/* if we empty lkb_callbacks, we don't want to unlock the spinlock +	   without removing lkb_cb_list; so empty lkb_cb_list is always +	   consistent with empty lkb_callbacks */ -	if ((ret_type == AST_BAST) && -	    dlm_modes_compat(bastmode, lkb->lkb_castmode_done)) { -		ret_type = 0; -		ret_mode = 0; +	lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_cb_list); -		if (do_cast) { -			ret_type = AST_COMP; -			ret_mode = castmode; -			lkb->lkb_ast_type &= ~AST_COMP; -			lkb->lkb_ast_first = 0; -		} +	rv = dlm_rem_lkb_callback(lkb->lkb_resource->res_ls, lkb, &cb, &resid); +	if (rv < 0) { +		/* this shouldn't happen; lkb should have been removed from +		   list when resid was zero */ +		log_print("dlm_rem_lkb_callback empty %x", lkb->lkb_id); +		list_del_init(&lkb->lkb_cb_list); +		spin_unlock(&proc->asts_spin); +		/* removes ref for proc->asts, may cause lkb to be freed */ +		dlm_put_lkb(lkb); +		goto try_another;  	} +	if (!resid) +		list_del_init(&lkb->lkb_cb_list); +	spin_unlock(&proc->asts_spin); -	if (lkb->lkb_ast_first != lkb->lkb_ast_type) { -		log_print("device_read %x ast_first %x ast_type %x", -			  lkb->lkb_id, lkb->lkb_ast_first, lkb->lkb_ast_type); +	if (cb.flags & DLM_CB_SKIP) { +		/* removes ref for proc->asts, may cause lkb to be freed */ +		if (!resid) +			dlm_put_lkb(lkb); +		goto try_another;  	} -	if (!lkb->lkb_ast_type) { -		list_del(&lkb->lkb_astqueue); -		removed = 1; -	} -	spin_unlock(&proc->asts_spin); +	if (cb.flags & DLM_CB_CAST) { +		int old_mode, new_mode; + +		old_mode = lkb->lkb_last_cast.mode; +		new_mode = cb.mode; -	if (ret_type) { -		error = copy_result_to_user(lkb->lkb_ua, -				test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), -				ret_type, ret_mode, buf, count); +		if (!cb.sb_status && lkb->lkb_lksb->sb_lvbptr && +		    dlm_lvb_operations[old_mode + 1][new_mode + 1]) +			copy_lvb = 1; -		if (ret_type == AST_COMP) -			lkb->lkb_castmode_done = castmode; -		if (ret_type == AST_BAST) -			lkb->lkb_bastmode_done = bastmode; +		lkb->lkb_lksb->sb_status = cb.sb_status; +		lkb->lkb_lksb->sb_flags = cb.sb_flags;  	} -	/* removes reference for the proc->asts lists added by -	   dlm_user_add_ast() and may result in the lkb being freed */ +	rv = copy_result_to_user(lkb->lkb_ua, +				 test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), +				 cb.flags, cb.mode, copy_lvb, buf, count); -	if (removed) +	/* removes ref for proc->asts, may cause lkb to be freed */ +	if (!resid)  		dlm_put_lkb(lkb); -	/* the bast that was queued was eliminated (see unnecessary above), -	   leaving nothing to return */ - -	if (!ret_type) -		goto try_another; - -	return error; +	return rv;  }  static unsigned int device_poll(struct file *file, poll_table *wait)  | 
