mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
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:
parent
1f0e890dba
commit
f11ac8db5d
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user