NFS client bugfixes for Linux 3.6

- NFSv3 mounts need to fail if the FSINFO rpc call fails
 - Ensure that the NFS commit cache gets torn down when we unload the
   NFS module.
 - Fix memory scribble issues when interrupting a LAYOUTGET rpc call
 - Fix NFSv4 legacy idmapper regressions
 - Fix issues with the NFSv4 getacl command
 - Fix a regression when using the legacy "mount -t nfs4"
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJQNPyKAAoJEGcL54qWCgDyQlAP/AmnLaJmKMPj5P4GCiA/uxMV
 sr0XSSVAv+OgUtRxLNO5PDyunVIIEjwA5nGa2RZEG14+eYwNEeQehaGoaGqoKLhh
 ATOCMvHHEs5qSKLckWsZWflf6U/MLimYS3D/hHVUtP0QWbBMtol3ImABs2ht/VUc
 HW0wQoEG+5mhbhC87d4ku5E+OHQzYeNNmdX2VkKOmT0CwRd/bfsHxlGwMmcHldP2
 TSge+MRhoKNycpy0j7nugHj2pZ12UiX+O8371OlAketnPk0OA+6RNGMyNo49IQAn
 VEN9MFWc4ypH1+hJ0OvhzmUA6Zn2/lyQXUtwM1DcXeBmLI4aClb3bT3G3xKNh09O
 yLEvkh2aUUlmoTSgP7vDK/Ui3QwVnsaJULvg+gywQJG6qTSFgUQfErNKgJmabHQb
 d0EuumlSGnJ7qdC5nmrUp+M/CpbfGh15ax/JnTRGVK7C3jeCm18vc2np+z4EUDp/
 USzrvuBu1B8eI0nQNoht0BeNf7sEJGgFIn8BJzxDakP8buXPMqEvFwA8c1JmMeBX
 E6pbG2jHqPdrTJtxQTpMRqhA0MxbFlVNi5cCs6U7ifiBqbRWEXyNVgAHTwoIxIrn
 ASbL0s8gZH+EMEXNOmPmFiO2NUjBCeSzAjrsTmSpWa13YmGfiroZN8N/iPzLnFf+
 FR3SFUnoJHq7x4uLZoAX
 =lqfh
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-3.6-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:
 - NFSv3 mounts need to fail if the FSINFO rpc call fails
 - Ensure that the NFS commit cache gets torn down when we unload the
   NFS module.
 - Fix memory scribble issues when interrupting a LAYOUTGET rpc call
 - Fix NFSv4 legacy idmapper regressions
 - Fix issues with the NFSv4 getacl command
 - Fix a regression when using the legacy "mount -t nfs4"

* tag 'nfs-for-3.6-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFSv3: Ensure that do_proc_get_root() reports errors correctly
  NFSv4: Ensure that nfs4_alloc_client cleans up on error.
  NFS: return -ENOKEY when the upcall fails to map the name
  NFS: Clear key construction data if the idmap upcall fails
  NFSv4: Don't use private xdr_stream fields in decode_getacl
  NFSv4: Fix the acl cache size calculation
  NFSv4: Fix pointer arithmetic in decode_getacl
  NFS: Alias the nfs module to nfs4
  NFS: Fix a regression when loading the NFS v4 module
  NFSv4.1: Remove a bogus BUG_ON() in nfs4_layoutreturn_done
  pnfs-obj: Better IO pattern in case of unaligned offset
  NFS41: add pg_layout_private to nfs_pageio_descriptor
  pnfs: nfs4_proc_layoutget returns void
  pnfs: defer release of pages in layoutget
  nfs: tear down caches in nfs_init_writepagecache when allocation fails
This commit is contained in:
Linus Torvalds 2012-08-22 09:57:25 -07:00
commit ad746be969
17 changed files with 241 additions and 119 deletions

View File

