Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5b1f0d9 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

locks/nfsd: allocate file lock outside of spinlock



As suggested by Christoph Hellwig, this moves allocation
of new file locks out of generic_setlease into the
callers, nfs4_open_delegation and fcntl_setlease in order
to allow GFP_KERNEL allocations when lock_flocks has
become a spinlock.

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Acked-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent a282a1fa
Loading
Loading
Loading
Loading
+12 −24
Original line number Original line Diff line number Diff line
@@ -162,10 +162,11 @@ EXPORT_SYMBOL_GPL(unlock_flocks);
static struct kmem_cache *filelock_cache __read_mostly;
static struct kmem_cache *filelock_cache __read_mostly;


/* Allocate an empty lock structure. */
/* Allocate an empty lock structure. */
static struct file_lock *locks_alloc_lock(void)
struct file_lock *locks_alloc_lock(void)
{
{
	return kmem_cache_alloc(filelock_cache, GFP_KERNEL);
	return kmem_cache_alloc(filelock_cache, GFP_KERNEL);
}
}
EXPORT_SYMBOL_GPL(locks_alloc_lock);


void locks_release_private(struct file_lock *fl)
void locks_release_private(struct file_lock *fl)
{
{
@@ -1365,7 +1366,6 @@ int fcntl_getlease(struct file *filp)
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
{
{
	struct file_lock *fl, **before, **my_before = NULL, *lease;
	struct file_lock *fl, **before, **my_before = NULL, *lease;
	struct file_lock *new_fl = NULL;
	struct dentry *dentry = filp->f_path.dentry;
	struct dentry *dentry = filp->f_path.dentry;
	struct inode *inode = dentry->d_inode;
	struct inode *inode = dentry->d_inode;
	int error, rdlease_count = 0, wrlease_count = 0;
	int error, rdlease_count = 0, wrlease_count = 0;
@@ -1385,11 +1385,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
	lease = *flp;
	lease = *flp;


	if (arg != F_UNLCK) {
	if (arg != F_UNLCK) {
		error = -ENOMEM;
		new_fl = locks_alloc_lock();
		if (new_fl == NULL)
			goto out;

		error = -EAGAIN;
		error = -EAGAIN;
		if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
		if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
			goto out;
			goto out;
@@ -1434,7 +1429,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
		goto out;
		goto out;
	}
	}


	error = 0;
	if (arg == F_UNLCK)
	if (arg == F_UNLCK)
		goto out;
		goto out;


@@ -1442,15 +1436,11 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
	if (!leases_enable)
	if (!leases_enable)
		goto out;
		goto out;


	locks_copy_lock(new_fl, lease);
	locks_insert_lock(before, lease);
	locks_insert_lock(before, new_fl);

	*flp = new_fl;
	return 0;
	return 0;


out:
out:
	if (new_fl != NULL)
	locks_free_lock(lease);
		locks_free_lock(new_fl);
	return error;
	return error;
}
}
EXPORT_SYMBOL(generic_setlease);
EXPORT_SYMBOL(generic_setlease);
@@ -1514,26 +1504,24 @@ EXPORT_SYMBOL_GPL(vfs_setlease);
 */
 */
int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
{
{
	struct file_lock fl, *flp = &fl;
	struct file_lock *fl;
	struct inode *inode = filp->f_path.dentry->d_inode;
	struct inode *inode = filp->f_path.dentry->d_inode;
	int error;
	int error;


	locks_init_lock(&fl);
	fl = lease_alloc(filp, arg);
	error = lease_init(filp, arg, &fl);
	if (IS_ERR(fl))
	if (error)
		return PTR_ERR(fl);
		return error;


	lock_flocks();
	lock_flocks();

	error = __vfs_setlease(filp, arg, &fl);
	error = __vfs_setlease(filp, arg, &flp);
	if (error || arg == F_UNLCK)
	if (error || arg == F_UNLCK)
		goto out_unlock;
		goto out_unlock;


	error = fasync_helper(fd, filp, 1, &flp->fl_fasync);
	error = fasync_helper(fd, filp, 1, &fl->fl_fasync);
	if (error < 0) {
	if (error < 0) {
		/* remove lease just inserted by setlease */
		/* remove lease just inserted by setlease */
		flp->fl_type = F_UNLCK | F_INPROGRESS;
		fl->fl_type = F_UNLCK | F_INPROGRESS;
		flp->fl_break_time = jiffies - 10;
		fl->fl_break_time = jiffies - 10;
		time_out_leases(inode);
		time_out_leases(inode);
		goto out_unlock;
		goto out_unlock;
	}
	}
+15 −11
Original line number Original line Diff line number Diff line
@@ -2614,7 +2614,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
	struct nfs4_delegation *dp;
	struct nfs4_delegation *dp;
	struct nfs4_stateowner *sop = stp->st_stateowner;
	struct nfs4_stateowner *sop = stp->st_stateowner;
	int cb_up = atomic_read(&sop->so_client->cl_cb_set);
	int cb_up = atomic_read(&sop->so_client->cl_cb_set);
	struct file_lock fl, *flp = &fl;
	struct file_lock *fl;
	int status, flag = 0;
	int status, flag = 0;


	flag = NFS4_OPEN_DELEGATE_NONE;
	flag = NFS4_OPEN_DELEGATE_NONE;
@@ -2648,20 +2648,24 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
		flag = NFS4_OPEN_DELEGATE_NONE;
		flag = NFS4_OPEN_DELEGATE_NONE;
		goto out;
		goto out;
	}
	}
	locks_init_lock(&fl);
	status = -ENOMEM;
	fl.fl_lmops = &nfsd_lease_mng_ops;
	fl = locks_alloc_lock();
	fl.fl_flags = FL_LEASE;
	if (!fl)
	fl.fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
		goto out;
	fl.fl_end = OFFSET_MAX;
	locks_init_lock(fl);
	fl.fl_owner =  (fl_owner_t)dp;
	fl->fl_lmops = &nfsd_lease_mng_ops;
	fl.fl_file = find_readable_file(stp->st_file);
	fl->fl_flags = FL_LEASE;
	BUG_ON(!fl.fl_file);
	fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
	fl.fl_pid = current->tgid;
	fl->fl_end = OFFSET_MAX;
	fl->fl_owner =  (fl_owner_t)dp;
	fl->fl_file = find_readable_file(stp->st_file);
	BUG_ON(!fl->fl_file);
	fl->fl_pid = current->tgid;


	/* vfs_setlease checks to see if delegation should be handed out.
	/* vfs_setlease checks to see if delegation should be handed out.
	 * the lock_manager callbacks fl_mylease and fl_change are used
	 * the lock_manager callbacks fl_mylease and fl_change are used
	 */
	 */
	if ((status = vfs_setlease(fl.fl_file, fl.fl_type, &flp))) {
	if ((status = vfs_setlease(fl->fl_file, fl->fl_type, &fl))) {
		dprintk("NFSD: setlease failed [%d], no delegation\n", status);
		dprintk("NFSD: setlease failed [%d], no delegation\n", status);
		unhash_delegation(dp);
		unhash_delegation(dp);
		flag = NFS4_OPEN_DELEGATE_NONE;
		flag = NFS4_OPEN_DELEGATE_NONE;
+1 −0
Original line number Original line Diff line number Diff line
@@ -1113,6 +1113,7 @@ extern int fcntl_getlease(struct file *filp);


/* fs/locks.c */
/* fs/locks.c */
extern void locks_init_lock(struct file_lock *);
extern void locks_init_lock(struct file_lock *);
extern struct file_lock * locks_alloc_lock(void);
extern void locks_copy_lock(struct file_lock *, struct file_lock *);
extern void locks_copy_lock(struct file_lock *, struct file_lock *);
extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_posix(struct file *, fl_owner_t);