Skip to content

Commit 76d348f

Browse files
jtlaytonJ. Bruce Fields
authored andcommitted
nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
Create a new per-lockowner+per-inode structure that contains a file_lock. Have nfsd4_lock add this structure to the lockowner's list prior to setting the lock. Then call the vfs and request a blocking lock (by setting FL_SLEEP). If we get anything besides FILE_LOCK_DEFERRED back, then we dequeue the block structure and free it. When the next lock request comes in, we'll look for an existing block for the same filehandle and dequeue and reuse it if there is one. When the lock comes free (a'la an lm_notify call), we dequeue it from the lockowner's list and kick off a CB_NOTIFY_LOCK callback to inform the client that it should retry the lock request. Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: J. Bruce Fields <[email protected]>
1 parent a188620 commit 76d348f

File tree

2 files changed

+156
-20
lines changed

2 files changed

+156
-20
lines changed

fs/nfsd/nfs4state.c

Lines changed: 147 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ static struct kmem_cache *odstate_slab;
9999
static void free_session(struct nfsd4_session *);
100100

101101
static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
102+
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
102103

103104
static bool is_session_dead(struct nfsd4_session *ses)
104105
{
@@ -210,6 +211,84 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
210211
spin_unlock(&nn->client_lock);
211212
}
212213

214+
static struct nfsd4_blocked_lock *
215+
find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
216+
struct nfsd_net *nn)
217+
{
218+
struct nfsd4_blocked_lock *cur, *found = NULL;
219+
220+
spin_lock(&nn->client_lock);
221+
list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
222+
if (fh_match(fh, &cur->nbl_fh)) {
223+
list_del_init(&cur->nbl_list);
224+
found = cur;
225+
break;
226+
}
227+
}
228+
spin_unlock(&nn->client_lock);
229+
if (found)
230+
posix_unblock_lock(&found->nbl_lock);
231+
return found;
232+
}
233+
234+
static struct nfsd4_blocked_lock *
235+
find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
236+
struct nfsd_net *nn)
237+
{
238+
struct nfsd4_blocked_lock *nbl;
239+
240+
nbl = find_blocked_lock(lo, fh, nn);
241+
if (!nbl) {
242+
nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
243+
if (nbl) {
244+
fh_copy_shallow(&nbl->nbl_fh, fh);
245+
locks_init_lock(&nbl->nbl_lock);
246+
nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
247+
&nfsd4_cb_notify_lock_ops,
248+
NFSPROC4_CLNT_CB_NOTIFY_LOCK);
249+
}
250+
}
251+
return nbl;
252+
}
253+
254+
static void
255+
free_blocked_lock(struct nfsd4_blocked_lock *nbl)
256+
{
257+
locks_release_private(&nbl->nbl_lock);
258+
kfree(nbl);
259+
}
260+
261+
static int
262+
nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
263+
{
264+
/*
265+
* Since this is just an optimization, we don't try very hard if it
266+
* turns out not to succeed. We'll requeue it on NFS4ERR_DELAY, and
267+
* just quit trying on anything else.
268+
*/
269+
switch (task->tk_status) {
270+
case -NFS4ERR_DELAY:
271+
rpc_delay(task, 1 * HZ);
272+
return 0;
273+
default:
274+
return 1;
275+
}
276+
}
277+
278+
static void
279+
nfsd4_cb_notify_lock_release(struct nfsd4_callback *cb)
280+
{
281+
struct nfsd4_blocked_lock *nbl = container_of(cb,
282+
struct nfsd4_blocked_lock, nbl_cb);
283+
284+
free_blocked_lock(nbl);
285+
}
286+
287+
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = {
288+
.done = nfsd4_cb_notify_lock_done,
289+
.release = nfsd4_cb_notify_lock_release,
290+
};
291+
213292
static inline struct nfs4_stateowner *
214293
nfs4_get_stateowner(struct nfs4_stateowner *sop)
215294
{
@@ -5309,7 +5388,29 @@ nfsd4_fl_put_owner(fl_owner_t owner)
53095388
nfs4_put_stateowner(&lo->lo_owner);
53105389
}
53115390

