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>
This commit is contained in:
Trond Myklebust 2010-06-25 16:35:53 -04:00
parent 1f0e890dba
commit f11ac8db5d
9 changed files with 118 additions and 19 deletions

View File

@ -69,6 +69,7 @@ struct nfs_direct_req {
/* I/O parameters */ /* I/O parameters */
struct nfs_open_context *ctx; /* file open context info */ 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 kiocb * iocb; /* controlling i/o request */
struct inode * inode; /* target file of i/o */ 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); INIT_LIST_HEAD(&dreq->rewrite_list);
dreq->iocb = NULL; dreq->iocb = NULL;
dreq->ctx = NULL; dreq->ctx = NULL;
dreq->l_ctx = NULL;
spin_lock_init(&dreq->lock); spin_lock_init(&dreq->lock);
atomic_set(&dreq->io_count, 0); atomic_set(&dreq->io_count, 0);
dreq->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); 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) if (dreq->ctx != NULL)
put_nfs_open_context(dreq->ctx); put_nfs_open_context(dreq->ctx);
kmem_cache_free(nfs_direct_cachep, dreq); 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->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode); data->args.fh = NFS_FH(inode);
data->args.context = ctx; data->args.context = ctx;
data->args.lock_context = dreq->l_ctx;
data->args.offset = pos; data->args.offset = pos;
data->args.pgbase = pgbase; data->args.pgbase = pgbase;
data->args.pages = data->pagevec; 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, static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos) 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 inode *inode = iocb->ki_filp->f_mapping->host;
struct nfs_direct_req *dreq; struct nfs_direct_req *dreq;
dreq = nfs_direct_req_alloc(); dreq = nfs_direct_req_alloc();
if (!dreq) if (dreq == NULL)
return -ENOMEM; goto out;
dreq->inode = inode; dreq->inode = inode;
dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); 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)) if (!is_sync_kiocb(iocb))
dreq->iocb = iocb; dreq->iocb = iocb;
result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos); result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
if (!result) if (!result)
result = nfs_direct_wait(dreq); result = nfs_direct_wait(dreq);
out_release:
nfs_direct_req_release(dreq); nfs_direct_req_release(dreq);
out:
return result; return result;
} }
@ -574,6 +583,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
data->args.offset = 0; data->args.offset = 0;
data->args.count = 0; data->args.count = 0;
data->args.context = dreq->ctx; data->args.context = dreq->ctx;
data->args.lock_context = dreq->l_ctx;
data->res.count = 0; data->res.count = 0;
data->res.fattr = &data->fattr; data->res.fattr = &data->fattr;
data->res.verf = &data->verf; 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->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode); data->args.fh = NFS_FH(inode);
data->args.context = ctx; data->args.context = ctx;
data->args.lock_context = dreq->l_ctx;
data->args.offset = pos; data->args.offset = pos;
data->args.pgbase = pgbase; data->args.pgbase = pgbase;
data->args.pages = data->pagevec; 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, unsigned long nr_segs, loff_t pos,
size_t count) size_t count)
{ {
ssize_t result = 0; ssize_t result = -ENOMEM;
struct inode *inode = iocb->ki_filp->f_mapping->host; struct inode *inode = iocb->ki_filp->f_mapping->host;
struct nfs_direct_req *dreq; struct nfs_direct_req *dreq;
size_t wsize = NFS_SERVER(inode)->wsize; 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(); dreq = nfs_direct_req_alloc();
if (!dreq) if (!dreq)
return -ENOMEM; goto out;
nfs_alloc_commit_data(dreq); nfs_alloc_commit_data(dreq);
if (dreq->commit_data == NULL || count < wsize) 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->inode = inode;
dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); 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)) if (!is_sync_kiocb(iocb))
dreq->iocb = iocb; dreq->iocb = iocb;
result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync); result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
if (!result) if (!result)
result = nfs_direct_wait(dreq); result = nfs_direct_wait(dreq);
out_release:
nfs_direct_req_release(dreq); nfs_direct_req_release(dreq);
out:
return result; return result;
} }

View File

@ -530,6 +530,68 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
return err; 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 * nfs_close_context - Common close_context() routine NFSv2/v3
* @ctx: pointer to context * @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); path_get(&ctx->path);
ctx->cred = get_rpccred(cred); ctx->cred = get_rpccred(cred);
ctx->state = NULL; ctx->state = NULL;
ctx->lockowner = current->files;
ctx->flags = 0; ctx->flags = 0;
ctx->error = 0; ctx->error = 0;
ctx->dir_cookie = 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; 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) struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
{ {
if (ctx != NULL) if (ctx != NULL)
atomic_inc(&ctx->count); atomic_inc(&ctx->lock_context.count);
return ctx; 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; 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; return;
list_del(&ctx->list); list_del(&ctx->list);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);

View File

@ -1324,14 +1324,14 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
hdr->replen += decode_putrootfh_maxsz; 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; nfs4_stateid stateid;
__be32 *p; __be32 *p;
p = reserve_space(xdr, NFS4_STATEID_SIZE); p = reserve_space(xdr, NFS4_STATEID_SIZE);
if (ctx->state != NULL) { 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); xdr_encode_opaque_fixed(p, stateid.data, NFS4_STATEID_SIZE);
} else } else
xdr_encode_opaque_fixed(p, zero_stateid.data, NFS4_STATEID_SIZE); 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 = reserve_space(xdr, 4);
*p = cpu_to_be32(OP_READ); *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 = reserve_space(xdr, 12);
p = xdr_encode_hyper(p, args->offset); 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 = reserve_space(xdr, 4);
*p = cpu_to_be32(OP_WRITE); *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 = reserve_space(xdr, 16);
p = xdr_encode_hyper(p, args->offset); p = xdr_encode_hyper(p, args->offset);

