aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Coddington <bcodding@redhat.com>2017-04-11 12:50:11 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2017-04-21 10:45:01 -0400
commitb1ece737f44f91dca8f4829cf0b442e752e406db (patch)
treed0368ec8da45be194c4cde25d46e178ea9060715
parent7d6ddf88c4db372689c8aa65ea652d0514d66c06 (diff)
lockd: Introduce nlmclnt_operations
NFS would enjoy the ability to modify the behavior of the NLM client's unlock RPC task in order to delay the transmission of the unlock until IO that was submitted under that lock has completed. This ability can ensure that the NLM client will always complete the transmission of an unlock even if the waiting caller has been interrupted with fatal signal. For this purpose, a pointer to a struct nlmclnt_operations can be assigned in a nfs_module's nfs_rpc_ops that will install those nlmclnt_operations on the nlm_host. The struct nlmclnt_operations defines three callback operations that will be used in a following patch: nlmclnt_alloc_call - used to call back after a successful allocation of a struct nlm_rqst in nlmclnt_proc(). nlmclnt_unlock_prepare - used to call back during NLM unlock's rpc_call_prepare. The NLM client defers calling rpc_call_start() until this callback returns false. nlmclnt_release_call - used to call back when the NLM client's struct nlm_rqst is freed. Signed-off-by: Benjamin Coddington <bcodding@redhat.com> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
-rw-r--r--fs/lockd/clntlock.c1
-rw-r--r--fs/lockd/clntproc.c26
-rw-r--r--fs/nfs/client.c1
-rw-r--r--fs/nfs/nfs3proc.c2
-rw-r--r--fs/nfs/proc.c2
-rw-r--r--include/linux/lockd/bind.h24
-rw-r--r--include/linux/lockd/lockd.h2
-rw-r--r--include/linux/nfs_xdr.h1
8 files changed, 54 insertions, 5 deletions
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 41e491b8e5d7..27d577dbe51a 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -69,6 +69,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
69 if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL) 69 if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL)
70 goto out_nobind; 70 goto out_nobind;
71 71
72 host->h_nlmclnt_ops = nlm_init->nlmclnt_ops;
72 return host; 73 return host;
73out_nobind: 74out_nobind:
74 nlmclnt_release_host(host); 75 nlmclnt_release_host(host);
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 112952037933..066ac313ae5c 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -150,17 +150,22 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
150 * @host: address of a valid nlm_host context representing the NLM server 150 * @host: address of a valid nlm_host context representing the NLM server
151 * @cmd: fcntl-style file lock operation to perform 151 * @cmd: fcntl-style file lock operation to perform
152 * @fl: address of arguments for the lock operation 152 * @fl: address of arguments for the lock operation
153 * @data: address of data to be sent to callback operations
153 * 154 *
154 */ 155 */
155int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) 156int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data)
156{ 157{
157 struct nlm_rqst *call; 158 struct nlm_rqst *call;
158 int status; 159 int status;
160 const struct nlmclnt_operations *nlmclnt_ops = host->h_nlmclnt_ops;
159 161
160 call = nlm_alloc_call(host); 162 call = nlm_alloc_call(host);
161 if (call == NULL) 163 if (call == NULL)
162 return -ENOMEM; 164 return -ENOMEM;
163 165
166 if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call)
167 nlmclnt_ops->nlmclnt_alloc_call(data);
168
164 nlmclnt_locks_init_private(fl, host); 169 nlmclnt_locks_init_private(fl, host);
165 if (!fl->fl_u.nfs_fl.owner) { 170 if (!fl->fl_u.nfs_fl.owner) {
166 /* lockowner allocation has failed */ 171 /* lockowner allocation has failed */
@@ -169,6 +174,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
169 } 174 }
170 /* Set up the argument struct */ 175 /* Set up the argument struct */
171 nlmclnt_setlockargs(call, fl); 176 nlmclnt_setlockargs(call, fl);
177 call->a_callback_data = data;
172 178
173 if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { 179 if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
174 if (fl->fl_type != F_UNLCK) { 180 if (fl->fl_type != F_UNLCK) {
@@ -214,8 +220,12 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
214 220
215void nlmclnt_release_call(struct nlm_rqst *call) 221void nlmclnt_release_call(struct nlm_rqst *call)
216{ 222{
223 const struct nlmclnt_operations *nlmclnt_ops = call->a_host->h_nlmclnt_ops;
224
217 if (!atomic_dec_and_test(&call->a_count)) 225 if (!atomic_dec_and_test(&call->a_count))
218 return; 226 return;
227 if (nlmclnt_ops && nlmclnt_ops->nlmclnt_release_call)
228 nlmclnt_ops->nlmclnt_release_call(call->a_callback_data);
219 nlmclnt_release_host(call->a_host); 229 nlmclnt_release_host(call->a_host);
220 nlmclnt_release_lockargs(call); 230 nlmclnt_release_lockargs(call);
221 kfree(call); 231 kfree(call);
@@ -687,6 +697,19 @@ out:
687 return status; 697 return status;
688} 698}
689 699
700static void nlmclnt_unlock_prepare(struct rpc_task *task, void *data)
701{
702 struct nlm_rqst *req = data;
703 const struct nlmclnt_operations *nlmclnt_ops = req->a_host->h_nlmclnt_ops;
704 bool defer_call = false;
705
706 if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare)
707 defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->a_callback_data);
708
709 if (!defer_call)
710 rpc_call_start(task);
711}
712
690static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) 713static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
691{ 714{
692 struct nlm_rqst *req = data; 715 struct nlm_rqst *req = data;
@@ -720,6 +743,7 @@ die:
720} 743}
721 744
722static const struct rpc_call_ops nlmclnt_unlock_ops = { 745static const struct rpc_call_ops nlmclnt_unlock_ops = {
746 .rpc_call_prepare = nlmclnt_unlock_prepare,
723 .rpc_call_done = nlmclnt_unlock_callback, 747 .rpc_call_done = nlmclnt_unlock_callback,
724 .rpc_release = nlmclnt_rpc_release, 748 .rpc_release = nlmclnt_rpc_release,
725}; 749};
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 3e7b2e6a7cfb..e0302101e18a 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -546,6 +546,7 @@ static int nfs_start_lockd(struct nfs_server *server)
546 .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 546 .noresvport = server->flags & NFS_MOUNT_NORESVPORT ?
547 1 : 0, 547 1 : 0,
548 .net = clp->cl_net, 548 .net = clp->cl_net,
549 .nlmclnt_ops = clp->cl_nfs_mod->rpc_ops->nlmclnt_ops,
549 }; 550 };
550 551
551 if (nlm_init.nfs_version > 3) 552 if (nlm_init.nfs_version > 3)
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index dc925b531f32..03b3c3de28f1 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -870,7 +870,7 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
870{ 870{
871 struct inode *inode = file_inode(filp); 871 struct inode *inode = file_inode(filp);
872 872
873 return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); 873 return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, NULL);
874} 874}
875 875
876static int nfs3_have_delegation(struct inode *inode, fmode_t flags) 876static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index b7bca8303989..9872cf676a50 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -638,7 +638,7 @@ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
638{ 638{
639 struct inode *inode = file_inode(filp); 639 struct inode *inode = file_inode(filp);
640 640
641 return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); 641 return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, NULL);
642} 642}
643 643
644/* Helper functions for NFS lock bounds checking */ 644/* Helper functions for NFS lock bounds checking */
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index 140edab64446..05728396a1a1 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -18,6 +18,7 @@
18 18
19/* Dummy declarations */ 19/* Dummy declarations */
20struct svc_rqst; 20struct svc_rqst;
21struct rpc_task;
21 22
22/* 23/*
23 * This is the set of functions for lockd->nfsd communication 24 * This is the set of functions for lockd->nfsd communication
@@ -43,6 +44,7 @@ struct nlmclnt_initdata {
43 u32 nfs_version; 44 u32 nfs_version;
44 int noresvport; 45 int noresvport;
45 struct net *net; 46 struct net *net;
47 const struct nlmclnt_operations *nlmclnt_ops;
46}; 48};
47 49
48/* 50/*
@@ -52,8 +54,26 @@ struct nlmclnt_initdata {
52extern struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init); 54extern struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init);
53extern void nlmclnt_done(struct nlm_host *host); 55extern void nlmclnt_done(struct nlm_host *host);
54 56
55extern int nlmclnt_proc(struct nlm_host *host, int cmd, 57/*
56 struct file_lock *fl); 58 * NLM client operations provide a means to modify RPC processing of NLM
59 * requests. Callbacks receive a pointer to data passed into the call to
60 * nlmclnt_proc().
61 */
62struct nlmclnt_operations {
63 /* Called on successful allocation of nlm_rqst, use for allocation or
64 * reference counting. */
65 void (*nlmclnt_alloc_call)(void *);
66
67 /* Called in rpc_task_prepare for unlock. A return value of true
68 * indicates the callback has put the task to sleep on a waitqueue
69 * and NLM should not call rpc_call_start(). */
70 bool (*nlmclnt_unlock_prepare)(struct rpc_task*, void *);
71
72 /* Called when the nlm_rqst is freed, callbacks should clean up here */
73 void (*nlmclnt_release_call)(void *);
74};
75
76extern int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data);
57extern int lockd_up(struct net *net); 77extern int lockd_up(struct net *net);
58extern void lockd_down(struct net *net); 78extern void lockd_down(struct net *net);
59 79
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index b37dee3acaba..41f7b6a04d69 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -69,6 +69,7 @@ struct nlm_host {
69 char *h_addrbuf; /* address eyecatcher */ 69 char *h_addrbuf; /* address eyecatcher */
70 struct net *net; /* host net */ 70 struct net *net; /* host net */
71 char nodename[UNX_MAXNODENAME + 1]; 71 char nodename[UNX_MAXNODENAME + 1];
72 const struct nlmclnt_operations *h_nlmclnt_ops; /* Callback ops for NLM users */
72}; 73};
73 74
74/* 75/*
@@ -142,6 +143,7 @@ struct nlm_rqst {
142 struct nlm_block * a_block; 143 struct nlm_block * a_block;
143 unsigned int a_retries; /* Retry count */ 144 unsigned int a_retries; /* Retry count */
144 u8 a_owner[NLMCLNT_OHSIZE]; 145 u8 a_owner[NLMCLNT_OHSIZE];
146 void * a_callback_data; /* sent to nlmclnt_operations callbacks */
145}; 147};
146 148
147/* 149/*
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 51e27f9746ee..677c6b91dfcd 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1551,6 +1551,7 @@ struct nfs_rpc_ops {
1551 const struct inode_operations *dir_inode_ops; 1551 const struct inode_operations *dir_inode_ops;
1552 const struct inode_operations *file_inode_ops; 1552 const struct inode_operations *file_inode_ops;
1553 const struct file_operations *file_ops; 1553 const struct file_operations *file_ops;
1554 const struct nlmclnt_operations *nlmclnt_ops;
1554 1555
1555 int (*getroot) (struct nfs_server *, struct nfs_fh *, 1556 int (*getroot) (struct nfs_server *, struct nfs_fh *,
1556 struct nfs_fsinfo *); 1557 struct nfs_fsinfo *);