5391+
static void
5392+
nfsd4_lm_notify(struct file_lock *fl)
5393+
{
5394+
struct nfs4_lockowner *lo = (struct nfs4_lockowner *)fl->fl_owner;
5395+
struct net *net = lo->lo_owner.so_client->net;
5396+
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
5397+
struct nfsd4_blocked_lock *nbl = container_of(fl,
5398+
struct nfsd4_blocked_lock, nbl_lock);
5399+
bool queue = false;
5400+
5401+
spin_lock(&nn->client_lock);
5402+
if (!list_empty(&nbl->nbl_list)) {
5403+
list_del_init(&nbl->nbl_list);
5404+
queue = true;
5405+
}
5406+
spin_unlock(&nn->client_lock);
5407+
5408+
if (queue)
5409+
nfsd4_run_cb(&nbl->nbl_cb);
5410+
}
5411+
53125412
static const struct lock_manager_operations nfsd_posix_mng_ops = {
5413+
.lm_notify = nfsd4_lm_notify,
53135414
.lm_get_owner = nfsd4_fl_get_owner,
53145415
.lm_put_owner = nfsd4_fl_put_owner,
53155416
};
@@ -5407,6 +5508,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
54075508
lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
54085509
if (!lo)
54095510
return NULL;
5511+
INIT_LIST_HEAD(&lo->lo_blocked);
54105512
INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
54115513
lo->lo_owner.so_is_open_owner = 0;
54125514
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
@@ -5588,12 +5690,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
55885690
struct nfs4_ol_stateid *open_stp = NULL;
55895691
struct nfs4_file *fp;
55905692
struct file *filp = NULL;
5693+
struct nfsd4_blocked_lock *nbl = NULL;
55915694
struct file_lock *file_lock = NULL;
55925695
struct file_lock *conflock = NULL;
55935696
__be32 status = 0;
55945697
int lkflg;
55955698
int err;
55965699
bool new = false;
5700+
unsigned char fl_type;
5701+
unsigned int fl_flags = FL_POSIX;
55975702
struct net *net = SVC_NET(rqstp);
55985703
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
55995704

@@ -5658,46 +5763,55 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
56585763
if (!locks_in_grace(net) && lock->lk_reclaim)
56595764
goto out;
56605765

5661-
file_lock = locks_alloc_lock();
5662-
if (!file_lock) {
5663-
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
5664-
status = nfserr_jukebox;
5665-
goto out;
5666-
}
5667-
56685766
fp = lock_stp->st_stid.sc_file;
56695767
switch (lock->lk_type) {
5670-
case NFS4_READ_LT:
56715768
case NFS4_READW_LT:
5769+
if (nfsd4_has_session(cstate))
5770+
fl_flags |= FL_SLEEP;
5771+
/* Fallthrough */
5772+
case NFS4_READ_LT:
56725773
spin_lock(&fp->fi_lock);
56735774
filp = find_readable_file_locked(fp);
56745775
if (filp)
56755776
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
56765777
spin_unlock(&fp->fi_lock);
5677-
file_lock->fl_type = F_RDLCK;
5778+
fl_type = F_RDLCK;
56785779
break;
5679-
case NFS4_WRITE_LT:
56805780
case NFS4_WRITEW_LT:
5781+
if (nfsd4_has_session(cstate))
5782+
fl_flags |= FL_SLEEP;
5783+
/* Fallthrough */
5784+
case NFS4_WRITE_LT:
56815785
spin_lock(&fp->fi_lock);
56825786
filp = find_writeable_file_locked(fp);
56835787
if (filp)
56845788
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
56855789
spin_unlock(&fp->fi_lock);
5686-
file_lock->fl_type = F_WRLCK;
5790+
fl_type = F_WRLCK;
56875791
break;
56885792
default:
56895793
status = nfserr_inval;
56905794
goto out;
56915795
}
5796+
56925797
if (!filp) {
56935798
status = nfserr_openmode;
56945799
goto out;
56955800
}
56965801

