diff options
Diffstat (limited to 'fs/locks.c')
| -rw-r--r-- | fs/locks.c | 61 | 
1 files changed, 58 insertions, 3 deletions
diff --git a/fs/locks.c b/fs/locks.c index 8c6df10cd9ed..ca28e0e50e56 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -300,6 +300,34 @@ void locks_release_private(struct file_lock *fl)  }  EXPORT_SYMBOL_GPL(locks_release_private); +/** + * locks_owner_has_blockers - Check for blocking lock requests + * @flctx: file lock context + * @owner: lock owner + * + * Return values: + *   %true: @owner has at least one blocker + *   %false: @owner has no blockers + */ +bool locks_owner_has_blockers(struct file_lock_context *flctx, +		fl_owner_t owner) +{ +	struct file_lock *fl; + +	spin_lock(&flctx->flc_lock); +	list_for_each_entry(fl, &flctx->flc_posix, fl_list) { +		if (fl->fl_owner != owner) +			continue; +		if (!list_empty(&fl->fl_blocked_requests)) { +			spin_unlock(&flctx->flc_lock); +			return true; +		} +	} +	spin_unlock(&flctx->flc_lock); +	return false; +} +EXPORT_SYMBOL_GPL(locks_owner_has_blockers); +  /* Free a lock which is not in use. */  void locks_free_lock(struct file_lock *fl)  { @@ -874,6 +902,8 @@ posix_test_lock(struct file *filp, struct file_lock *fl)  	struct file_lock *cfl;  	struct file_lock_context *ctx;  	struct inode *inode = locks_inode(filp); +	void *owner; +	void (*func)(void);  	ctx = smp_load_acquire(&inode->i_flctx);  	if (!ctx || list_empty_careful(&ctx->flc_posix)) { @@ -881,12 +911,23 @@ posix_test_lock(struct file *filp, struct file_lock *fl)  		return;  	} +retry:  	spin_lock(&ctx->flc_lock);  	list_for_each_entry(cfl, &ctx->flc_posix, fl_list) { -		if (posix_locks_conflict(fl, cfl)) { -			locks_copy_conflock(fl, cfl); -			goto out; +		if (!posix_locks_conflict(fl, cfl)) +			continue; +		if (cfl->fl_lmops && cfl->fl_lmops->lm_lock_expirable +			&& (*cfl->fl_lmops->lm_lock_expirable)(cfl)) { +			owner = cfl->fl_lmops->lm_mod_owner; +			func = cfl->fl_lmops->lm_expire_lock; +			__module_get(owner); +			spin_unlock(&ctx->flc_lock); +			(*func)(); +			module_put(owner); +			goto retry;  		} +		locks_copy_conflock(fl, cfl); +		goto out;  	}  	fl->fl_type = F_UNLCK;  out: @@ -1060,6 +1101,8 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,  	int error;  	bool added = false;  	LIST_HEAD(dispose); +	void *owner; +	void (*func)(void);  	ctx = locks_get_lock_context(inode, request->fl_type);  	if (!ctx) @@ -1078,6 +1121,7 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,  		new_fl2 = locks_alloc_lock();  	} +retry:  	percpu_down_read(&file_rwsem);  	spin_lock(&ctx->flc_lock);  	/* @@ -1089,6 +1133,17 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,  		list_for_each_entry(fl, &ctx->flc_posix, fl_list) {  			if (!posix_locks_conflict(request, fl))  				continue; +			if (fl->fl_lmops && fl->fl_lmops->lm_lock_expirable +				&& (*fl->fl_lmops->lm_lock_expirable)(fl)) { +				owner = fl->fl_lmops->lm_mod_owner; +				func = fl->fl_lmops->lm_expire_lock; +				__module_get(owner); +				spin_unlock(&ctx->flc_lock); +				percpu_up_read(&file_rwsem); +				(*func)(); +				module_put(owner); +				goto retry; +			}  			if (conflock)  				locks_copy_conflock(conflock, fl);  			error = -EAGAIN;  | 
