Commit 673fdfe3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-3.13-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes:
 - Stable fix for data corruption when retransmitting O_DIRECT writes
 - Stable fix for a deep recursion/stack overflow bug in rpc_release_client
 - Stable fix for infinite looping when mounting a NFSv4.x volume
 - Fix a typo in the nfs mount option parser
 - Allow pNFS layouts to be compiled into the kernel when NFSv4.1 is

* tag 'nfs-for-3.13-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  nfs: fix pnfs Kconfig defaults
  NFS: correctly report misuse of "migration" mount option.
  nfs: don't retry detect_trunking with RPC_AUTH_UNIX more than once
  SUNRPC: Avoid deep recursion in rpc_release_client
  SUNRPC: Fix a data corruption issue when retransmitting RPC calls
parents 73d75ba9 8c2fabc6
...@@ -116,17 +116,17 @@ config NFS_V4_2 ...@@ -116,17 +116,17 @@ config NFS_V4_2
config PNFS_FILE_LAYOUT config PNFS_FILE_LAYOUT
tristate tristate
depends on NFS_V4_1 depends on NFS_V4_1
default m default NFS_V4
config PNFS_BLOCK config PNFS_BLOCK
tristate tristate
depends on NFS_V4_1 && BLK_DEV_DM depends on NFS_V4_1 && BLK_DEV_DM
default m default NFS_V4
config PNFS_OBJLAYOUT config PNFS_OBJLAYOUT
tristate tristate
depends on NFS_V4_1 && SCSI_OSD_ULD depends on NFS_V4_1 && SCSI_OSD_ULD
default m default NFS_V4
config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
string "NFSv4.1 Implementation ID Domain" string "NFSv4.1 Implementation ID Domain"
......
...@@ -2093,10 +2093,15 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, ...@@ -2093,10 +2093,15 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
nfs4_root_machine_cred(clp); nfs4_root_machine_cred(clp);
goto again; goto again;
} }
if (i > 2) if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX)
break; break;
case -NFS4ERR_CLID_INUSE: case -NFS4ERR_CLID_INUSE:
case -NFS4ERR_WRONGSEC: case -NFS4ERR_WRONGSEC:
/* No point in retrying if we already used RPC_AUTH_UNIX */
if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX) {
status = -EPERM;
break;
}
clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX); clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
if (IS_ERR(clnt)) { if (IS_ERR(clnt)) {
status = PTR_ERR(clnt); status = PTR_ERR(clnt);
......
...@@ -1614,7 +1614,7 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1614,7 +1614,7 @@ static int nfs_parse_mount_options(char *raw,
goto out_minorversion_mismatch; goto out_minorversion_mismatch;
if (mnt->options & NFS_OPTION_MIGRATION && if (mnt->options & NFS_OPTION_MIGRATION &&
mnt->version != 4 && mnt->minorversion != 0) (mnt->version != 4 || mnt->minorversion != 0))
goto out_migration_misuse; goto out_migration_misuse;
/* /*
......
...@@ -750,14 +750,16 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client); ...@@ -750,14 +750,16 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
/* /*
* Free an RPC client * Free an RPC client
*/ */
static void static struct rpc_clnt *
rpc_free_client(struct rpc_clnt *clnt) rpc_free_client(struct rpc_clnt *clnt)
{ {
struct rpc_clnt *parent = NULL;
dprintk_rcu("RPC: destroying %s client for %s\n", dprintk_rcu("RPC: destroying %s client for %s\n",
clnt->cl_program->name, clnt->cl_program->name,
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
if (clnt->cl_parent != clnt) if (clnt->cl_parent != clnt)
rpc_release_client(clnt->cl_parent); parent = clnt->cl_parent;
rpc_clnt_remove_pipedir(clnt); rpc_clnt_remove_pipedir(clnt);
rpc_unregister_client(clnt); rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
...@@ -766,18 +768,17 @@ rpc_free_client(struct rpc_clnt *clnt) ...@@ -766,18 +768,17 @@ rpc_free_client(struct rpc_clnt *clnt)
rpciod_down(); rpciod_down();
rpc_free_clid(clnt); rpc_free_clid(clnt);
kfree(clnt); kfree(clnt);
return parent;
} }
/* /*
* Free an RPC client * Free an RPC client
*/ */
static void static struct rpc_clnt *
rpc_free_auth(struct rpc_clnt *clnt) rpc_free_auth(struct rpc_clnt *clnt)
{ {
if (clnt->cl_auth == NULL) { if (clnt->cl_auth == NULL)
rpc_free_client(clnt); return rpc_free_client(clnt);
return;
}
/* /*
* Note: RPCSEC_GSS may need to send NULL RPC calls in order to * Note: RPCSEC_GSS may need to send NULL RPC calls in order to
...@@ -788,7 +789,8 @@ rpc_free_auth(struct rpc_clnt *clnt) ...@@ -788,7 +789,8 @@ rpc_free_auth(struct rpc_clnt *clnt)
rpcauth_release(clnt->cl_auth); rpcauth_release(clnt->cl_auth);
clnt->cl_auth = NULL; clnt->cl_auth = NULL;
if (atomic_dec_and_test(&clnt->cl_count)) if (atomic_dec_and_test(&clnt->cl_count))
rpc_free_client(clnt); return rpc_free_client(clnt);
return NULL;
} }
/* /*
...@@ -799,10 +801,13 @@ rpc_release_client(struct rpc_clnt *clnt) ...@@ -799,10 +801,13 @@ rpc_release_client(struct rpc_clnt *clnt)
{ {
dprintk("RPC: rpc_release_client(%p)\n", clnt); dprintk("RPC: rpc_release_client(%p)\n", clnt);
if (list_empty(&clnt->cl_tasks)) do {
wake_up(&destroy_wait); if (list_empty(&clnt->cl_tasks))
if (atomic_dec_and_test(&clnt->cl_count)) wake_up(&destroy_wait);
rpc_free_auth(clnt); if (!atomic_dec_and_test(&clnt->cl_count))
break;
clnt = rpc_free_auth(clnt);
} while (clnt != NULL);
} }
EXPORT_SYMBOL_GPL(rpc_release_client); EXPORT_SYMBOL_GPL(rpc_release_client);
......
...@@ -393,8 +393,10 @@ static int xs_send_kvec(struct socket *sock, struct sockaddr *addr, int addrlen, ...@@ -393,8 +393,10 @@ static int xs_send_kvec(struct socket *sock, struct sockaddr *addr, int addrlen,
return kernel_sendmsg(sock, &msg, NULL, 0, 0); return kernel_sendmsg(sock, &msg, NULL, 0, 0);
} }
static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more) static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more, bool zerocopy)
{ {
ssize_t (*do_sendpage)(struct socket *sock, struct page *page,
int offset, size_t size, int flags);
struct page **ppage; struct page **ppage;
unsigned int remainder; unsigned int remainder;
int err, sent = 0; int err, sent = 0;
...@@ -403,6 +405,9 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i ...@@ -403,6 +405,9 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
base += xdr->page_base; base += xdr->page_base;
ppage = xdr->pages + (base >> PAGE_SHIFT); ppage = xdr->pages + (base >> PAGE_SHIFT);
base &= ~PAGE_MASK; base &= ~PAGE_MASK;
do_sendpage = sock->ops->sendpage;
if (!zerocopy)
do_sendpage = sock_no_sendpage;
for(;;) { for(;;) {
unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder); unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder);
int flags = XS_SENDMSG_FLAGS; int flags = XS_SENDMSG_FLAGS;
...@@ -410,7 +415,7 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i ...@@ -410,7 +415,7 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
remainder -= len; remainder -= len;
if (remainder != 0 || more) if (remainder != 0 || more)
flags |= MSG_MORE; flags |= MSG_MORE;
err = sock->ops->sendpage(sock, *ppage, base, len, flags); err = do_sendpage(sock, *ppage, base, len, flags);
if (remainder == 0 || err != len) if (remainder == 0 || err != len)
break; break;
sent += err; sent += err;
...@@ -431,9 +436,10 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i ...@@ -431,9 +436,10 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
* @addrlen: UDP only -- length of destination address * @addrlen: UDP only -- length of destination address
* @xdr: buffer containing this request * @xdr: buffer containing this request
* @base: starting position in the buffer * @base: starting position in the buffer
* @zerocopy: true if it is safe to use sendpage()
* *
*/ */
static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base) static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base, bool zerocopy)
{ {
unsigned int remainder = xdr->len - base; unsigned int remainder = xdr->len - base;
int err, sent = 0; int err, sent = 0;
...@@ -461,7 +467,7 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, ...@@ -461,7 +467,7 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
if (base < xdr->page_len) { if (base < xdr->page_len) {
unsigned int len = xdr->page_len - base; unsigned int len = xdr->page_len - base;
remainder -= len; remainder -= len;
err = xs_send_pagedata(sock, xdr, base, remainder != 0); err = xs_send_pagedata(sock, xdr, base, remainder != 0, zerocopy);
if (remainder == 0 || err != len) if (remainder == 0 || err != len)
goto out; goto out;
sent += err; sent += err;
...@@ -564,7 +570,7 @@ static int xs_local_send_request(struct rpc_task *task) ...@@ -564,7 +570,7 @@ static int xs_local_send_request(struct rpc_task *task)
req->rq_svec->iov_base, req->rq_svec->iov_len); req->rq_svec->iov_base, req->rq_svec->iov_len);
status = xs_sendpages(transport->sock, NULL, 0, status = xs_sendpages(transport->sock, NULL, 0,
xdr, req->rq_bytes_sent); xdr, req->rq_bytes_sent, true);
dprintk("RPC: %s(%u) = %d\n", dprintk("RPC: %s(%u) = %d\n",
__func__, xdr->len - req->rq_bytes_sent, status); __func__, xdr->len - req->rq_bytes_sent, status);
if (likely(status >= 0)) { if (likely(status >= 0)) {
...@@ -620,7 +626,7 @@ static int xs_udp_send_request(struct rpc_task *task) ...@@ -620,7 +626,7 @@ static int xs_udp_send_request(struct rpc_task *task)
status = xs_sendpages(transport->sock, status = xs_sendpages(transport->sock,
xs_addr(xprt), xs_addr(xprt),
xprt->addrlen, xdr, xprt->addrlen, xdr,
req->rq_bytes_sent); req->rq_bytes_sent, true);
dprintk("RPC: xs_udp_send_request(%u) = %d\n", dprintk("RPC: xs_udp_send_request(%u) = %d\n",
xdr->len - req->rq_bytes_sent, status); xdr->len - req->rq_bytes_sent, status);
...@@ -693,6 +699,7 @@ static int xs_tcp_send_request(struct rpc_task *task) ...@@ -693,6 +699,7 @@ static int xs_tcp_send_request(struct rpc_task *task)
struct rpc_xprt *xprt = req->rq_xprt; struct rpc_xprt *xprt = req->rq_xprt;
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct xdr_buf *xdr = &req->rq_snd_buf; struct xdr_buf *xdr = &req->rq_snd_buf;
bool zerocopy = true;
int status; int status;
xs_encode_stream_record_marker(&req->rq_snd_buf); xs_encode_stream_record_marker(&req->rq_snd_buf);
...@@ -700,13 +707,20 @@ static int xs_tcp_send_request(struct rpc_task *task) ...@@ -700,13 +707,20 @@ static int xs_tcp_send_request(struct rpc_task *task)
xs_pktdump("packet data:", xs_pktdump("packet data:",
req->rq_svec->iov_base, req->rq_svec->iov_base,
req->rq_svec->iov_len); req->rq_svec->iov_len);
/* Don't use zero copy if this is a resend. If the RPC call
* completes while the socket holds a reference to the pages,
* then we may end up resending corrupted data.
*/
if (task->tk_flags & RPC_TASK_SENT)
zerocopy = false;
/* Continue transmitting the packet/record. We must be careful /* Continue transmitting the packet/record. We must be careful
* to cope with writespace callbacks arriving _after_ we have * to cope with writespace callbacks arriving _after_ we have
* called sendmsg(). */ * called sendmsg(). */
while (1) { while (1) {
status = xs_sendpages(transport->sock, status = xs_sendpages(transport->sock,
NULL, 0, xdr, req->rq_bytes_sent); NULL, 0, xdr, req->rq_bytes_sent,
zerocopy);
dprintk("RPC: xs_tcp_send_request(%u) = %d\n", dprintk("RPC: xs_tcp_send_request(%u) = %d\n",
xdr->len - req->rq_bytes_sent, status); xdr->len - req->rq_bytes_sent, status);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment