diff options
Diffstat (limited to 'fs/locks.c')
| -rw-r--r-- | fs/locks.c | 73 | 
1 files changed, 68 insertions, 5 deletions
diff --git a/fs/locks.c b/fs/locks.c index 686eae21daf6..6970f55daf54 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -212,6 +212,7 @@ struct file_lock_list_struct {  static DEFINE_PER_CPU(struct file_lock_list_struct, file_lock_list);  DEFINE_STATIC_PERCPU_RWSEM(file_rwsem); +  /*   * The blocked_hash is used to find POSIX lock loops for deadlock detection.   * It is protected by blocked_lock_lock. @@ -1592,7 +1593,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)  	ctx = smp_load_acquire(&inode->i_flctx);  	if (!ctx) {  		WARN_ON_ONCE(1); -		return error; +		goto free_lock;  	}  	percpu_down_read(&file_rwsem); @@ -1672,6 +1673,7 @@ out:  	spin_unlock(&ctx->flc_lock);  	percpu_up_read(&file_rwsem);  	locks_dispose_list(&dispose); +free_lock:  	locks_free_lock(new_fl);  	return error;  } @@ -1990,6 +1992,64 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp,  }  EXPORT_SYMBOL(generic_setlease); +#if IS_ENABLED(CONFIG_SRCU) +/* + * Kernel subsystems can register to be notified on any attempt to set + * a new lease with the lease_notifier_chain. This is used by (e.g.) nfsd + * to close files that it may have cached when there is an attempt to set a + * conflicting lease. + */ +static struct srcu_notifier_head lease_notifier_chain; + +static inline void +lease_notifier_chain_init(void) +{ +	srcu_init_notifier_head(&lease_notifier_chain); +} + +static inline void +setlease_notifier(long arg, struct file_lock *lease) +{ +	if (arg != F_UNLCK) +		srcu_notifier_call_chain(&lease_notifier_chain, arg, lease); +} + +int lease_register_notifier(struct notifier_block *nb) +{ +	return srcu_notifier_chain_register(&lease_notifier_chain, nb); +} +EXPORT_SYMBOL_GPL(lease_register_notifier); + +void lease_unregister_notifier(struct notifier_block *nb) +{ +	srcu_notifier_chain_unregister(&lease_notifier_chain, nb); +} +EXPORT_SYMBOL_GPL(lease_unregister_notifier); + +#else /* !IS_ENABLED(CONFIG_SRCU) */ +static inline void +lease_notifier_chain_init(void) +{ +} + +static inline void +setlease_notifier(long arg, struct file_lock *lease) +{ +} + +int lease_register_notifier(struct notifier_block *nb) +{ +	return 0; +} +EXPORT_SYMBOL_GPL(lease_register_notifier); + +void lease_unregister_notifier(struct notifier_block *nb) +{ +} +EXPORT_SYMBOL_GPL(lease_unregister_notifier); + +#endif /* IS_ENABLED(CONFIG_SRCU) */ +  /**   * vfs_setlease        -       sets a lease on an open file   * @filp:	file pointer @@ -2010,6 +2070,8 @@ EXPORT_SYMBOL(generic_setlease);  int  vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **priv)  { +	if (lease) +		setlease_notifier(arg, *lease);  	if (filp->f_op->setlease)  		return filp->f_op->setlease(filp, arg, lease, priv);  	else @@ -2784,10 +2846,10 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,  			       ? (fl->fl_type & LOCK_WRITE) ? "RW   " : "READ "  			       : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");  	} else { -		seq_printf(f, "%s ", -			       (lease_breaking(fl)) -			       ? (fl->fl_type == F_UNLCK) ? "UNLCK" : "READ " -			       : (fl->fl_type == F_WRLCK) ? "WRITE" : "READ "); +		int type = IS_LEASE(fl) ? target_leasetype(fl) : fl->fl_type; + +		seq_printf(f, "%s ", (type == F_WRLCK) ? "WRITE" : +				     (type == F_RDLCK) ? "READ" : "UNLCK");  	}  	if (inode) {  		/* userspace relies on this representation of dev_t */ @@ -2923,6 +2985,7 @@ static int __init filelock_init(void)  		INIT_HLIST_HEAD(&fll->hlist);  	} +	lease_notifier_chain_init();  	return 0;  }  core_initcall(filelock_init);  |