NFSv4: Ensure that we track the NFSv4 lock state in read/write requests.
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 25 Jun 2010 20:35:53 +0000 (16:35 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 30 Jul 2010 18:41:56 +0000 (14:41 -0400)
This patch fixes bugzilla entry 14501:
  https://bugzilla.kernel.org/show_bug.cgi?id=14501

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/direct.c
fs/nfs/inode.c
fs/nfs/nfs4xdr.c
fs/nfs/pagelist.c
fs/nfs/read.c
fs/nfs/write.c
include/linux/nfs_fs.h
include/linux/nfs_page.h
include/linux/nfs_xdr.h

index ad4cd31d6050d22c2a6267b005bab48b84f70f66..064a80961677fbf3c62e309a4f15693e391ed7e6 100644 (file)
@@ -69,6 +69,7 @@ struct nfs_direct_req {
 
        /* I/O parameters */
        struct nfs_open_context *ctx;           /* file open context info */
+       struct nfs_lock_context *l_ctx;         /* Lock context info */
        struct kiocb *          iocb;           /* controlling i/o request */
        struct inode *          inode;          /* target file of i/o */
 
@@ -160,6 +161,7 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
        INIT_LIST_HEAD(&dreq->rewrite_list);
        dreq->iocb = NULL;
        dreq->ctx = NULL;
+       dreq->l_ctx = NULL;
        spin_lock_init(&dreq->lock);
        atomic_set(&dreq->io_count, 0);
        dreq->count = 0;
@@ -173,6 +175,8 @@ static void nfs_direct_req_free(struct kref *kref)
 {
        struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
 
+       if (dreq->l_ctx != NULL)
+               nfs_put_lock_context(dreq->l_ctx);
        if (dreq->ctx != NULL)
                put_nfs_open_context(dreq->ctx);
        kmem_cache_free(nfs_direct_cachep, dreq);
@@ -336,6 +340,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
                data->cred = msg.rpc_cred;
                data->args.fh = NFS_FH(inode);
                data->args.context = ctx;
+               data->args.lock_context = dreq->l_ctx;
                data->args.offset = pos;
                data->args.pgbase = pgbase;
                data->args.pages = data->pagevec;
@@ -416,24 +421,28 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
 static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
                               unsigned long nr_segs, loff_t pos)
 {
-       ssize_t result = 0;
+       ssize_t result = -ENOMEM;
        struct inode *inode = iocb->ki_filp->f_mapping->host;
        struct nfs_direct_req *dreq;
 
        dreq = nfs_direct_req_alloc();
-       if (!dreq)
-               return -ENOMEM;
+       if (dreq == NULL)
+               goto out;
 
        dreq->inode = inode;
        dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
+       dreq->l_ctx = nfs_get_lock_context(dreq->ctx);
+       if (dreq->l_ctx == NULL)
+               goto out_release;
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
        result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
        if (!result)
                result = nfs_direct_wait(dreq);
+out_release:
        nfs_direct_req_release(dreq);
-
+out:
        return result;
 }
 
@@ -574,6 +583,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
        data->args.offset = 0;
        data->args.count = 0;
        data->args.context = dreq->ctx;
+       data->args.lock_context = dreq->l_ctx;
        data->res.count = 0;
        data->res.fattr = &data->fattr;
        data->res.verf = &data->verf;
@@ -761,6 +771,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
                data->cred = msg.rpc_cred;
                data->args.fh = NFS_FH(inode);
                data->args.context = ctx;
+               data->args.lock_context = dreq->l_ctx;
                data->args.offset = pos;
                data->args.pgbase = pgbase;
                data->args.pages = data->pagevec;
@@ -845,7 +856,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
                                unsigned long nr_segs, loff_t pos,
                                size_t count)
 {
-       ssize_t result = 0;
+       ssize_t result = -ENOMEM;
        struct inode *inode = iocb->ki_filp->f_mapping->host;
        struct nfs_direct_req *dreq;
        size_t wsize = NFS_SERVER(inode)->wsize;
@@ -853,7 +864,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
 
        dreq = nfs_direct_req_alloc();
        if (!dreq)
-               return -ENOMEM;
+               goto out;
        nfs_alloc_commit_data(dreq);
 
        if (dreq->commit_data == NULL || count < wsize)
@@ -861,14 +872,18 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
 
        dreq->inode = inode;
        dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
+       dreq->l_ctx = nfs_get_lock_context(dreq->ctx);
+       if (dreq->l_ctx != NULL)
+               goto out_release;
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
        result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
        if (!result)
                result = nfs_direct_wait(dreq);
+out_release:
        nfs_direct_req_release(dreq);
-
+out:
        return result;
 }
 
index 099b3518feea6409861c55e7ca7a5fbe421c0125..ec7a8f96a2c2fbc4ab995222502fa6f08b21ed28 100644 (file)
@@ -530,6 +530,68 @@ out:
        return err;
 }
 
