Commit d2b6b4c8 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'nfsd-5.3' of git://linux-nfs.org/~bfields/linux

Pull nfsd updates from Bruce Fields:
 "Highlights:

   - Add a new /proc/fs/nfsd/clients/ directory which exposes some
     long-requested information about NFSv4 clients (like open files)
     and allows forced revocation of client state.

   - Replace the global duplicate reply cache by a cache per network
     namespace; previously, a request in one network namespace could
     incorrectly match an entry from another, though we haven't seen
     this in production. This is the last remaining container bug that
     I'm aware of; at this point you should be able to run separate
     nfsd's in each network namespace, each with their own set of
     exports, and everything should work.

   - Cleanup and modify lock code to show the pid of lockd as the owner
     of NLM locks. This is the correct version of the bugfix originally
     attempted in b8eee0e9 ("lockd: Show pid of lockd for remote
     locks")"

* tag 'nfsd-5.3' of git://linux-nfs.org/~bfields/linux: (34 commits)
  nfsd: Make __get_nfsdfs_client() static
  nfsd: Make two functions static
  nfsd: Fix misuse of strlcpy
  sunrpc/cache: remove the exporting of cache_seq_next
  nfsd: decode implementation id
  nfsd: create xdr_netobj_dup helper
  nfsd: allow forced expiration of NFSv4 clients
  nfsd: create get_nfsdfs_clp helper
  nfsd4: show layout stateids
  nfsd: show lock and deleg stateids
  nfsd4: add file to display list of client's opens
  nfsd: add more information to client info file
  nfsd: escape high characters in binary data
  nfsd: copy client's address including port number to cl_addr
  nfsd4: add a client info file
  nfsd: make client/ directory names small ints
  nfsd: add nfsd/clients directory
  nfsd4: use reference count to free client
  nfsd: rename cl_refcount
  nfsd: persist nfsd filesystem across mounts
  ...
parents 0248a8be b78fa45d
Loading
Loading
Loading
Loading
+0 −14
Original line number Diff line number Diff line
@@ -361,8 +361,6 @@ so fl_release_private called on a lease should not block.

----------------------- lock_manager_operations ---------------------------
prototypes:
	int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
	unsigned long (*lm_owner_key)(struct file_lock *);
	void (*lm_notify)(struct file_lock *);  /* unblock callback */
	int (*lm_grant)(struct file_lock *, struct file_lock *, int);
	void (*lm_break)(struct file_lock *); /* break_lease callback */
@@ -371,23 +369,11 @@ prototypes:
locking rules:

			inode->i_lock	blocked_lock_lock	may block
lm_compare_owner:	yes[1]		maybe			no
lm_owner_key		yes[1]		yes			no
lm_notify:		yes		yes			no
lm_grant:		no		no			no
lm_break:		yes		no			no
lm_change		yes		no			no

[1]:	->lm_compare_owner and ->lm_owner_key are generally called with
*an* inode->i_lock held. It may not be the i_lock of the inode
associated with either file_lock argument! This is the case with deadlock
detection, since the code has to chase down the owners of locks that may
be entirely unrelated to the one on which the lock is being acquired.
For deadlock detection however, the blocked_lock_lock is also held. The
fact that these locks are held ensures that the file_locks do not
disappear out from under you while doing the comparison or generating an
owner key.

--------------------------- buffer_head -----------------------------------
prototypes:
	void (*b_end_io)(struct buffer_head *bh, int uptodate);
+11 −10
Original line number Diff line number Diff line
@@ -47,13 +47,14 @@ void nlmclnt_next_cookie(struct nlm_cookie *c)
	c->len=4;
}

static struct nlm_lockowner *nlm_get_lockowner(struct nlm_lockowner *lockowner)
static struct nlm_lockowner *
nlmclnt_get_lockowner(struct nlm_lockowner *lockowner)
{
	refcount_inc(&lockowner->count);
	return lockowner;
}

static void nlm_put_lockowner(struct nlm_lockowner *lockowner)
static void nlmclnt_put_lockowner(struct nlm_lockowner *lockowner)
{
	if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
		return;
@@ -82,28 +83,28 @@ static inline uint32_t __nlm_alloc_pid(struct nlm_host *host)
	return res;
}

static struct nlm_lockowner *__nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner)
static struct nlm_lockowner *__nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner)
{
	struct nlm_lockowner *lockowner;
	list_for_each_entry(lockowner, &host->h_lockowners, list) {
		if (lockowner->owner != owner)
			continue;
		return nlm_get_lockowner(lockowner);
		return nlmclnt_get_lockowner(lockowner);
	}
	return NULL;
}

static struct nlm_lockowner *nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner)
static struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner)
{
	struct nlm_lockowner *res, *new = NULL;

	spin_lock(&host->h_lock);
	res = __nlm_find_lockowner(host, owner);
	res = __nlmclnt_find_lockowner(host, owner);
	if (res == NULL) {
		spin_unlock(&host->h_lock);
		new = kmalloc(sizeof(*new), GFP_KERNEL);
		spin_lock(&host->h_lock);
		res = __nlm_find_lockowner(host, owner);
		res = __nlmclnt_find_lockowner(host, owner);
		if (res == NULL && new != NULL) {
			res = new;
			refcount_set(&new->count, 1);
@@ -457,7 +458,7 @@ static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
	spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
	new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state;
	new->fl_u.nfs_fl.owner = nlm_get_lockowner(fl->fl_u.nfs_fl.owner);
	new->fl_u.nfs_fl.owner = nlmclnt_get_lockowner(fl->fl_u.nfs_fl.owner);
	list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted);
	spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
}
@@ -467,7 +468,7 @@ static void nlmclnt_locks_release_private(struct file_lock *fl)
	spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
	list_del(&fl->fl_u.nfs_fl.list);
	spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
	nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
	nlmclnt_put_lockowner(fl->fl_u.nfs_fl.owner);
}

static const struct file_lock_operations nlmclnt_lock_ops = {
@@ -478,7 +479,7 @@ static const struct file_lock_operations nlmclnt_lock_ops = {
static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host)
{
	fl->fl_u.nfs_fl.state = 0;
	fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner);
	fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host, fl->fl_owner);
	INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list);
	fl->fl_ops = &nlmclnt_lock_ops;
}
+13 −1
Original line number Diff line number Diff line
@@ -46,8 +46,14 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,

		/* Set up the missing parts of the file_lock structure */
		lock->fl.fl_file  = file->f_file;
		lock->fl.fl_owner = (fl_owner_t) host;
		lock->fl.fl_pid = current->tgid;
		lock->fl.fl_lmops = &nlmsvc_lock_operations;
		nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
		if (!lock->fl.fl_owner) {
			/* lockowner allocation has failed */
			nlmsvc_release_host(host);
			return nlm_lck_denied_nolocks;
		}
	}

	return 0;
@@ -94,6 +100,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
	else
		dprintk("lockd: TEST4        status %d\n", ntohl(resp->status));

	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rc;
@@ -142,6 +149,7 @@ __nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp)
	else
		dprintk("lockd: LOCK         status %d\n", ntohl(resp->status));

	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rc;
@@ -178,6 +186,7 @@ __nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_res *resp)
	resp->status = nlmsvc_cancel_blocked(SVC_NET(rqstp), file, &argp->lock);

	dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
@@ -217,6 +226,7 @@ __nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp)
	resp->status = nlmsvc_unlock(SVC_NET(rqstp), file, &argp->lock);

	dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
@@ -365,6 +375,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp)
	resp->status = nlmsvc_share_file(host, file, argp);

	dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
@@ -399,6 +410,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp)
	resp->status = nlmsvc_unshare_file(host, file, argp);

	dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
+98 −20
Original line number Diff line number Diff line
@@ -332,6 +332,93 @@ restart:
	mutex_unlock(&file->f_mutex);
}

static struct nlm_lockowner *
nlmsvc_get_lockowner(struct nlm_lockowner *lockowner)
{
	refcount_inc(&lockowner->count);
	return lockowner;
}

static void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner)
{
	if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
		return;
	list_del(&lockowner->list);
	spin_unlock(&lockowner->host->h_lock);
	nlmsvc_release_host(lockowner->host);
	kfree(lockowner);
}

static struct nlm_lockowner *__nlmsvc_find_lockowner(struct nlm_host *host, pid_t pid)
{
	struct nlm_lockowner *lockowner;
	list_for_each_entry(lockowner, &host->h_lockowners, list) {
		if (lockowner->pid != pid)
			continue;
		return nlmsvc_get_lockowner(lockowner);
	}
	return NULL;
}

static struct nlm_lockowner *nlmsvc_find_lockowner(struct nlm_host *host, pid_t pid)
{
	struct nlm_lockowner *res, *new = NULL;

	spin_lock(&host->h_lock);
	res = __nlmsvc_find_lockowner(host, pid);

	if (res == NULL) {
		spin_unlock(&host->h_lock);
		new = kmalloc(sizeof(*res), GFP_KERNEL);
		spin_lock(&host->h_lock);
		res = __nlmsvc_find_lockowner(host, pid);
		if (res == NULL && new != NULL) {
			res = new;
			/* fs/locks.c will manage the refcount through lock_ops */
			refcount_set(&new->count, 1);
			new->pid = pid;
			new->host = nlm_get_host(host);
			list_add(&new->list, &host->h_lockowners);
			new = NULL;
		}
	}

	spin_unlock(&host->h_lock);
	kfree(new);
	return res;
}

void
nlmsvc_release_lockowner(struct nlm_lock *lock)
{
	if (lock->fl.fl_owner)
		nlmsvc_put_lockowner(lock->fl.fl_owner);
}

static void nlmsvc_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
	struct nlm_lockowner *nlm_lo = (struct nlm_lockowner *)fl->fl_owner;
	new->fl_owner = nlmsvc_get_lockowner(nlm_lo);
}