5802+
nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
5803+
if (!nbl) {
5804+
dprintk("NFSD: %s: unable to allocate block!\n", __func__);
5805+
status = nfserr_jukebox;
5806+
goto out;
5807+
}
5808+
5809+
file_lock = &nbl->nbl_lock;
5810+
file_lock->fl_type = fl_type;
56975811
file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
56985812
file_lock->fl_pid = current->tgid;
56995813
file_lock->fl_file = filp;
5700-
file_lock->fl_flags = FL_POSIX;
5814+
file_lock->fl_flags = fl_flags;
57015815
file_lock->fl_lmops = &nfsd_posix_mng_ops;
57025816
file_lock->fl_start = lock->lk_offset;
57035817
file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
@@ -5710,18 +5824,27 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
57105824
goto out;
57115825
}
57125826

5827+
if (fl_flags & FL_SLEEP) {
5828+
spin_lock(&nn->client_lock);
5829+
list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
5830+
spin_unlock(&nn->client_lock);
5831+
}
5832+
57135833
err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
5714-
switch (-err) {
5834+
switch (err) {
57155835
case 0: /* success! */
57165836
nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
57175837
status = 0;
57185838
break;
5719-
case (EAGAIN): /* conflock holds conflicting lock */
5839+
case FILE_LOCK_DEFERRED:
5840+
nbl = NULL;
5841+
/* Fallthrough */
5842+
case -EAGAIN: /* conflock holds conflicting lock */
57205843
status = nfserr_denied;
57215844
dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
57225845
nfs4_set_lock_denied(conflock, &lock->lk_denied);
57235846
break;
5724-
case (EDEADLK):
5847+
case -EDEADLK:
57255848
status = nfserr_deadlock;
57265849
break;
57275850
default:
@@ -5730,6 +5853,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
57305853
break;
57315854
}
57325855
out:
5856+
if (nbl) {
5857+
/* dequeue it if we queued it before */
5858+
if (fl_flags & FL_SLEEP) {
5859+
spin_lock(&nn->client_lock);
5860+
list_del_init(&nbl->nbl_list);
5861+
spin_unlock(&nn->client_lock);
5862+
}
5863+
free_blocked_lock(nbl);
5864+
}
57335865
if (filp)
57345866
fput(filp);
57355867
if (lock_stp) {
@@ -5753,8 +5885,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
57535885
if (open_stp)
57545886
nfs4_put_stid(&open_stp->st_stid);
57555887
nfsd4_bump_seqid(cstate, status);
5756-
if (file_lock)
5757-
locks_free_lock(file_lock);
57585888
if (conflock)
57595889
locks_free_lock(conflock);
57605890
return status;

fs/nfsd/state.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,11 +440,11 @@ struct nfs4_openowner {
440440
/*
441441
* Represents a generic "lockowner". Similar to an openowner. References to it
442442
* are held by the lock stateids that are created on its behalf. This object is
443-
* a superset of the nfs4_stateowner struct (or would be if it needed any extra
444-
* fields).
443+
* a superset of the nfs4_stateowner struct.
445444
*/
446445
struct nfs4_lockowner {
447-
struct nfs4_stateowner lo_owner; /* must be first element */
446+
struct nfs4_stateowner lo_owner; /* must be first element */
447+
struct list_head lo_blocked; /* blocked file_locks */
448448
};
449449

450450
static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
@@ -580,7 +580,13 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
580580
return (s32)(a->si_generation - b->si_generation) > 0;
581581
}
582582

583+
/*
584+
* When a client tries to get a lock on a file, we set one of these objects
585+
* on the blocking lock. When the lock becomes free, we can then issue a
586+
* CB_NOTIFY_LOCK to the server.
587+
*/
583588
struct nfsd4_blocked_lock {
589+
struct list_head nbl_list;
584590
struct file_lock nbl_lock;
585591
struct knfsd_fh nbl_fh;
586592
struct nfsd4_callback nbl_cb;

0 commit comments

Comments
 (0)