+static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
+{
+       atomic_set(&l_ctx->count, 1);
+       l_ctx->lockowner = current->files;
+       l_ctx->pid = current->tgid;
+       INIT_LIST_HEAD(&l_ctx->list);
+}
+
+static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
+{
+       struct nfs_lock_context *pos;
+
+       list_for_each_entry(pos, &ctx->lock_context.list, list) {
+               if (pos->lockowner != current->files)
+                       continue;
+               if (pos->pid != current->tgid)
+                       continue;
+               atomic_inc(&pos->count);
+               return pos;
+       }
+       return NULL;
+}
+
+struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx)
+{
+       struct nfs_lock_context *res, *new = NULL;
+       struct inode *inode = ctx->path.dentry->d_inode;
+
+       spin_lock(&inode->i_lock);
+       res = __nfs_find_lock_context(ctx);
+       if (res == NULL) {
+               spin_unlock(&inode->i_lock);
+               new = kmalloc(sizeof(*new), GFP_KERNEL);
+               if (new == NULL)
+                       return NULL;
+               nfs_init_lock_context(new);
+               spin_lock(&inode->i_lock);
+               res = __nfs_find_lock_context(ctx);
+               if (res == NULL) {
+                       list_add_tail(&new->list, &ctx->lock_context.list);
+                       new->open_context = ctx;
+                       res = new;
+                       new = NULL;
+               }
+       }
+       spin_unlock(&inode->i_lock);
+       kfree(new);
+       return res;
+}
+
+void nfs_put_lock_context(struct nfs_lock_context *l_ctx)
+{
+       struct nfs_open_context *ctx = l_ctx->open_context;
+       struct inode *inode = ctx->path.dentry->d_inode;
+
+       if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock))
+               return;
+       list_del(&l_ctx->list);
+       spin_unlock(&inode->i_lock);
+       kfree(l_ctx);
+}
+
 /**
  * nfs_close_context - Common close_context() routine NFSv2/v3
  * @ctx: pointer to context
@@ -566,11 +628,11 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct
                path_get(&ctx->path);
                ctx->cred = get_rpccred(cred);
                ctx->state = NULL;
-               ctx->lockowner = current->files;
                ctx->flags = 0;
                ctx->error = 0;
                ctx->dir_cookie = 0;
-               atomic_set(&ctx->count, 1);
+               nfs_init_lock_context(&ctx->lock_context);
+               ctx->lock_context.open_context = ctx;
        }
        return ctx;
 }
@@ -578,7 +640,7 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct
 struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
 {
        if (ctx != NULL)
-               atomic_inc(&ctx->count);
+               atomic_inc(&ctx->lock_context.count);
        return ctx;
 }
 
@@ -586,7 +648,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
 {
        struct inode *inode = ctx->path.dentry->d_inode;
 
-       if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock))
+       if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock))
                return;
        list_del(&ctx->list);
        spin_unlock(&inode->i_lock);
index 1f7781d636ae7cd1d313fa2864b490d06e183a21..873b62f209ea2f2c2f6a7b8537f4805b3785b2fe 100644 (file)
@@ -1324,14 +1324,14 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
        hdr->replen += decode_putrootfh_maxsz;
 }
 
-static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
+static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx, const struct nfs_lock_context *l_ctx)
 {
        nfs4_stateid stateid;
        __be32 *p;
 
        p = reserve_space(xdr, NFS4_STATEID_SIZE);
        if (ctx->state != NULL) {
-               nfs4_copy_stateid(&stateid, ctx->state, ctx->lockowner);
+               nfs4_copy_stateid(&stateid, ctx->state, l_ctx->lockowner);
                xdr_encode_opaque_fixed(p, stateid.data, NFS4_STATEID_SIZE);
        } else
                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,
        p = reserve_space(xdr, 4);
        *p = cpu_to_be32(OP_READ);
 
-       encode_stateid(xdr, args->context);
+       encode_stateid(xdr, args->context, args->lock_context);
 
        p = reserve_space(xdr, 12);
        p = xdr_encode_hyper(p, args->offset);
@@ -1523,7 +1523,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg
        p = reserve_space(xdr, 4);
        *p = cpu_to_be32(OP_WRITE);
 
-       encode_stateid(xdr, args->context);
+       encode_stateid(xdr, args->context, args->lock_context);
 
        p = reserve_space(xdr, 16);
        p = xdr_encode_hyper(p, args->offset);
index a3654e57b589a603bf363fd339cb2915dffe5f7b..919490232e17ea887fd96b50f0b709e12b5f0520 100644 (file)
@@ -79,6 +79,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
        req->wb_pgbase  = offset;
        req->wb_bytes   = count;
        req->wb_context = get_nfs_open_context(ctx);
+       req->wb_lock_context = nfs_get_lock_context(ctx);
        kref_init(&req->wb_kref);
        return req;
 }
@@ -141,11 +142,16 @@ void nfs_clear_request(struct nfs_page *req)
 {
        struct page *page = req->wb_page;
        struct nfs_open_context *ctx = req->wb_context;
+       struct nfs_lock_context *l_ctx = req->wb_lock_context;
 
        if (page != NULL) {
                page_cache_release(page);
                req->wb_page = NULL;
        }
+       if (l_ctx != NULL) {
+               nfs_put_lock_context(l_ctx);
+               req->wb_lock_context = NULL;
+       }
        if (ctx != NULL) {
                put_nfs_open_context(ctx);
                req->wb_context = NULL;
@@ -235,7 +241,7 @@ static int nfs_can_coalesce_requests(struct nfs_page *prev,
 {
        if (req->wb_context->cred != prev->wb_context->cred)
                return 0;
-       if (req->wb_context->lockowner != prev->wb_context->lockowner)
+       if (req->wb_lock_context->lockowner != prev->wb_lock_context->lockowner)
                return 0;
        if (req->wb_context->state != prev->wb_context->state)
                return 0;
index 5a33a92e8168155b25ad3c8aa9280f31574fc81f..87adc2744246a59aafe6339f16599517c6e3c03b 100644 (file)
@@ -190,6 +190,7 @@ static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
        data->args.pages  = data->pagevec;
        data->args.count  = count;
        data->args.context = get_nfs_open_context(req->wb_context);
+       data->args.lock_context = req->wb_lock_context;
 
        data->res.fattr   = &data->fattr;
        data->res.count   = count;
index 03df22822c4cc85aff285dba6f278dab591a8b73..5eccea127cac9b9ebe3f3cfaef96a0d023e92575 100644 (file)
@@ -689,7 +689,9 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
                req = nfs_page_find_request(page);
                if (req == NULL)
                        return 0;
-               do_flush = req->wb_page != page || req->wb_context != ctx;
+               do_flush = req->wb_page != page || req->wb_context != ctx ||
+                       req->wb_lock_context->lockowner != current->files ||
+                       req->wb_lock_context->pid != current->tgid;
                nfs_release_request(req);
                if (!do_flush)
                        return 0;
@@ -813,6 +815,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
        data->args.pages  = data->pagevec;
        data->args.count  = count;
        data->args.context = get_nfs_open_context(req->wb_context);
+       data->args.lock_context = req->wb_lock_context;
        data->args.stable  = NFS_UNSTABLE;
        if (how & FLUSH_STABLE) {
                data->args.stable = NFS_DATA_SYNC;
index 77c2ae53431cccbdccf7932a84cd032e0b7e914c..a9d802615082776955146d77eb2a9093cf93e7b0 100644 (file)
@@ -72,13 +72,20 @@ struct nfs_access_entry {
        int                     mask;
 };
 
+struct nfs_lock_context {
+       atomic_t count;
+       struct list_head list;
+       struct nfs_open_context *open_context;
+       fl_owner_t lockowner;
+       pid_t pid;
+};
+
 struct nfs4_state;
 struct nfs_open_context {
-       atomic_t count;
+       struct nfs_lock_context lock_context;
        struct path path;
        struct rpc_cred *cred;
        struct nfs4_state *state;
-       fl_owner_t lockowner;
        fmode_t mode;
 
        unsigned long flags;
@@ -353,6 +360,8 @@ extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
 extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
 extern void put_nfs_open_context(struct nfs_open_context *ctx);
 extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
+extern struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx);
+extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx);
 extern u64 nfs_compat_user_ino64(u64 fileid);
 extern void nfs_fattr_init(struct nfs_fattr *fattr);
 
index 3c60685d972b595a5be6366328276a9c5a1cbcdb..f8b60e7f4c44d9b652a94bca2ab8945130bdb061 100644 (file)
@@ -39,6 +39,7 @@ struct nfs_page {
        struct list_head        wb_list;        /* Defines state of page: */
        struct page             *wb_page;       /* page to read in/write out */
        struct nfs_open_context *wb_context;    /* File state context info */
+       struct nfs_lock_context *wb_lock_context;       /* lock context info */
        atomic_t                wb_complete;    /* i/os we're waiting for */
        pgoff_t                 wb_index;       /* Offset >> PAGE_CACHE_SHIFT */
        unsigned int            wb_offset,      /* Offset & ~PAGE_CACHE_MASK */
index a319cb926abf74ea7b77cd6e030a69391974f529..87202c7026e3436f7fae6c63d5c60bca5c1ccd46 100644 (file)
@@ -334,6 +334,7 @@ struct nfs4_delegreturnres {
 struct nfs_readargs {
        struct nfs_fh *         fh;
        struct nfs_open_context *context;
+       struct nfs_lock_context *lock_context;
        __u64                   offset;
        __u32                   count;
        unsigned int            pgbase;
@@ -354,6 +355,7 @@ struct nfs_readres {
 struct nfs_writeargs {
        struct nfs_fh *         fh;
        struct nfs_open_context *context;
+       struct nfs_lock_context *lock_context;
        __u64                   offset;
        __u32                   count;
        enum nfs3_stable_how    stable;