@ -12,19 +12,19 @@ nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-$(CONFIG_SYSCTL) += sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
obj-$(CONFIG_NFS_V2) += nfs2.o obj-$(CONFIG_NFS_V2) += nfsv2.o
nfs2-y := nfs2super.o proc.o nfs2xdr.o nfsv2-y := nfs2super.o proc.o nfs2xdr.o
obj-$(CONFIG_NFS_V3) += nfs3.o obj-$(CONFIG_NFS_V3) += nfsv3.o
nfs3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o nfsv3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o
nfs3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfsv3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
obj-$(CONFIG_NFS_V4) += nfs4.o obj-$(CONFIG_NFS_V4) += nfsv4.o
nfs4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \ delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \
nfs4namespace.o nfs4getroot.o nfs4client.o nfs4namespace.o nfs4getroot.o nfs4client.o
nfs4-$(CONFIG_SYSCTL) += nfs4sysctl.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o
nfs4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o
nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o

View File

@ -105,7 +105,7 @@ struct nfs_subversion *get_nfs_version(unsigned int version)
if (IS_ERR(nfs)) { if (IS_ERR(nfs)) {
mutex_lock(&nfs_version_mutex); mutex_lock(&nfs_version_mutex);
request_module("nfs%d", version); request_module("nfsv%d", version);
nfs = find_nfs_version(version); nfs = find_nfs_version(version);
mutex_unlock(&nfs_version_mutex); mutex_unlock(&nfs_version_mutex);
} }

View File

@ -61,6 +61,12 @@ struct idmap {
struct mutex idmap_mutex; struct mutex idmap_mutex;
}; };
struct idmap_legacy_upcalldata {
struct rpc_pipe_msg pipe_msg;
struct idmap_msg idmap_msg;
struct idmap *idmap;
};
/** /**
* nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
* @fattr: fully initialised struct nfs_fattr * @fattr: fully initialised struct nfs_fattr
@ -324,6 +330,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
ret = nfs_idmap_request_key(&key_type_id_resolver_legacy, ret = nfs_idmap_request_key(&key_type_id_resolver_legacy,
name, namelen, type, data, name, namelen, type, data,
data_size, idmap); data_size, idmap);
idmap->idmap_key_cons = NULL;
mutex_unlock(&idmap->idmap_mutex); mutex_unlock(&idmap->idmap_mutex);
} }
return ret; return ret;
@ -380,11 +387,13 @@ static const match_table_t nfs_idmap_tokens = {
static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *); static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *);
static ssize_t idmap_pipe_downcall(struct file *, const char __user *, static ssize_t idmap_pipe_downcall(struct file *, const char __user *,
size_t); size_t);
static void idmap_release_pipe(struct inode *);
static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *); static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
static const struct rpc_pipe_ops idmap_upcall_ops = { static const struct rpc_pipe_ops idmap_upcall_ops = {
.upcall = rpc_pipe_generic_upcall, .upcall = rpc_pipe_generic_upcall,
.downcall = idmap_pipe_downcall, .downcall = idmap_pipe_downcall,
.release_pipe = idmap_release_pipe,
.destroy_msg = idmap_pipe_destroy_msg, .destroy_msg = idmap_pipe_destroy_msg,
}; };
@ -616,7 +625,8 @@ void nfs_idmap_quit(void)
nfs_idmap_quit_keyring(); nfs_idmap_quit_keyring();
} }
static int nfs_idmap_prepare_message(char *desc, struct idmap_msg *im, static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
struct idmap_msg *im,
struct rpc_pipe_msg *msg) struct rpc_pipe_msg *msg)
{ {
substring_t substr; substring_t substr;
@ -659,6 +669,7 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
const char *op, const char *op,
void *aux) void *aux)
{ {
struct idmap_legacy_upcalldata *data;
struct rpc_pipe_msg *msg; struct rpc_pipe_msg *msg;
struct idmap_msg *im; struct idmap_msg *im;
struct idmap *idmap = (struct idmap *)aux; struct idmap *idmap = (struct idmap *)aux;
@ -666,15 +677,15 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
int ret = -ENOMEM; int ret = -ENOMEM;
/* msg and im are freed in idmap_pipe_destroy_msg */ /* msg and im are freed in idmap_pipe_destroy_msg */
msg = kmalloc(sizeof(*msg), GFP_KERNEL); data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!msg) if (!data)
goto out0;
im = kmalloc(sizeof(*im), GFP_KERNEL);
if (!im)
goto out1; goto out1;
ret = nfs_idmap_prepare_message(key->description, im, msg); msg = &data->pipe_msg;
im = &data->idmap_msg;
data->idmap = idmap;
ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
if (ret < 0) if (ret < 0)
goto out2; goto out2;
@ -683,15 +694,15 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
ret = rpc_queue_upcall(idmap->idmap_pipe, msg); ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
if (ret < 0) if (ret < 0)
goto out2; goto out3;
return ret; return ret;
out3:
idmap->idmap_key_cons = NULL;
out2: out2:
kfree(im); kfree(data);
out1: out1:
kfree(msg);
out0:
complete_request_key(cons, ret); complete_request_key(cons, ret);
return ret; return ret;
} }
@ -749,9 +760,8 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
} }
if (!(im.im_status & IDMAP_STATUS_SUCCESS)) { if (!(im.im_status & IDMAP_STATUS_SUCCESS)) {
ret = mlen; ret = -ENOKEY;
complete_request_key(cons, -ENOKEY); goto out;
goto out_incomplete;
} }
namelen_in = strnlen(im.im_name, IDMAP_NAMESZ); namelen_in = strnlen(im.im_name, IDMAP_NAMESZ);
@ -768,16 +778,32 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
out: out:
complete_request_key(cons, ret); complete_request_key(cons, ret);
out_incomplete:
return ret; return ret;
} }
static void static void
idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg) idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{ {
struct idmap_legacy_upcalldata *data = container_of(msg,
struct idmap_legacy_upcalldata,
pipe_msg);
struct idmap *idmap = data->idmap;
struct key_construction *cons;
if (msg->errno) {
cons = ACCESS_ONCE(idmap->idmap_key_cons);
idmap->idmap_key_cons = NULL;
complete_request_key(cons, msg->errno);
}
/* Free memory allocated in nfs_idmap_legacy_upcall() */ /* Free memory allocated in nfs_idmap_legacy_upcall() */
kfree(msg->data); kfree(data);
kfree(msg); }
static void
idmap_release_pipe(struct inode *inode)
{
struct rpc_inode *rpci = RPC_I(inode);
struct idmap *idmap = (struct idmap *)rpci->private;
idmap->idmap_key_cons = NULL;
} }
int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)