View File

@ -79,6 +79,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
req->wb_pgbase = offset; req->wb_pgbase = offset;
req->wb_bytes = count; req->wb_bytes = count;
req->wb_context = get_nfs_open_context(ctx); req->wb_context = get_nfs_open_context(ctx);
req->wb_lock_context = nfs_get_lock_context(ctx);
kref_init(&req->wb_kref); kref_init(&req->wb_kref);
return req; return req;
} }
@ -141,11 +142,16 @@ void nfs_clear_request(struct nfs_page *req)
{ {
struct page *page = req->wb_page; struct page *page = req->wb_page;
struct nfs_open_context *ctx = req->wb_context; struct nfs_open_context *ctx = req->wb_context;
struct nfs_lock_context *l_ctx = req->wb_lock_context;
if (page != NULL) { if (page != NULL) {
page_cache_release(page); page_cache_release(page);
req->wb_page = NULL; req->wb_page = NULL;
} }
if (l_ctx != NULL) {
nfs_put_lock_context(l_ctx);
req->wb_lock_context = NULL;
}
if (ctx != NULL) { if (ctx != NULL) {
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
req->wb_context = NULL; 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) if (req->wb_context->cred != prev->wb_context->cred)
return 0; return 0;
if (req->wb_context->lockowner != prev->wb_context->lockowner) if (req->wb_lock_context->lockowner != prev->wb_lock_context->lockowner)
return 0; return 0;
if (req->wb_context->state != prev->wb_context->state) if (req->wb_context->state != prev->wb_context->state)
return 0; return 0;

View 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.pages = data->pagevec;
data->args.count = count; data->args.count = count;
data->args.context = get_nfs_open_context(req->wb_context); 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.fattr = &data->fattr;
data->res.count = count; data->res.count = count;

View File

@ -689,7 +689,9 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
req = nfs_page_find_request(page); req = nfs_page_find_request(page);
if (req == NULL) if (req == NULL)
return 0; 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); nfs_release_request(req);
if (!do_flush) if (!do_flush)
return 0; return 0;
@ -813,6 +815,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
data->args.pages = data->pagevec; data->args.pages = data->pagevec;
data->args.count = count; data->args.count = count;
data->args.context = get_nfs_open_context(req->wb_context); data->args.context = get_nfs_open_context(req->wb_context);
data->args.lock_context = req->wb_lock_context;
data->args.stable = NFS_UNSTABLE; data->args.stable = NFS_UNSTABLE;
if (how & FLUSH_STABLE) { if (how & FLUSH_STABLE) {
data->args.stable = NFS_DATA_SYNC; data->args.stable = NFS_DATA_SYNC;

View File

@ -72,13 +72,20 @@ struct nfs_access_entry {
int mask; 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 nfs4_state;
struct nfs_open_context { struct nfs_open_context {
atomic_t count; struct nfs_lock_context lock_context;
struct path path; struct path path;
struct rpc_cred *cred; struct rpc_cred *cred;
struct nfs4_state *state; struct nfs4_state *state;
fl_owner_t lockowner;
fmode_t mode; fmode_t mode;
unsigned long flags; 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 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 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_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 u64 nfs_compat_user_ino64(u64 fileid);
extern void nfs_fattr_init(struct nfs_fattr *fattr); extern void nfs_fattr_init(struct nfs_fattr *fattr);

View File

@ -39,6 +39,7 @@ struct nfs_page {
struct list_head wb_list; /* Defines state of page: */ struct list_head wb_list; /* Defines state of page: */
struct page *wb_page; /* page to read in/write out */ struct page *wb_page; /* page to read in/write out */
struct nfs_open_context *wb_context; /* File state context info */ 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 */ atomic_t wb_complete; /* i/os we're waiting for */
pgoff_t wb_index; /* Offset >> PAGE_CACHE_SHIFT */ pgoff_t wb_index; /* Offset >> PAGE_CACHE_SHIFT */
unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */ unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */

View File

@ -334,6 +334,7 @@ struct nfs4_delegreturnres {
struct nfs_readargs { struct nfs_readargs {
struct nfs_fh * fh; struct nfs_fh * fh;
struct nfs_open_context *context; struct nfs_open_context *context;
struct nfs_lock_context *lock_context;
__u64 offset; __u64 offset;
__u32 count; __u32 count;
unsigned int pgbase; unsigned int pgbase;
@ -354,6 +355,7 @@ struct nfs_readres {
struct nfs_writeargs { struct nfs_writeargs {
struct nfs_fh * fh; struct nfs_fh * fh;
struct nfs_open_context *context; struct nfs_open_context *context;
struct nfs_lock_context *lock_context;
__u64 offset; __u64 offset;
__u32 count; __u32 count;
enum nfs3_stable_how stable; enum nfs3_stable_how stable;