static void nlmsvc_locks_release_private(struct file_lock *fl)
{
	nlmsvc_put_lockowner((struct nlm_lockowner *)fl->fl_owner);
}

static const struct file_lock_operations nlmsvc_lock_ops = {
	.fl_copy_lock = nlmsvc_locks_copy_lock,
	.fl_release_private = nlmsvc_locks_release_private,
};

void nlmsvc_locks_init_private(struct file_lock *fl, struct nlm_host *host,
						pid_t pid)
{
	fl->fl_owner = nlmsvc_find_lockowner(host, pid);
	if (fl->fl_owner != NULL)
		fl->fl_ops = &nlmsvc_lock_ops;
}

/*
 * Initialize arguments for GRANTED call. The nlm_rqst structure
 * has been cleared already.
@@ -345,7 +432,7 @@ static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)

	/* set default data area */
	call->a_args.lock.oh.data = call->a_owner;
	call->a_args.lock.svid = lock->fl.fl_pid;
	call->a_args.lock.svid = ((struct nlm_lockowner *)lock->fl.fl_owner)->pid;

	if (lock->oh.len > NLMCLNT_OHSIZE) {
		void *data = kmalloc(lock->oh.len, GFP_KERNEL);
@@ -509,6 +596,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
{
	int			error;
	__be32			ret;
	struct nlm_lockowner	*test_owner;

	dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
				locks_inode(file->f_file)->i_sb->s_id,
@@ -522,6 +610,9 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
		goto out;
	}

	/* If there's a conflicting lock, remember to clean up the test lock */
	test_owner = (struct nlm_lockowner *)lock->fl.fl_owner;

	error = vfs_test_lock(file->f_file, &lock->fl);
	if (error) {
		/* We can't currently deal with deferred test requests */
@@ -543,11 +634,16 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
	conflock->caller = "somehost";	/* FIXME */
	conflock->len = strlen(conflock->caller);
	conflock->oh.len = 0;		/* don't return OH info */
	conflock->svid = lock->fl.fl_pid;
	conflock->svid = ((struct nlm_lockowner *)lock->fl.fl_owner)->pid;
	conflock->fl.fl_type = lock->fl.fl_type;
	conflock->fl.fl_start = lock->fl.fl_start;
	conflock->fl.fl_end = lock->fl.fl_end;
	locks_release_private(&lock->fl);

	/* Clean up the test lock */
	lock->fl.fl_owner = NULL;
	nlmsvc_put_lockowner(test_owner);

	ret = nlm_lck_denied;
out:
	return ret;
@@ -692,25 +788,7 @@ nlmsvc_notify_blocked(struct file_lock *fl)
	printk(KERN_WARNING "lockd: notification for unknown block!\n");
}

static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
{
	return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid;
}

/*
 * Since NLM uses two "keys" for tracking locks, we need to hash them down
 * to one for the blocked_hash. Here, we're just xor'ing the host address
 * with the pid in order to create a key value for picking a hash bucket.
 */
static unsigned long
nlmsvc_owner_key(struct file_lock *fl)
{
	return (unsigned long)fl->fl_owner ^ (unsigned long)fl->fl_pid;
}

const struct lock_manager_operations nlmsvc_lock_operations = {
	.lm_compare_owner = nlmsvc_same_owner,
	.lm_owner_key = nlmsvc_owner_key,
	.lm_notify = nlmsvc_notify_blocked,
	.lm_grant = nlmsvc_grant_deferred,
};
+13 −1
Original line number Diff line number Diff line
@@ -76,8 +76,14 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,

		/* Set up the missing parts of the file_lock structure */
		lock->fl.fl_file  = file->f_file;
		lock->fl.fl_owner = (fl_owner_t) host;
		lock->fl.fl_pid = current->tgid;
		lock->fl.fl_lmops = &nlmsvc_lock_operations;
		nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
		if (!lock->fl.fl_owner) {
			/* lockowner allocation has failed */
			nlmsvc_release_host(host);
			return nlm_lck_denied_nolocks;
		}
	}

	return 0;
@@ -125,6 +131,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
		dprintk("lockd: TEST          status %d vers %d\n",
			ntohl(resp->status), rqstp->rq_vers);

	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rc;
@@ -173,6 +180,7 @@ __nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp)
	else
		dprintk("lockd: LOCK         status %d\n", ntohl(resp->status));

	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rc;
@@ -210,6 +218,7 @@ __nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_res *resp)
	resp->status = cast_status(nlmsvc_cancel_blocked(net, file, &argp->lock));

	dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
@@ -250,6 +259,7 @@ __nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp)
	resp->status = cast_status(nlmsvc_unlock(net, file, &argp->lock));

	dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
@@ -408,6 +418,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp)
	resp->status = cast_status(nlmsvc_share_file(host, file, argp));

	dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
@@ -442,6 +453,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp)
	resp->status = cast_status(nlmsvc_unshare_file(host, file, argp));

	dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
	nlmsvc_release_lockowner(&argp->lock);
	nlmsvc_release_host(host);
	nlm_release_file(file);
	return rpc_success;
Loading