View File

@ -69,7 +69,7 @@ do_proc_get_root(struct rpc_clnt *client, struct nfs_fh *fhandle,
nfs_fattr_init(info->fattr); nfs_fattr_init(info->fattr);
status = rpc_call_sync(client, &msg, 0); status = rpc_call_sync(client, &msg, 0);
dprintk("%s: reply fsinfo: %d\n", __func__, status); dprintk("%s: reply fsinfo: %d\n", __func__, status);
if (!(info->fattr->valid & NFS_ATTR_FATTR)) { if (status == 0 && !(info->fattr->valid & NFS_ATTR_FATTR)) {
msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR]; msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
msg.rpc_resp = info->fattr; msg.rpc_resp = info->fattr;
status = rpc_call_sync(client, &msg, 0); status = rpc_call_sync(client, &msg, 0);

View File

@ -205,6 +205,9 @@ extern const struct dentry_operations nfs4_dentry_operations;
int nfs_atomic_open(struct inode *, struct dentry *, struct file *, int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
unsigned, umode_t, int *); unsigned, umode_t, int *);
/* super.c */
extern struct file_system_type nfs4_fs_type;
/* nfs4namespace.c */ /* nfs4namespace.c */
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *); rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *); struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *);

View File

@ -74,7 +74,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
return clp; return clp;
error: error:
kfree(clp); nfs_free_client(clp);
return ERR_PTR(err); return ERR_PTR(err);
} }

View File

