mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-18 13:36:07 +07:00
NFS client bugfixes for Linux 3.6
- Final (hopefully) fix for the range checking code in NFSv4 getacl. This should fix the Oopses being seen when the acl size is close to PAGE_SIZE. - Fix a regression with the legacy binary mount code - Fix a regression in the readdir cookieverf initialisation - Fix an RPC over UDP regression - Ensure that we report all errors in the NFSv4 open code - Ensure that fsync() reports all relevant synchronisation errors. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQUN+KAAoJEGcL54qWCgDyHGcQAKj7MYVDIjhdmsVGGNWXUCnf X0LVg/ajh+vjusK+hmquzcJikZqgce5IU5DW4vcFr1X8BgP+R51UVvU0KksByD5H ourV2JVCztAQzQ4WWOsZAGqN0tooJUjyEjl4lEiDsQCF4Nk1HWbuCHeYuX74OToZ jrgedj0EZ6zb7TOizvbgU/7lI+FKu3Hlw6+u27M9phtSuefJdYSHZHYVMOX81qPh k0zgZ4tuLIaDuBB84iCrPwNt9icnevq6cIc+AGluI6xhDw+foPvUaUR+OUI420IZ tunNzP2So+nNoyjEiyMVENaCdEyA75XAmmGHTUUdBiVOsMV4HF/TqvTtSsjk2mN1 FbZVvtjD6srjsQaKdVmqMIZBdhY9LSMLIQVqb4H2rYP6Mwq06WTuyCxf5YhzFfoy 2tai7JuqBkTAWfKB8ESWywV6Qk/MkUWRAOBO6ksS66gAwpcFDj6nfeAdwaEmoYKc uzLUIRZaclPMZf661cs1fWeFV5XOnCL7je4owgTRGs7MHooWHPcC3273fEJqnhFz 5MkC7nfmUiGcdO1v0mfYTEtMj9Pp9icBoZcVTGn4eZIHzvhhZOx//8LhyBfS+jll bKjaLZ1rErvIqwnSGcB7PK2yBYY9P6ZaxWjOrAAncZmiOxfhN0hvCo54jNOr/VZ+ atsDEAuqSTeK7ouBqyO4 =e5yE -----END PGP SIGNATURE----- Merge tag 'nfs-for-3.6-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs Pull NFS client bugfixes from Trond Myklebust: - Final (hopefully) fix for the range checking code in NFSv4 getacl. This should fix the Oopses being seen when the acl size is close to PAGE_SIZE. - Fix a regression with the legacy binary mount code - Fix a regression in the readdir cookieverf initialisation - Fix an RPC over UDP regression - Ensure that we report all errors in the NFSv4 open code - Ensure that fsync() reports all relevant synchronisation errors. * tag 'nfs-for-3.6-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: NFS: fsync() must exit with an error if page writeback failed SUNRPC: Fix a UDP transport regression NFS: return error from decode_getfh in decode open NFSv4: Fix buffer overflow checking in __nfs4_get_acl_uncached NFSv4: Fix range checking in __nfs4_get_acl_uncached and __nfs4_proc_set_acl NFS: Fix a problem with the legacy binary mount code NFS: Fix the initialisation of the readdir 'cookieverf' array
This commit is contained in:
commit
22b4e63ebe
@ -287,10 +287,12 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
mutex_lock(&inode->i_mutex);
|
||||
ret = nfs_file_fsync_commit(file, start, end, datasync);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ static void nfs_zap_caches_locked(struct inode *inode)
|
||||
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
|
||||
nfsi->attrtimeo_timestamp = jiffies;
|
||||
|
||||
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
|
||||
memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
|
||||
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
|
||||
else
|
||||
|
@ -643,7 +643,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
||||
u64 cookie, struct page **pages, unsigned int count, int plus)
|
||||
{
|
||||
struct inode *dir = dentry->d_inode;
|
||||
__be32 *verf = NFS_COOKIEVERF(dir);
|
||||
__be32 *verf = NFS_I(dir)->cookieverf;
|
||||
struct nfs3_readdirargs arg = {
|
||||
.fh = NFS_FH(dir),
|
||||
.cookie = cookie,
|
||||
|
@ -96,13 +96,15 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
mutex_lock(&inode->i_mutex);
|
||||
ret = nfs_file_fsync_commit(file, start, end, datasync);
|
||||
if (!ret && !datasync)
|
||||
/* application has asked for meta-data sync */
|
||||
ret = pnfs_layoutcommit_inode(inode, true);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -3215,11 +3215,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
||||
dentry->d_parent->d_name.name,
|
||||
dentry->d_name.name,
|
||||
(unsigned long long)cookie);
|
||||
nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
|
||||
nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args);
|
||||
res.pgbase = args.pgbase;
|
||||
status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
|
||||
if (status >= 0) {
|
||||
memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
|
||||
memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE);
|
||||
status += args.pgbase;
|
||||
}
|
||||
|
||||
@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
|
||||
&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
|
||||
}
|
||||
|
||||
/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that
|
||||
* it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on
|
||||
/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
|
||||
* it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on
|
||||
* the stack.
|
||||
*/
|
||||
#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)
|
||||
#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
|
||||
|
||||
static int buf_to_pages_noslab(const void *buf, size_t buflen,
|
||||
struct page **pages, unsigned int *pgbase)
|
||||
@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen,
|
||||
spages = pages;
|
||||
|
||||
do {
|
||||
len = min_t(size_t, PAGE_CACHE_SIZE, buflen);
|
||||
len = min_t(size_t, PAGE_SIZE, buflen);
|
||||
newpage = alloc_page(GFP_KERNEL);
|
||||
|
||||
if (newpage == NULL)
|
||||
@ -3739,7 +3739,7 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size
|
||||
struct nfs4_cached_acl *acl;
|
||||
size_t buflen = sizeof(*acl) + acl_len;
|
||||
|
||||
if (pages && buflen <= PAGE_SIZE) {
|
||||
if (buflen <= PAGE_SIZE) {
|
||||
acl = kmalloc(buflen, GFP_KERNEL);
|
||||
if (acl == NULL)
|
||||
goto out;
|
||||
@ -3782,17 +3782,15 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
|
||||
.rpc_argp = &args,
|
||||
.rpc_resp = &res,
|
||||
};
|
||||
int ret = -ENOMEM, npages, i;
|
||||
size_t acl_len = 0;
|
||||
unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
|
||||
int ret = -ENOMEM, i;
|
||||
|
||||
npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
/* As long as we're doing a round trip to the server anyway,
|
||||
* let's be prepared for a page of acl data. */
|
||||
if (npages == 0)
|
||||
npages = 1;
|
||||
|
||||
/* Add an extra page to handle the bitmap returned */
|
||||
npages++;
|
||||
if (npages > ARRAY_SIZE(pages))
|
||||
return -ERANGE;
|
||||
|
||||
for (i = 0; i < npages; i++) {
|
||||
pages[i] = alloc_page(GFP_KERNEL);
|
||||
@ -3808,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
|
||||
args.acl_len = npages * PAGE_SIZE;
|
||||
args.acl_pgbase = 0;
|
||||
|
||||
/* Let decode_getfacl know not to fail if the ACL data is larger than
|
||||
* the page we send as a guess */
|
||||
if (buf == NULL)
|
||||
res.acl_flags |= NFS4_ACL_LEN_REQUEST;
|
||||
|
||||
dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n",
|
||||
__func__, buf, buflen, npages, args.acl_len);
|
||||
ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
|
||||
@ -3820,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
acl_len = res.acl_len;
|
||||
if (acl_len > args.acl_len)
|
||||
nfs4_write_cached_acl(inode, NULL, 0, acl_len);
|
||||
else
|
||||
nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
|
||||
acl_len);
|
||||
if (buf) {
|
||||
/* Handle the case where the passed-in buffer is too short */
|
||||
if (res.acl_flags & NFS4_ACL_TRUNC) {
|
||||
/* Did the user only issue a request for the acl length? */
|
||||
if (buf == NULL)
|
||||
goto out_ok;
|
||||
ret = -ERANGE;
|
||||
if (acl_len > buflen)
|
||||
goto out_free;
|
||||
_copy_from_pages(buf, pages, res.acl_data_offset,
|
||||
acl_len);
|
||||
goto out_free;
|
||||
}
|
||||
ret = acl_len;
|
||||
nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
|
||||
if (buf)
|
||||
_copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len);
|
||||
out_ok:
|
||||
ret = res.acl_len;
|
||||
out_free:
|
||||
for (i = 0; i < npages; i++)
|
||||
if (pages[i])
|
||||
@ -3891,10 +3883,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
|
||||
.rpc_argp = &arg,
|
||||
.rpc_resp = &res,
|
||||
};
|
||||
unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
|
||||
int ret, i;
|
||||
|
||||
if (!nfs4_server_supports_acls(server))
|
||||
return -EOPNOTSUPP;
|
||||
if (npages > ARRAY_SIZE(pages))
|
||||
return -ERANGE;
|
||||
i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
|
||||
if (i < 0)
|
||||
return i;
|
||||
|
@ -5072,18 +5072,14 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
* are stored with the acl data to handle the problem of
|
||||
* variable length bitmaps.*/
|
||||
res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
|
||||
|
||||
/* We ignore &savep and don't do consistency checks on
|
||||
* the attr length. Let userspace figure it out.... */
|
||||
res->acl_len = attrlen;
|
||||
if (attrlen > (xdr->nwords << 2)) {
|
||||
if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
|
||||
/* getxattr interface called with a NULL buf */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check for receive buffer overflow */
|
||||
if (res->acl_len > (xdr->nwords << 2) ||
|
||||
res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
|
||||
res->acl_flags |= NFS4_ACL_TRUNC;
|
||||
dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
|
||||
attrlen, xdr->nwords << 2);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else
|
||||
status = -EOPNOTSUPP;
|
||||
@ -6229,7 +6225,8 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
status = decode_open(xdr, res);
|
||||
if (status)
|
||||
goto out;
|
||||
if (decode_getfh(xdr, &res->fh) != 0)
|
||||
status = decode_getfh(xdr, &res->fh);
|
||||
if (status)
|
||||
goto out;
|
||||
decode_getfattr(xdr, res->f_attr, res->server);
|
||||
out:
|
||||
|
@ -1867,6 +1867,7 @@ static int nfs23_validate_mount_data(void *options,
|
||||
|
||||
memcpy(sap, &data->addr, sizeof(data->addr));
|
||||
args->nfs_server.addrlen = sizeof(data->addr);
|
||||
args->nfs_server.port = ntohs(data->addr.sin_port);
|
||||
if (!nfs_verify_server_address(sap))
|
||||
goto out_no_address;
|
||||
|
||||
@ -2564,6 +2565,7 @@ static int nfs4_validate_mount_data(void *options,
|
||||
return -EFAULT;
|
||||
if (!nfs_verify_server_address(sap))
|
||||
goto out_no_address;
|
||||
args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
|
||||
|
||||
if (data->auth_flavourlen) {
|
||||
if (data->auth_flavourlen > 1)
|
||||
|
@ -265,11 +265,6 @@ static inline const struct nfs_rpc_ops *NFS_PROTO(const struct inode *inode)
|
||||
return NFS_SERVER(inode)->nfs_client->rpc_ops;
|
||||
}
|
||||
|
||||
static inline __be32 *NFS_COOKIEVERF(const struct inode *inode)
|
||||
{
|
||||
return NFS_I(inode)->cookieverf;
|
||||
}
|
||||
|
||||
static inline unsigned NFS_MINATTRTIMEO(const struct inode *inode)
|
||||
{
|
||||
struct nfs_server *nfss = NFS_SERVER(inode);
|
||||
|
@ -652,7 +652,7 @@ struct nfs_getaclargs {
|
||||
};
|
||||
|
||||
/* getxattr ACL interface flags */
|
||||
#define NFS4_ACL_LEN_REQUEST 0x0001 /* zero length getxattr buffer */
|
||||
#define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */
|
||||
struct nfs_getaclres {
|
||||
size_t acl_len;
|
||||
size_t acl_data_offset;
|
||||
|
@ -114,6 +114,7 @@ struct rpc_xprt_ops {
|
||||
void (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize);
|
||||
int (*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
void (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
void (*rpcbind)(struct rpc_task *task);
|
||||
void (*set_port)(struct rpc_xprt *xprt, unsigned short port);
|
||||
void (*connect)(struct rpc_task *task);
|
||||
@ -281,6 +282,8 @@ void xprt_connect(struct rpc_task *task);
|
||||
void xprt_reserve(struct rpc_task *task);
|
||||
int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
int xprt_prepare_transmit(struct rpc_task *task);
|
||||
void xprt_transmit(struct rpc_task *task);
|
||||
void xprt_end_transmit(struct rpc_task *task);
|
||||
|
@ -969,11 +969,11 @@ static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void xprt_alloc_slot(struct rpc_task *task)
|
||||
void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
|
||||
{
|
||||
struct rpc_xprt *xprt = task->tk_xprt;
|
||||
struct rpc_rqst *req;
|
||||
|
||||
spin_lock(&xprt->reserve_lock);
|
||||
if (!list_empty(&xprt->free)) {
|
||||
req = list_entry(xprt->free.next, struct rpc_rqst, rq_list);
|
||||
list_del(&req->rq_list);
|
||||
@ -994,12 +994,29 @@ static void xprt_alloc_slot(struct rpc_task *task)
|
||||
default:
|
||||
task->tk_status = -EAGAIN;
|
||||
}
|
||||
spin_unlock(&xprt->reserve_lock);
|
||||
return;
|
||||
out_init_req:
|
||||
task->tk_status = 0;
|
||||
task->tk_rqstp = req;
|
||||
xprt_request_init(task, xprt);
|
||||
spin_unlock(&xprt->reserve_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xprt_alloc_slot);
|
||||
|
||||
void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
|
||||
{
|
||||
/* Note: grabbing the xprt_lock_write() ensures that we throttle
|
||||
* new slot allocation if the transport is congested (i.e. when
|
||||
* reconnecting a stream transport or when out of socket write
|
||||
* buffer space).
|
||||
*/
|
||||
if (xprt_lock_write(xprt, task)) {
|
||||
xprt_alloc_slot(xprt, task);
|
||||
xprt_release_write(xprt, task);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xprt_lock_and_alloc_slot);
|
||||
|
||||
static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
|
||||
{
|
||||
@ -1083,20 +1100,9 @@ void xprt_reserve(struct rpc_task *task)
|
||||
if (task->tk_rqstp != NULL)
|
||||
return;
|
||||
|
||||
/* Note: grabbing the xprt_lock_write() here is not strictly needed,
|
||||
* but ensures that we throttle new slot allocation if the transport
|
||||
* is congested (e.g. if reconnecting or if we're out of socket
|
||||
* write buffer space).
|
||||
*/
|
||||
task->tk_timeout = 0;
|
||||
task->tk_status = -EAGAIN;
|
||||
if (!xprt_lock_write(xprt, task))
|
||||
return;
|
||||
|
||||
spin_lock(&xprt->reserve_lock);
|
||||
xprt_alloc_slot(task);
|
||||
spin_unlock(&xprt->reserve_lock);
|
||||
xprt_release_write(xprt, task);
|
||||
xprt->ops->alloc_slot(xprt, task);
|
||||
}
|
||||
|
||||
static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
|
||||
|
@ -713,6 +713,7 @@ static void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
|
||||
static struct rpc_xprt_ops xprt_rdma_procs = {
|
||||
.reserve_xprt = xprt_rdma_reserve_xprt,
|
||||
.release_xprt = xprt_release_xprt_cong, /* sunrpc/xprt.c */
|
||||
.alloc_slot = xprt_alloc_slot,
|
||||
.release_request = xprt_release_rqst_cong, /* ditto */
|
||||
.set_retrans_timeout = xprt_set_retrans_timeout_def, /* ditto */
|
||||
.rpcbind = rpcb_getport_async, /* sunrpc/rpcb_clnt.c */
|
||||
|
@ -2473,6 +2473,7 @@ static void bc_destroy(struct rpc_xprt *xprt)
|
||||
static struct rpc_xprt_ops xs_local_ops = {
|
||||
.reserve_xprt = xprt_reserve_xprt,
|
||||
.release_xprt = xs_tcp_release_xprt,
|
||||
.alloc_slot = xprt_alloc_slot,
|
||||
.rpcbind = xs_local_rpcbind,
|
||||
.set_port = xs_local_set_port,
|
||||
.connect = xs_connect,
|
||||
@ -2489,6 +2490,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
|
||||
.set_buffer_size = xs_udp_set_buffer_size,
|
||||
.reserve_xprt = xprt_reserve_xprt_cong,
|
||||
.release_xprt = xprt_release_xprt_cong,
|
||||
.alloc_slot = xprt_alloc_slot,
|
||||
.rpcbind = rpcb_getport_async,
|
||||
.set_port = xs_set_port,
|
||||
.connect = xs_connect,
|
||||
@ -2506,6 +2508,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
|
||||
static struct rpc_xprt_ops xs_tcp_ops = {
|
||||
.reserve_xprt = xprt_reserve_xprt,
|
||||
.release_xprt = xs_tcp_release_xprt,
|
||||
.alloc_slot = xprt_lock_and_alloc_slot,
|
||||
.rpcbind = rpcb_getport_async,
|
||||
.set_port = xs_set_port,
|
||||
.connect = xs_connect,
|
||||
|
Loading…
Reference in New Issue
Block a user