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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQIcBAABAgAGBQJSh95hAAoJEGcL54qWCgDy1wgP/1zc4C7sMBQFWpIo676MHT4n m5v4bWgYhRBC0dne5GG8dC4+Q2cPkua4H7cWHCJKQmMuDmbzgOB33RVyQdwU/YNp ItLIZLz2EySCKo8OOKvbf4l5jDFeoBYEbheB2bmcE42BgixaTbiHKXpgCtoHr5pT qOX0JI29QtstAY3heiLW52bA3OqNJGwfE595KKEHXZwcD0n8izjqOU7Vrqj0E8/Q S+Xw9a613fo7chzbdcugR+iW6kkr7qtjxXiI5OXvplGyHycbBJRfvAqHkg01Z69k At9Y43cTEFiEx/zfKflmiFkn+IF9xFhABYNCKvpTtLFvQkwJDfYHa6h2jrFac/87 mTRZHIzJ0nghhE1VxOEjA2zvIE3Hd5Xk4By+2BKJaB/Tp0RPbSsHs7t0s8t7RdHi ZwP/bNDynZY3S+HlbMor3A3900bUXLQBpCpRt/0+Hvc5bGLRszA5/Jinv+EqwOT9 LHXTE/CsQGJCOz72SjDZT4Gsa0t11UKdRpznk4XCEvH9tflK78nS32XUktZEC9u/ bCycLbvX+LrquxjQ9WN2TCmwnwyEiv45tSK2b8gf8JS1zJmePDKdnQ1dpHbiZAIO uhEhAqDwAY64+T2+AGncITh8ZfthZhU6wkfGoepqYvC1/5AaeSWrFidDvE1NJUGh xjcsGH6Ym8NnnT3rt/qp =uOIM -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
673fdfe3f0
5 changed files with 48 additions and 24 deletions
|
@ -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 @@ again:
|
||||||
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,
|
||||||
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);
|
||||||
/*
|
/*
|
||||||
* 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)
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
* @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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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);
|
||||||
|
|
Loading…
Reference in a new issue