@ -3737,9 +3737,10 @@ out:
static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len) static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
{ {
struct nfs4_cached_acl *acl; struct nfs4_cached_acl *acl;
size_t buflen = sizeof(*acl) + acl_len;
if (pages && acl_len <= PAGE_SIZE) { if (pages && buflen <= PAGE_SIZE) {
acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL); acl = kmalloc(buflen, GFP_KERNEL);
if (acl == NULL) if (acl == NULL)
goto out; goto out;
acl->cached = 1; acl->cached = 1;
@ -3819,7 +3820,7 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
if (ret) if (ret)
goto out_free; goto out_free;
acl_len = res.acl_len - res.acl_data_offset; acl_len = res.acl_len;
if (acl_len > args.acl_len) if (acl_len > args.acl_len)
nfs4_write_cached_acl(inode, NULL, 0, acl_len); nfs4_write_cached_acl(inode, NULL, 0, acl_len);
else else
@ -6223,11 +6224,58 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
dprintk("<-- %s\n", __func__); dprintk("<-- %s\n", __func__);
} }
static size_t max_response_pages(struct nfs_server *server)
{
u32 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
return nfs_page_array_len(0, max_resp_sz);
}
static void nfs4_free_pages(struct page **pages, size_t size)
{
int i;
if (!pages)
return;
for (i = 0; i < size; i++) {
if (!pages[i])
break;
__free_page(pages[i]);
}
kfree(pages);
}
static struct page **nfs4_alloc_pages(size_t size, gfp_t gfp_flags)
{
struct page **pages;
int i;
pages = kcalloc(size, sizeof(struct page *), gfp_flags);
if (!pages) {
dprintk("%s: can't alloc array of %zu pages\n", __func__, size);
return NULL;
}
for (i = 0; i < size; i++) {
pages[i] = alloc_page(gfp_flags);
if (!pages[i]) {
dprintk("%s: failed to allocate page\n", __func__);
nfs4_free_pages(pages, size);
return NULL;
}
}
return pages;
}
static void nfs4_layoutget_release(void *calldata) static void nfs4_layoutget_release(void *calldata)
{ {
struct nfs4_layoutget *lgp = calldata; struct nfs4_layoutget *lgp = calldata;
struct nfs_server *server = NFS_SERVER(lgp->args.inode);
size_t max_pages = max_response_pages(server);
dprintk("--> %s\n", __func__); dprintk("--> %s\n", __func__);
nfs4_free_pages(lgp->args.layout.pages, max_pages);
put_nfs_open_context(lgp->args.ctx); put_nfs_open_context(lgp->args.ctx);
kfree(calldata); kfree(calldata);
dprintk("<-- %s\n", __func__); dprintk("<-- %s\n", __func__);
@ -6239,9 +6287,10 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = {
.rpc_release = nfs4_layoutget_release, .rpc_release = nfs4_layoutget_release,
}; };
int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) void nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
{ {
struct nfs_server *server = NFS_SERVER(lgp->args.inode); struct nfs_server *server = NFS_SERVER(lgp->args.inode);
size_t max_pages = max_response_pages(server);
struct rpc_task *task; struct rpc_task *task;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
@ -6259,12 +6308,19 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
dprintk("--> %s\n", __func__); dprintk("--> %s\n", __func__);
lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags);
if (!lgp->args.layout.pages) {
nfs4_layoutget_release(lgp);
return;
}
lgp->args.layout.pglen = max_pages * PAGE_SIZE;
lgp->res.layoutp = &lgp->args.layout; lgp->res.layoutp = &lgp->args.layout;
lgp->res.seq_res.sr_slot = NULL; lgp->res.seq_res.sr_slot = NULL;
nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0); nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0);
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task))
return PTR_ERR(task); return;
status = nfs4_wait_for_completion_rpc_task(task); status = nfs4_wait_for_completion_rpc_task(task);
if (status == 0) if (status == 0)
status = task->tk_status; status = task->tk_status;
@ -6272,7 +6328,7 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
status = pnfs_layout_process(lgp); status = pnfs_layout_process(lgp);
rpc_put_task(task); rpc_put_task(task);
dprintk("<-- %s status=%d\n", __func__, status); dprintk("<-- %s status=%d\n", __func__, status);
return status; return;
} }
static void static void
@ -6304,12 +6360,8 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
return; return;
} }
spin_lock(&lo->plh_inode->i_lock); spin_lock(&lo->plh_inode->i_lock);
if (task->tk_status == 0) { if (task->tk_status == 0 && lrp->res.lrs_present)
if (lrp->res.lrs_present) {
pnfs_set_layout_stateid(lo, &lrp->res.stateid, true); pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
} else
BUG_ON(!list_empty(&lo->plh_segs));
}
lo->plh_block_lgets--; lo->plh_block_lgets--;
spin_unlock(&lo->plh_inode->i_lock); spin_unlock(&lo->plh_inode->i_lock);
dprintk("<-- %s\n", __func__); dprintk("<-- %s\n", __func__);

