aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2010-06-25 16:35:53 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2010-07-30 14:41:56 -0400
commitf11ac8db5d07b6e99d41ff4aa39d878ee5cef1c5 (patch)
tree4393cbc2653145ba3269d5143f5db7811a59698b /fs/nfs
parent1f0e890dba5b0f543fea47732116b1c65d55614e (diff)
NFSv4: Ensure that we track the NFSv4 lock state in read/write requests.
This patch fixes bugzilla entry 14501: https://bugzilla.kernel.org/show_bug.cgi?id=14501 Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/direct.c29
-rw-r--r--fs/nfs/inode.c70
-rw-r--r--fs/nfs/nfs4xdr.c8
-rw-r--r--fs/nfs/pagelist.c8
-rw-r--r--fs/nfs/read.c1
-rw-r--r--fs/nfs/write.c5
6 files changed, 104 insertions, 17 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index ad4cd31d6050..064a80961677 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -69,6 +69,7 @@ struct nfs_direct_req {
69 69
70 /* I/O parameters */ 70 /* I/O parameters */
71 struct nfs_open_context *ctx; /* file open context info */ 71 struct nfs_open_context *ctx; /* file open context info */
72 struct nfs_lock_context *l_ctx; /* Lock context info */
72 struct kiocb * iocb; /* controlling i/o request */ 73 struct kiocb * iocb; /* controlling i/o request */
73 struct inode * inode; /* target file of i/o */ 74 struct inode * inode; /* target file of i/o */
74 75
@@ -160,6 +161,7 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
160 INIT_LIST_HEAD(&dreq->rewrite_list); 161 INIT_LIST_HEAD(&dreq->rewrite_list);
161 dreq->iocb = NULL; 162 dreq->iocb = NULL;
162 dreq->ctx = NULL; 163 dreq->ctx = NULL;
164 dreq->l_ctx = NULL;
163 spin_lock_init(&dreq->lock); 165 spin_lock_init(&dreq->lock);
164 atomic_set(&dreq->io_count, 0); 166 atomic_set(&dreq->io_count, 0);
165 dreq->count = 0; 167 dreq->count = 0;
@@ -173,6 +175,8 @@ static void nfs_direct_req_free(struct kref *kref)
173{ 175{
174 struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref); 176 struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
175 177
178 if (dreq->l_ctx != NULL)
179 nfs_put_lock_context(dreq->l_ctx);
176 if (dreq->ctx != NULL) 180 if (dreq->ctx != NULL)
177 put_nfs_open_context(dreq->ctx); 181 put_nfs_open_context(dreq->ctx);
178 kmem_cache_free(nfs_direct_cachep, dreq); 182 kmem_cache_free(nfs_direct_cachep, dreq);
@@ -336,6 +340,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
336 data->cred = msg.rpc_cred; 340 data->cred = msg.rpc_cred;
337 data->args.fh = NFS_FH(inode); 341 data->args.fh = NFS_FH(inode);
338 data->args.context = ctx; 342 data->args.context = ctx;
343 data->args.lock_context = dreq->l_ctx;
339 data->args.offset = pos; 344 data->args.offset = pos;
340 data->args.pgbase = pgbase; 345 data->args.pgbase = pgbase;
341 data->args.pages = data->pagevec; 346 data->args.pages = data->pagevec;
@@ -416,24 +421,28 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
416static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, 421static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
417 unsigned long nr_segs, loff_t pos) 422 unsigned long nr_segs, loff_t pos)
418{ 423{
419 ssize_t result = 0; 424 ssize_t result = -ENOMEM;
420 struct inode *inode = iocb->ki_filp->f_mapping->host; 425 struct inode *inode = iocb->ki_filp->f_mapping->host;
421 struct nfs_direct_req *dreq; 426 struct nfs_direct_req *dreq;
422 427
423 dreq = nfs_direct_req_alloc(); 428 dreq = nfs_direct_req_alloc();
424 if (!dreq) 429 if (dreq == NULL)
425 return -ENOMEM; 430 goto out;
426 431
427 dreq->inode = inode; 432 dreq->inode = inode;
428 dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); 433 dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
434 dreq->l_ctx = nfs_get_lock_context(dreq->ctx);
435 if (dreq->l_ctx == NULL)
436 goto out_release;
429 if (!is_sync_kiocb(iocb)) 437 if (!is_sync_kiocb(iocb))
430 dreq->iocb = iocb; 438 dreq->iocb = iocb;
431 439
432 result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos); 440 result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
433 if (!result) 441 if (!result)
434 result = nfs_direct_wait(dreq); 442 result = nfs_direct_wait(dreq);
443out_release:
435 nfs_direct_req_release(dreq); 444 nfs_direct_req_release(dreq);
436 445out:
437 return result; 446 return result;
438} 447}
439 448
@@ -574,6 +583,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
574 data->args.offset = 0; 583 data->args.offset = 0;
575 data->args.count = 0; 584 data->args.count = 0;
576 data->args.context = dreq->ctx; 585 data->args.context = dreq->ctx;
586 data->args.lock_context = dreq->l_ctx;
577 data->res.count = 0; 587 data->res.count = 0;
578 data->res.fattr = &data->fattr; 588 data->res.fattr = &data->fattr;
579 data->res.verf = &data->verf; 589 data->res.verf = &data->verf;
@@ -761,6 +771,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
761 data->cred = msg.rpc_cred; 771 data->cred = msg.rpc_cred;
762 data->args.fh = NFS_FH(inode); 772 data->args.fh = NFS_FH(inode);
763 data->args.context = ctx; 773 data->args.context = ctx;
774 data->args.lock_context = dreq->l_ctx;
764 data->args.offset = pos; 775 data->args.offset = pos;
765 data->args.pgbase = pgbase; 776 data->args.pgbase = pgbase;
766 data->args.pages = data->pagevec; 777 data->args.pages = data->pagevec;
@@ -845,7 +856,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
845 unsigned long nr_segs, loff_t pos, 856 unsigned long nr_segs, loff_t pos,
846 size_t count) 857 size_t count)
847{ 858{
848 ssize_t result = 0; 859 ssize_t result = -ENOMEM;
849 struct inode *inode = iocb->ki_filp->f_mapping->host; 860 struct inode *inode = iocb->ki_filp->f_mapping->host;
850 struct nfs_direct_req *dreq; 861 struct nfs_direct_req *dreq;
851 size_t wsize = NFS_SERVER(inode)->wsize; 862 size_t wsize = NFS_SERVER(inode)->wsize;
@@ -853,7 +864,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
853 864
854 dreq = nfs_direct_req_alloc(); 865 dreq = nfs_direct_req_alloc();
855 if (!dreq) 866 if (!dreq)
856 return -ENOMEM; 867 goto out;
857 nfs_alloc_commit_data(dreq); 868 nfs_alloc_commit_data(dreq);
858 869
859 if (dreq->commit_data == NULL || count < wsize) 870 if (dreq->commit_data == NULL || count < wsize)
@@ -861,14 +872,18 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
861 872
862 dreq->inode = inode; 873 dreq->inode = inode;
863 dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); 874 dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
875 dreq->l_ctx = nfs_get_lock_context(dreq->ctx);
876 if (dreq->l_ctx != NULL)
877 goto out_release;
864 if (!is_sync_kiocb(iocb)) 878 if (!is_sync_kiocb(iocb))
865 dreq->iocb = iocb; 879 dreq->iocb = iocb;
866 880
867 result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync); 881 result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
868 if (!result) 882 if (!result)
869 result = nfs_direct_wait(dreq); 883 result = nfs_direct_wait(dreq);
884out_release:
870 nfs_direct_req_release(dreq); 885 nfs_direct_req_release(dreq);
871 886out:
872 return result; 887 return result;
873} 888}
874 889
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 099b3518feea..ec7a8f96a2c2 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -530,6 +530,68 @@ out:
530 return err; 530 return err;
531} 531}
532 532
533static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
534{
535 atomic_set(&l_ctx->count, 1);
536 l_ctx->lockowner = current->files;
537 l_ctx->pid = current->tgid;
538 INIT_LIST_HEAD(&l_ctx->list);
539}
540
541static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
542{
543 struct nfs_lock_context *pos;
544
545 list_for_each_entry(pos, &ctx->lock_context.list, list) {
546 if (pos->lockowner != current->files)
547 continue;
548 if (pos->pid != current->tgid)
549 continue;
550 atomic_inc(&pos->count);
551 return pos;
552 }
553 return NULL;
554}
555
556struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx)
557{
558 struct nfs_lock_context *res, *new = NULL;
559 struct inode *inode = ctx->path.dentry->d_inode;
560
561 spin_lock(&inode->i_lock);
562 res = __nfs_find_lock_context(ctx);
563 if (res == NULL) {
564 spin_unlock(&inode->i_lock);
565 new = kmalloc(sizeof(*new), GFP_KERNEL);
566 if (new == NULL)
567 return NULL;
568 nfs_init_lock_context(new);
569 spin_lock(&inode->i_lock);
570 res = __nfs_find_lock_context(ctx);
571 if (res == NULL) {
572 list_add_tail(&new->list, &ctx->lock_context.list);
573 new->open_context = ctx;
574 res = new;
575 new = NULL;
576 }
577 }
578 spin_unlock(&inode->i_lock);
579 kfree(new);
580 return res;
581}
582
583void nfs_put_lock_context(struct nfs_lock_context *l_ctx)
584{
585 struct nfs_open_context *ctx = l_ctx->open_context;
586 struct inode *inode = ctx->path.dentry->d_inode;
587
588 if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock))
589 return;
590 list_del(&l_ctx->list);
591 spin_unlock(&inode->i_lock);
592 kfree(l_ctx);
593}
594
533/** 595/**
534 * nfs_close_context - Common close_context() routine NFSv2/v3 596 * nfs_close_context - Common close_context() routine NFSv2/v3
535 * @ctx: pointer to context 597 * @ctx: pointer to context
@@ -566,11 +628,11 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct
566 path_get(&ctx->path); 628 path_get(&ctx->path);
567 ctx->cred = get_rpccred(cred); 629 ctx->cred = get_rpccred(cred);
568 ctx->state = NULL; 630 ctx->state = NULL;
569 ctx->lockowner = current->files;
570 ctx->flags = 0; 631 ctx->flags = 0;
571 ctx->error = 0; 632 ctx->error = 0;
572 ctx->dir_cookie = 0; 633 ctx->dir_cookie = 0;
573 atomic_set(&ctx->count, 1); 634 nfs_init_lock_context(&ctx->lock_context);
635 ctx->lock_context.open_context = ctx;
574 } 636 }
575 return ctx; 637 return ctx;
576} 638}
@@ -578,7 +640,7 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct
578struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) 640struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
579{ 641{
580 if (ctx != NULL) 642 if (ctx != NULL)
581 atomic_inc(&ctx->count); 643 atomic_inc(&ctx->lock_context.count);
582 return ctx; 644 return ctx;
583} 645}
584 646
@@ -586,7 +648,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
586{ 648{
587 struct inode *inode = ctx->path.dentry->d_inode; 649 struct inode *inode = ctx->path.dentry->d_inode;
588 650
589 if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock)) 651 if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock))
590 return; 652 return;
591 list_del(&ctx->list); 653 list_del(&ctx->list);
592 spin_unlock(&inode->i_lock); 654 spin_unlock(&inode->i_lock);
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 1f7781d636ae..873b62f209ea 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1324,14 +1324,14 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
1324 hdr->replen += decode_putrootfh_maxsz; 1324 hdr->replen += decode_putrootfh_maxsz;
1325} 1325}
1326 1326
1327static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx) 1327static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx, const struct nfs_lock_context *l_ctx)
1328{ 1328{
1329 nfs4_stateid stateid; 1329 nfs4_stateid stateid;
1330 __be32 *p; 1330 __be32 *p;
1331 1331
1332 p = reserve_space(xdr, NFS4_STATEID_SIZE); 1332 p = reserve_space(xdr, NFS4_STATEID_SIZE);
1333 if (ctx->state != NULL) { 1333 if (ctx->state != NULL) {
1334 nfs4_copy_stateid(&stateid, ctx->state, ctx->lockowner); 1334 nfs4_copy_stateid(&stateid, ctx->state, l_ctx->lockowner);
1335 xdr_encode_opaque_fixed(p, stateid.data, NFS4_STATEID_SIZE); 1335 xdr_encode_opaque_fixed(p, stateid.data, NFS4_STATEID_SIZE);
1336 } else 1336 } else
1337 xdr_encode_opaque_fixed(p, zero_stateid.data, NFS4_STATEID_SIZE); 1337 xdr_encode_opaque_fixed(p, zero_stateid.data, NFS4_STATEID_SIZE);
@@ -1344,7 +1344,7 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args,
1344 p = reserve_space(xdr, 4); 1344 p = reserve_space(xdr, 4);
1345 *p = cpu_to_be32(OP_READ); 1345 *p = cpu_to_be32(OP_READ);
1346 1346
1347 encode_stateid(xdr, args->context); 1347 encode_stateid(xdr, args->context, args->lock_context);
1348 1348
1349 p = reserve_space(xdr, 12); 1349 p = reserve_space(xdr, 12);
1350 p = xdr_encode_hyper(p, args->offset); 1350 p = xdr_encode_hyper(p, args->offset);
@@ -1523,7 +1523,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg
1523 p = reserve_space(xdr, 4); 1523 p = reserve_space(xdr, 4);
1524 *p = cpu_to_be32(OP_WRITE); 1524 *p = cpu_to_be32(OP_WRITE);
1525 1525
1526 encode_stateid(xdr, args->context); 1526 encode_stateid(xdr, args->context, args->lock_context);
1527 1527
1528 p = reserve_space(xdr, 16); 1528 p = reserve_space(xdr, 16);
1529 p = xdr_encode_hyper(p, args->offset); 1529 p = xdr_encode_hyper(p, args->offset);
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index a3654e57b589..919490232e17 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -79,6 +79,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
79 req->wb_pgbase = offset; 79 req->wb_pgbase = offset;
80 req->wb_bytes = count; 80 req->wb_bytes = count;
81 req->wb_context = get_nfs_open_context(ctx); 81 req->wb_context = get_nfs_open_context(ctx);
82 req->wb_lock_context = nfs_get_lock_context(ctx);
82 kref_init(&req->wb_kref); 83 kref_init(&req->wb_kref);
83 return req; 84 return req;
84} 85}
@@ -141,11 +142,16 @@ void nfs_clear_request(struct nfs_page *req)
141{ 142{
142 struct page *page = req->wb_page; 143 struct page *page = req->wb_page;
143 struct nfs_open_context *ctx = req->wb_context; 144 struct nfs_open_context *ctx = req->wb_context;
145 struct nfs_lock_context *l_ctx = req->wb_lock_context;
144 146
145 if (page != NULL) { 147 if (page != NULL) {
146 page_cache_release(page); 148 page_cache_release(page);
147 req->wb_page = NULL; 149 req->wb_page = NULL;
148 } 150 }
151 if (l_ctx != NULL) {
152 nfs_put_lock_context(l_ctx);
153 req->wb_lock_context = NULL;
154 }
149 if (ctx != NULL) { 155 if (ctx != NULL) {
150 put_nfs_open_context(ctx); 156 put_nfs_open_context(ctx);
151 req->wb_context = NULL; 157 req->wb_context = NULL;
@@ -235,7 +241,7 @@ static int nfs_can_coalesce_requests(struct nfs_page *prev,
235{ 241{
236 if (req->wb_context->cred != prev->wb_context->cred) 242 if (req->wb_context->cred != prev->wb_context->cred)
237 return 0; 243 return 0;
238 if (req->wb_context->lockowner != prev->wb_context->lockowner) 244 if (req->wb_lock_context->lockowner != prev->wb_lock_context->lockowner)
239 return 0; 245 return 0;
240 if (req->wb_context->state != prev->wb_context->state) 246 if (req->wb_context->state != prev->wb_context->state)
241 return 0; 247 return 0;
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 5a33a92e8168..87adc2744246 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -190,6 +190,7 @@ static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
190 data->args.pages = data->pagevec; 190 data->args.pages = data->pagevec;
191 data->args.count = count; 191 data->args.count = count;
192 data->args.context = get_nfs_open_context(req->wb_context); 192 data->args.context = get_nfs_open_context(req->wb_context);
193 data->args.lock_context = req->wb_lock_context;
193 194
194 data->res.fattr = &data->fattr; 195 data->res.fattr = &data->fattr;
195 data->res.count = count; 196 data->res.count = count;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 03df22822c4c..5eccea127cac 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -689,7 +689,9 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
689 req = nfs_page_find_request(page); 689 req = nfs_page_find_request(page);
690 if (req == NULL) 690 if (req == NULL)
691 return 0; 691 return 0;
692 do_flush = req->wb_page != page || req->wb_context != ctx; 692 do_flush = req->wb_page != page || req->wb_context != ctx ||
693 req->wb_lock_context->lockowner != current->files ||
694 req->wb_lock_context->pid != current->tgid;
693 nfs_release_request(req); 695 nfs_release_request(req);
694 if (!do_flush) 696 if (!do_flush)
695 return 0; 697 return 0;
@@ -813,6 +815,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
813 data->args.pages = data->pagevec; 815 data->args.pages = data->pagevec;
814 data->args.count = count; 816 data->args.count = count;
815 data->args.context = get_nfs_open_context(req->wb_context); 817 data->args.context = get_nfs_open_context(req->wb_context);
818 data->args.lock_context = req->wb_lock_context;
816 data->args.stable = NFS_UNSTABLE; 819 data->args.stable = NFS_UNSTABLE;
817 if (how & FLUSH_STABLE) { 820 if (how & FLUSH_STABLE) {
818 data->args.stable = NFS_DATA_SYNC; 821 data->args.stable = NFS_DATA_SYNC;