View File

@ -23,14 +23,6 @@ static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type, static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data); int flags, const char *dev_name, void *raw_data);
static struct file_system_type nfs4_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs_fs_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
static struct file_system_type nfs4_remote_fs_type = { static struct file_system_type nfs4_remote_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "nfs4", .name = "nfs4",
@ -344,14 +336,8 @@ static int __init init_nfs_v4(void)
if (err) if (err)
goto out1; goto out1;
err = register_filesystem(&nfs4_fs_type);
if (err < 0)
goto out2;
register_nfs_version(&nfs_v4); register_nfs_version(&nfs_v4);
return 0; return 0;
out2:
nfs4_unregister_sysctl();
out1: out1:
nfs_idmap_quit(); nfs_idmap_quit();
out: out:
@ -361,7 +347,6 @@ out:
static void __exit exit_nfs_v4(void) static void __exit exit_nfs_v4(void)
{ {
unregister_nfs_version(&nfs_v4); unregister_nfs_version(&nfs_v4);
unregister_filesystem(&nfs4_fs_type);
nfs4_unregister_sysctl(); nfs4_unregister_sysctl();
nfs_idmap_quit(); nfs_idmap_quit();
} }

View File

@ -5045,22 +5045,19 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
struct nfs_getaclres *res) struct nfs_getaclres *res)
{ {
unsigned int savep; unsigned int savep;
__be32 *bm_p;
uint32_t attrlen, uint32_t attrlen,
bitmap[3] = {0}; bitmap[3] = {0};
int status; int status;
size_t page_len = xdr->buf->page_len; unsigned int pg_offset;
res->acl_len = 0; res->acl_len = 0;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto out; goto out;
bm_p = xdr->p; xdr_enter_page(xdr, xdr->buf->page_len);
res->acl_data_offset = be32_to_cpup(bm_p) + 2;
res->acl_data_offset <<= 2; /* Calculate the offset of the page data */
/* Check if the acl data starts beyond the allocated buffer */ pg_offset = xdr->buf->head[0].iov_len;
if (res->acl_data_offset > page_len)
return -ERANGE;
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
goto out; goto out;
@ -5074,23 +5071,20 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
/* The bitmap (xdr len + bitmaps) and the attr xdr len words /* The bitmap (xdr len + bitmaps) and the attr xdr len words
* are stored with the acl data to handle the problem of * are stored with the acl data to handle the problem of
* variable length bitmaps.*/ * variable length bitmaps.*/
xdr->p = bm_p; res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
/* We ignore &savep and don't do consistency checks on /* We ignore &savep and don't do consistency checks on
* the attr length. Let userspace figure it out.... */ * the attr length. Let userspace figure it out.... */
attrlen += res->acl_data_offset; res->acl_len = attrlen;
if (attrlen > page_len) { if (attrlen > (xdr->nwords << 2)) {
if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
/* getxattr interface called with a NULL buf */ /* getxattr interface called with a NULL buf */
res->acl_len = attrlen;
goto out; goto out;
} }
dprintk("NFS: acl reply: attrlen %u > page_len %zu\n", dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
attrlen, page_len); attrlen, xdr->nwords << 2);
return -EINVAL; return -EINVAL;
} }
xdr_read_pages(xdr, attrlen);
res->acl_len = attrlen;
} else } else
status = -EOPNOTSUPP; status = -EOPNOTSUPP;

View File

@ -570,17 +570,66 @@ static bool objio_pg_test(struct nfs_pageio_descriptor *pgio,
return false; return false;
return pgio->pg_count + req->wb_bytes <= return pgio->pg_count + req->wb_bytes <=
(unsigned long)pgio->pg_layout_private;
}
void objio_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
{
pnfs_generic_pg_init_read(pgio, req);
if (unlikely(pgio->pg_lseg == NULL))
return; /* Not pNFS */
pgio->pg_layout_private = (void *)
OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length; OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
} }
static bool aligned_on_raid_stripe(u64 offset, struct ore_layout *layout,
unsigned long *stripe_end)
{
u32 stripe_off;
unsigned stripe_size;
if (layout->raid_algorithm == PNFS_OSD_RAID_0)
return true;
stripe_size = layout->stripe_unit *
(layout->group_width - layout->parity);
div_u64_rem(offset, stripe_size, &stripe_off);
if (!stripe_off)
return true;
*stripe_end = stripe_size - stripe_off;
return false;
}
void objio_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
{
unsigned long stripe_end = 0;
pnfs_generic_pg_init_write(pgio, req);
if (unlikely(pgio->pg_lseg == NULL))
return; /* Not pNFS */
if (req->wb_offset ||
!aligned_on_raid_stripe(req->wb_index * PAGE_SIZE,
&OBJIO_LSEG(pgio->pg_lseg)->layout,
&stripe_end)) {
pgio->pg_layout_private = (void *)stripe_end;
} else {
pgio->pg_layout_private = (void *)
OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
}
}
static const struct nfs_pageio_ops objio_pg_read_ops = { static const struct nfs_pageio_ops objio_pg_read_ops = {
.pg_init = pnfs_generic_pg_init_read, .pg_init = objio_init_read,
.pg_test = objio_pg_test, .pg_test = objio_pg_test,
.pg_doio = pnfs_generic_pg_readpages, .pg_doio = pnfs_generic_pg_readpages,
}; };
static const struct nfs_pageio_ops objio_pg_write_ops = { static const struct nfs_pageio_ops objio_pg_write_ops = {
.pg_init = pnfs_generic_pg_init_write, .pg_init = objio_init_write,
.pg_test = objio_pg_test, .pg_test = objio_pg_test,
.pg_doio = pnfs_generic_pg_writepages, .pg_doio = pnfs_generic_pg_writepages,
}; };

View File

@ -49,6 +49,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
hdr->io_start = req_offset(hdr->req); hdr->io_start = req_offset(hdr->req);
hdr->good_bytes = desc->pg_count; hdr->good_bytes = desc->pg_count;
hdr->dreq = desc->pg_dreq; hdr->dreq = desc->pg_dreq;
hdr->layout_private = desc->pg_layout_private;
hdr->release = release; hdr->release = release;
hdr->completion_ops = desc->pg_completion_ops; hdr->completion_ops = desc->pg_completion_ops;
if (hdr->completion_ops->init_hdr) if (hdr->completion_ops->init_hdr)
@ -268,6 +269,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
desc->pg_error = 0; desc->pg_error = 0;
desc->pg_lseg = NULL; desc->pg_lseg = NULL;
desc->pg_dreq = NULL; desc->pg_dreq = NULL;
desc->pg_layout_private = NULL;
} }
EXPORT_SYMBOL_GPL(nfs_pageio_init); EXPORT_SYMBOL_GPL(nfs_pageio_init);

View File

@ -583,9 +583,6 @@ send_layoutget(struct pnfs_layout_hdr *lo,
struct nfs_server *server = NFS_SERVER(ino); struct nfs_server *server = NFS_SERVER(ino);
struct nfs4_layoutget *lgp; struct nfs4_layoutget *lgp;
struct pnfs_layout_segment *lseg = NULL; struct pnfs_layout_segment *lseg = NULL;
struct page **pages = NULL;
int i;
u32 max_resp_sz, max_pages;
dprintk("--> %s\n", __func__); dprintk("--> %s\n", __func__);
@ -594,20 +591,6 @@ send_layoutget(struct pnfs_layout_hdr *lo,
if (lgp == NULL) if (lgp == NULL)
return NULL; return NULL;
/* allocate pages for xdr post processing */
max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
max_pages = nfs_page_array_len(0, max_resp_sz);
pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags);
if (!pages)
goto out_err_free;
for (i = 0; i < max_pages; i++) {
pages[i] = alloc_page(gfp_flags);
if (!pages[i])
goto out_err_free;
}
lgp->args.minlength = PAGE_CACHE_SIZE; lgp->args.minlength = PAGE_CACHE_SIZE;
if (lgp->args.minlength > range->length) if (lgp->args.minlength > range->length)
lgp->args.minlength = range->length; lgp->args.minlength = range->length;
@ -616,39 +599,19 @@ send_layoutget(struct pnfs_layout_hdr *lo,
lgp->args.type = server->pnfs_curr_ld->id; lgp->args.type = server->pnfs_curr_ld->id;
lgp->args.inode = ino; lgp->args.inode = ino;
lgp->args.ctx = get_nfs_open_context(ctx); lgp->args.ctx = get_nfs_open_context(ctx);
lgp->args.layout.pages = pages;
lgp->args.layout.pglen = max_pages * PAGE_SIZE;
lgp->lsegpp = &lseg; lgp->lsegpp = &lseg;
lgp->gfp_flags = gfp_flags; lgp->gfp_flags = gfp_flags;
/* Synchronously retrieve layout information from server and /* Synchronously retrieve layout information from server and
* store in lseg. * store in lseg.
*/ */
nfs4_proc_layoutget(lgp); nfs4_proc_layoutget(lgp, gfp_flags);
if (!lseg) { if (!lseg) {
/* remember that LAYOUTGET failed and suspend trying */ /* remember that LAYOUTGET failed and suspend trying */
set_bit(lo_fail_bit(range->iomode), &lo->plh_flags); set_bit(lo_fail_bit(range->iomode), &lo->plh_flags);
} }
/* free xdr pages */
for (i = 0; i < max_pages; i++)
__free_page(pages[i]);
kfree(pages);
return lseg; return lseg;
out_err_free:
/* free any allocated xdr pages, lgp as it's not used */
if (pages) {
for (i = 0; i < max_pages; i++) {
if (!pages[i])
break;
__free_page(pages[i]);
}
kfree(pages);
}
kfree(lgp);
return NULL;
} }
/* /*

View File

@ -172,7 +172,7 @@ extern int nfs4_proc_getdevicelist(struct nfs_server *server,
struct pnfs_devicelist *devlist); struct pnfs_devicelist *devlist);
extern int nfs4_proc_getdeviceinfo(struct nfs_server *server, extern int nfs4_proc_getdeviceinfo(struct nfs_server *server,
struct pnfs_device *dev); struct pnfs_device *dev);
extern int nfs4_proc_layoutget(struct nfs4_layoutget *lgp); extern void nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags);
extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp); extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp);
/* pnfs.c */ /* pnfs.c */

View File

@ -319,6 +319,34 @@ EXPORT_SYMBOL_GPL(nfs_sops);
static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *); static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *);
static int nfs4_validate_mount_data(void *options, static int nfs4_validate_mount_data(void *options,
struct nfs_parsed_mount_data *args, const char *dev_name); struct nfs_parsed_mount_data *args, const char *dev_name);
struct file_system_type nfs4_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs_fs_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
EXPORT_SYMBOL_GPL(nfs4_fs_type);
static int __init register_nfs4_fs(void)
{
return register_filesystem(&nfs4_fs_type);
}
static void unregister_nfs4_fs(void)
{
unregister_filesystem(&nfs4_fs_type);
}
#else
static int __init register_nfs4_fs(void)
{
return 0;
}
static void unregister_nfs4_fs(void)
{
}
#endif #endif
static struct shrinker acl_shrinker = { static struct shrinker acl_shrinker = {
@ -337,12 +365,18 @@ int __init register_nfs_fs(void)
if (ret < 0) if (ret < 0)
goto error_0; goto error_0;
ret = nfs_register_sysctl(); ret = register_nfs4_fs();
if (ret < 0) if (ret < 0)
goto error_1; goto error_1;
ret = nfs_register_sysctl();
if (ret < 0)
goto error_2;
register_shrinker(&acl_shrinker); register_shrinker(&acl_shrinker);
return 0; return 0;
error_2:
unregister_nfs4_fs();
error_1: error_1:
unregister_filesystem(&nfs_fs_type); unregister_filesystem(&nfs_fs_type);
error_0: error_0:
@ -356,6 +390,7 @@ void __exit unregister_nfs_fs(void)
{ {
unregister_shrinker(&acl_shrinker); unregister_shrinker(&acl_shrinker);
nfs_unregister_sysctl(); nfs_unregister_sysctl();
unregister_nfs4_fs();
unregister_filesystem(&nfs_fs_type); unregister_filesystem(&nfs_fs_type);
} }
@ -2645,4 +2680,6 @@ MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
module_param(send_implementation_id, ushort, 0644); module_param(send_implementation_id, ushort, 0644);
MODULE_PARM_DESC(send_implementation_id, MODULE_PARM_DESC(send_implementation_id,
"Send implementation ID with NFSv4.1 exchange_id"); "Send implementation ID with NFSv4.1 exchange_id");
MODULE_ALIAS("nfs4");
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */

View File

@ -1814,19 +1814,19 @@ int __init nfs_init_writepagecache(void)
nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE, nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE,
nfs_wdata_cachep); nfs_wdata_cachep);
if (nfs_wdata_mempool == NULL) if (nfs_wdata_mempool == NULL)
return -ENOMEM; goto out_destroy_write_cache;
nfs_cdata_cachep = kmem_cache_create("nfs_commit_data", nfs_cdata_cachep = kmem_cache_create("nfs_commit_data",
sizeof(struct nfs_commit_data), sizeof(struct nfs_commit_data),
0, SLAB_HWCACHE_ALIGN, 0, SLAB_HWCACHE_ALIGN,
NULL); NULL);
if (nfs_cdata_cachep == NULL) if (nfs_cdata_cachep == NULL)
return -ENOMEM; goto out_destroy_write_mempool;
nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT, nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT,
nfs_wdata_cachep); nfs_wdata_cachep);
if (nfs_commit_mempool == NULL) if (nfs_commit_mempool == NULL)
return -ENOMEM; goto out_destroy_commit_cache;
/* /*
* NFS congestion size, scale with available memory. * NFS congestion size, scale with available memory.
@ -1849,11 +1849,20 @@ int __init nfs_init_writepagecache(void)
nfs_congestion_kb = 256*1024; nfs_congestion_kb = 256*1024;
return 0; return 0;
out_destroy_commit_cache:
kmem_cache_destroy(nfs_cdata_cachep);
out_destroy_write_mempool:
mempool_destroy(nfs_wdata_mempool);
out_destroy_write_cache:
kmem_cache_destroy(nfs_wdata_cachep);
return -ENOMEM;
} }
void nfs_destroy_writepagecache(void) void nfs_destroy_writepagecache(void)
{ {
mempool_destroy(nfs_commit_mempool); mempool_destroy(nfs_commit_mempool);
kmem_cache_destroy(nfs_cdata_cachep);
mempool_destroy(nfs_wdata_mempool); mempool_destroy(nfs_wdata_mempool);
kmem_cache_destroy(nfs_wdata_cachep); kmem_cache_destroy(nfs_wdata_cachep);
} }

View File

@ -69,6 +69,7 @@ struct nfs_pageio_descriptor {
const struct nfs_pgio_completion_ops *pg_completion_ops; const struct nfs_pgio_completion_ops *pg_completion_ops;
struct pnfs_layout_segment *pg_lseg; struct pnfs_layout_segment *pg_lseg;
struct nfs_direct_req *pg_dreq; struct nfs_direct_req *pg_dreq;
void *pg_layout_private;
}; };
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) #define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))

View File

@ -1248,6 +1248,7 @@ struct nfs_pgio_header {
void (*release) (struct nfs_pgio_header *hdr); void (*release) (struct nfs_pgio_header *hdr);
const struct nfs_pgio_completion_ops *completion_ops; const struct nfs_pgio_completion_ops *completion_ops;
struct nfs_direct_req *dreq; struct nfs_direct_req *dreq;
void *layout_private;
spinlock_t lock; spinlock_t lock;
/* fields protected by lock */ /* fields protected by lock */
int pnfs_error; int pnfs_error;