Commit 6bc986ab authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-6.7-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client updates from Trond Myklebust:
 "Bugfixes:

   - SUNRPC:
       - re-probe the target RPC port after an ECONNRESET error
       - handle allocation errors from rpcb_call_async()
       - fix a use-after-free condition in rpc_pipefs
       - fix up various checks for timeouts

   - NFSv4.1:
       - Handle NFS4ERR_DELAY errors during session trunking
       - fix SP4_MACH_CRED protection for pnfs IO

   - NFSv4:
       - Ensure that we test all delegations when the server notifies
         us that it may have revoked some of them

  Features:

   - Allow knfsd processes to break out of NFS4ERR_DELAY loops when
     re-exporting NFSv4.x by setting appropriate values for the
     'delay_retrans' module parameter

   - nfs: Convert nfs_symlink() to use a folio"

* tag 'nfs-for-6.7-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  nfs: Convert nfs_symlink() to use a folio
  SUNRPC: Fix RPC client cleaned up the freed pipefs dentries
  NFSv4.1: fix SP4_MACH_CRED protection for pnfs IO
  SUNRPC: Add an IS_ERR() check back to where it was
  NFSv4.1: fix handling NFS4ERR_DELAY when testing for session trunking
  nfs41: drop dependency between flexfiles layout driver and NFSv3 modules
  NFSv4: fairly test all delegations on a SEQ4_ revocation
  SUNRPC: SOFTCONN tasks should time out when on the sending list
  SUNRPC: Force close the socket when a hard error is reported
  SUNRPC: Don't skip timeout checks in call_connect_status()
  SUNRPC: ECONNRESET might require a rebind
  NFSv4/pnfs: Allow layoutget to return EAGAIN for softerr mounts
  NFSv4: Add a parameter to limit the number of retries after NFS4ERR_DELAY
parents 67c0afb6 f003a717
...@@ -3596,6 +3596,13 @@ ...@@ -3596,6 +3596,13 @@
[NFS] set the TCP port on which the NFSv4 callback [NFS] set the TCP port on which the NFSv4 callback
channel should listen. channel should listen.
nfs.delay_retrans=
[NFS] specifies the number of times the NFSv4 client
retries the request before returning an EAGAIN error,
after a reply of NFS4ERR_DELAY from the server.
Only applies if the softerr mount option is enabled,
and the specified value is >= 0.
nfs.enable_ino64= nfs.enable_ino64=
[NFS] enable 64-bit inode numbers. [NFS] enable 64-bit inode numbers.
If zero, the NFS client will fake up a 32-bit inode If zero, the NFS client will fake up a 32-bit inode
......
...@@ -125,7 +125,7 @@ config PNFS_BLOCK ...@@ -125,7 +125,7 @@ config PNFS_BLOCK
config PNFS_FLEXFILE_LAYOUT config PNFS_FLEXFILE_LAYOUT
tristate tristate
depends on NFS_V4_1 && NFS_V3 depends on NFS_V4_1
default NFS_V4 default NFS_V4
config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
......
...@@ -448,6 +448,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, ...@@ -448,6 +448,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
delegation->cred = get_cred(cred); delegation->cred = get_cred(cred);
delegation->inode = inode; delegation->inode = inode;
delegation->flags = 1<<NFS_DELEGATION_REFERENCED; delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
delegation->test_gen = 0;
spin_lock_init(&delegation->lock); spin_lock_init(&delegation->lock);
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
...@@ -1294,6 +1295,8 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server, ...@@ -1294,6 +1295,8 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
struct inode *inode; struct inode *inode;
const struct cred *cred; const struct cred *cred;
nfs4_stateid stateid; nfs4_stateid stateid;
unsigned long gen = ++server->delegation_gen;
restart: restart:
rcu_read_lock(); rcu_read_lock();
restart_locked: restart_locked:
...@@ -1303,7 +1306,8 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server, ...@@ -1303,7 +1306,8 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
test_bit(NFS_DELEGATION_RETURNING, test_bit(NFS_DELEGATION_RETURNING,
&delegation->flags) || &delegation->flags) ||
test_bit(NFS_DELEGATION_TEST_EXPIRED, test_bit(NFS_DELEGATION_TEST_EXPIRED,
&delegation->flags) == 0) &delegation->flags) == 0 ||
delegation->test_gen == gen)
continue; continue;
inode = nfs_delegation_grab_inode(delegation); inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL) if (inode == NULL)
...@@ -1312,6 +1316,7 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server, ...@@ -1312,6 +1316,7 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
cred = get_cred_rcu(delegation->cred); cred = get_cred_rcu(delegation->cred);
nfs4_stateid_copy(&stateid, &delegation->stateid); nfs4_stateid_copy(&stateid, &delegation->stateid);
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
delegation->test_gen = gen;
clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
rcu_read_unlock(); rcu_read_unlock();
nfs_delegation_test_free_expired(inode, &stateid, cred); nfs_delegation_test_free_expired(inode, &stateid, cred);
......
...@@ -21,6 +21,7 @@ struct nfs_delegation { ...@@ -21,6 +21,7 @@ struct nfs_delegation {
fmode_t type; fmode_t type;
unsigned long pagemod_limit; unsigned long pagemod_limit;
__u64 change_attr; __u64 change_attr;
unsigned long test_gen;
unsigned long flags; unsigned long flags;
refcount_t refcount; refcount_t refcount;
spinlock_t lock; spinlock_t lock;
......
...@@ -2532,7 +2532,7 @@ EXPORT_SYMBOL_GPL(nfs_unlink); ...@@ -2532,7 +2532,7 @@ EXPORT_SYMBOL_GPL(nfs_unlink);
int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir, int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *symname) struct dentry *dentry, const char *symname)
{ {
struct page *page; struct folio *folio;
char *kaddr; char *kaddr;
struct iattr attr; struct iattr attr;
unsigned int pathlen = strlen(symname); unsigned int pathlen = strlen(symname);
...@@ -2547,24 +2547,24 @@ int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir, ...@@ -2547,24 +2547,24 @@ int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
attr.ia_mode = S_IFLNK | S_IRWXUGO; attr.ia_mode = S_IFLNK | S_IRWXUGO;
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
page = alloc_page(GFP_USER); folio = folio_alloc(GFP_USER, 0);
if (!page) if (!folio)
return -ENOMEM; return -ENOMEM;
kaddr = page_address(page); kaddr = folio_address(folio);
memcpy(kaddr, symname, pathlen); memcpy(kaddr, symname, pathlen);
if (pathlen < PAGE_SIZE) if (pathlen < PAGE_SIZE)
memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen); memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
trace_nfs_symlink_enter(dir, dentry); trace_nfs_symlink_enter(dir, dentry);
error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr); error = NFS_PROTO(dir)->symlink(dir, dentry, folio, pathlen, &attr);
trace_nfs_symlink_exit(dir, dentry, error); trace_nfs_symlink_exit(dir, dentry, error);
if (error != 0) { if (error != 0) {
dfprintk(VFS, "NFS: symlink(%s/%lu, %pd, %s) error %d\n", dfprintk(VFS, "NFS: symlink(%s/%lu, %pd, %s) error %d\n",
dir->i_sb->s_id, dir->i_ino, dir->i_sb->s_id, dir->i_ino,
dentry, symname, error); dentry, symname, error);
d_drop(dentry); d_drop(dentry);
__free_page(page); folio_put(folio);
return error; return error;
} }
...@@ -2574,18 +2574,13 @@ int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir, ...@@ -2574,18 +2574,13 @@ int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
* No big deal if we can't add this page to the page cache here. * No big deal if we can't add this page to the page cache here.
* READLINK will get the missing page from the server if needed. * READLINK will get the missing page from the server if needed.
*/ */
if (!add_to_page_cache_lru(page, d_inode(dentry)->i_mapping, 0, if (filemap_add_folio(d_inode(dentry)->i_mapping, folio, 0,
GFP_KERNEL)) { GFP_KERNEL) == 0) {
SetPageUptodate(page); folio_mark_uptodate(folio);
unlock_page(page); folio_unlock(folio);
/* }
* add_to_page_cache_lru() grabs an extra page refcount.
* Drop it here to avoid leaking this page later.
*/
put_page(page);
} else
__free_page(page);
folio_put(folio);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(nfs_symlink); EXPORT_SYMBOL_GPL(nfs_symlink);
......
...@@ -543,9 +543,10 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name) ...@@ -543,9 +543,10 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name)
} }
static int static int
nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct folio *folio,
unsigned int len, struct iattr *sattr) unsigned int len, struct iattr *sattr)
{ {
struct page *page = &folio->page;
struct nfs3_createdata *data; struct nfs3_createdata *data;
struct dentry *d_alias; struct dentry *d_alias;
int status = -ENOMEM; int status = -ENOMEM;
......
...@@ -209,6 +209,7 @@ struct nfs4_exception { ...@@ -209,6 +209,7 @@ struct nfs4_exception {
struct inode *inode; struct inode *inode;
nfs4_stateid *stateid; nfs4_stateid *stateid;
long timeout; long timeout;
unsigned short retrans;
unsigned char task_is_privileged : 1; unsigned char task_is_privileged : 1;
unsigned char delay : 1, unsigned char delay : 1,
recovering : 1, recovering : 1,
...@@ -546,6 +547,7 @@ extern unsigned short max_session_slots; ...@@ -546,6 +547,7 @@ extern unsigned short max_session_slots;
extern unsigned short max_session_cb_slots; extern unsigned short max_session_cb_slots;
extern unsigned short send_implementation_id; extern unsigned short send_implementation_id;
extern bool recover_lost_locks; extern bool recover_lost_locks;
extern short nfs_delay_retrans;
#define NFS4_CLIENT_ID_UNIQ_LEN (64) #define NFS4_CLIENT_ID_UNIQ_LEN (64)
extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN]; extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN];
......
...@@ -585,6 +585,21 @@ static int nfs4_do_handle_exception(struct nfs_server *server, ...@@ -585,6 +585,21 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
return 0; return 0;
} }
/*
* Track the number of NFS4ERR_DELAY related retransmissions and return
* EAGAIN if the 'softerr' mount option is set, and we've exceeded the limit
* set by 'nfs_delay_retrans'.
*/
static int nfs4_exception_should_retrans(const struct nfs_server *server,
struct nfs4_exception *exception)
{
if (server->flags & NFS_MOUNT_SOFTERR && nfs_delay_retrans >= 0) {
if (exception->retrans++ >= (unsigned short)nfs_delay_retrans)
return -EAGAIN;
}
return 0;
}
/* This is the error handling routine for processes that are allowed /* This is the error handling routine for processes that are allowed
* to sleep. * to sleep.
*/ */
...@@ -595,6 +610,11 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_ ...@@ -595,6 +610,11 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
ret = nfs4_do_handle_exception(server, errorcode, exception); ret = nfs4_do_handle_exception(server, errorcode, exception);
if (exception->delay) { if (exception->delay) {
int ret2 = nfs4_exception_should_retrans(server, exception);
if (ret2 < 0) {
exception->retry = 0;
return ret2;
}
ret = nfs4_delay(&exception->timeout, ret = nfs4_delay(&exception->timeout,
exception->interruptible); exception->interruptible);
goto out_retry; goto out_retry;
...@@ -623,6 +643,11 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server, ...@@ -623,6 +643,11 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server,
ret = nfs4_do_handle_exception(server, errorcode, exception); ret = nfs4_do_handle_exception(server, errorcode, exception);
if (exception->delay) { if (exception->delay) {
int ret2 = nfs4_exception_should_retrans(server, exception);
if (ret2 < 0) {
exception->retry = 0;
return ret2;
}
rpc_delay(task, nfs4_update_delay(&exception->timeout)); rpc_delay(task, nfs4_update_delay(&exception->timeout));
goto out_retry; goto out_retry;
} }
...@@ -5011,9 +5036,10 @@ static void nfs4_free_createdata(struct nfs4_createdata *data) ...@@ -5011,9 +5036,10 @@ static void nfs4_free_createdata(struct nfs4_createdata *data)
} }
static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
struct page *page, unsigned int len, struct iattr *sattr, struct folio *folio, unsigned int len, struct iattr *sattr,
struct nfs4_label *label) struct nfs4_label *label)
{ {
struct page *page = &folio->page;
struct nfs4_createdata *data; struct nfs4_createdata *data;
int status = -ENAMETOOLONG; int status = -ENAMETOOLONG;
...@@ -5038,7 +5064,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, ...@@ -5038,7 +5064,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
} }
static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
struct page *page, unsigned int len, struct iattr *sattr) struct folio *folio, unsigned int len, struct iattr *sattr)
{ {
struct nfs4_exception exception = { struct nfs4_exception exception = {
.interruptible = true, .interruptible = true,
...@@ -5049,7 +5075,7 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, ...@@ -5049,7 +5075,7 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
label = nfs4_label_init_security(dir, dentry, sattr, &l); label = nfs4_label_init_security(dir, dentry, sattr, &l);
do { do {
err = _nfs4_proc_symlink(dir, dentry, page, len, sattr, label); err = _nfs4_proc_symlink(dir, dentry, folio, len, sattr, label);
trace_nfs4_symlink(dir, &dentry->d_name, err); trace_nfs4_symlink(dir, &dentry->d_name, err);
err = nfs4_handle_exception(NFS_SERVER(dir), err, err = nfs4_handle_exception(NFS_SERVER(dir), err,
&exception); &exception);
...@@ -5622,7 +5648,7 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr, ...@@ -5622,7 +5648,7 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE]; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0); nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0);
nfs4_state_protect_write(server->nfs_client, clnt, msg, hdr); nfs4_state_protect_write(hdr->ds_clp ? hdr->ds_clp : server->nfs_client, clnt, msg, hdr);
} }
static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
...@@ -5663,7 +5689,8 @@ static void nfs4_proc_commit_setup(struct nfs_commit_data *data, struct rpc_mess ...@@ -5663,7 +5689,8 @@ static void nfs4_proc_commit_setup(struct nfs_commit_data *data, struct rpc_mess
data->res.server = server; data->res.server = server;
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT]; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0); nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0);
nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_COMMIT, clnt, msg); nfs4_state_protect(data->ds_clp ? data->ds_clp : server->nfs_client,
NFS_SP4_MACH_CRED_COMMIT, clnt, msg);
} }
static int _nfs4_proc_commit(struct file *dst, struct nfs_commitargs *args, static int _nfs4_proc_commit(struct file *dst, struct nfs_commitargs *args,
...@@ -8934,6 +8961,7 @@ void nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt, ...@@ -8934,6 +8961,7 @@ void nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
sp4_how = (adata->clp->cl_sp4_flags == 0 ? SP4_NONE : SP4_MACH_CRED); sp4_how = (adata->clp->cl_sp4_flags == 0 ? SP4_NONE : SP4_MACH_CRED);
try_again:
/* Test connection for session trunking. Async exchange_id call */ /* Test connection for session trunking. Async exchange_id call */
task = nfs4_run_exchange_id(adata->clp, adata->cred, sp4_how, xprt); task = nfs4_run_exchange_id(adata->clp, adata->cred, sp4_how, xprt);
if (IS_ERR(task)) if (IS_ERR(task))
...@@ -8946,11 +8974,15 @@ void nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt, ...@@ -8946,11 +8974,15 @@ void nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
if (status == 0) if (status == 0)
rpc_clnt_xprt_switch_add_xprt(clnt, xprt); rpc_clnt_xprt_switch_add_xprt(clnt, xprt);
else if (rpc_clnt_xprt_switch_has_addr(clnt, else if (status != -NFS4ERR_DELAY && rpc_clnt_xprt_switch_has_addr(clnt,
(struct sockaddr *)&xprt->addr)) (struct sockaddr *)&xprt->addr))
rpc_clnt_xprt_switch_remove_xprt(clnt, xprt); rpc_clnt_xprt_switch_remove_xprt(clnt, xprt);
rpc_put_task(task); rpc_put_task(task);
if (status == -NFS4ERR_DELAY) {
ssleep(1);
goto try_again;
}
} }
EXPORT_SYMBOL_GPL(nfs4_test_session_trunk); EXPORT_SYMBOL_GPL(nfs4_test_session_trunk);
...@@ -9621,6 +9653,9 @@ nfs4_layoutget_handle_exception(struct rpc_task *task, ...@@ -9621,6 +9653,9 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
nfs4_sequence_free_slot(&lgp->res.seq_res); nfs4_sequence_free_slot(&lgp->res.seq_res);
exception->state = NULL;
exception->stateid = NULL;
switch (nfs4err) { switch (nfs4err) {
case 0: case 0:
goto out; goto out;
...@@ -9716,7 +9751,8 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = { ...@@ -9716,7 +9751,8 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = {
}; };
struct pnfs_layout_segment * struct pnfs_layout_segment *
nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout) nfs4_proc_layoutget(struct nfs4_layoutget *lgp,
struct nfs4_exception *exception)
{ {
struct inode *inode = lgp->args.inode; struct inode *inode = lgp->args.inode;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
...@@ -9736,13 +9772,10 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout) ...@@ -9736,13 +9772,10 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
RPC_TASK_MOVEABLE, RPC_TASK_MOVEABLE,
}; };
struct pnfs_layout_segment *lseg = NULL; struct pnfs_layout_segment *lseg = NULL;
struct nfs4_exception exception = {
.inode = inode,
.timeout = *timeout,
};
int status = 0; int status = 0;
nfs4_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0, 0); nfs4_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0, 0);
exception->retry = 0;
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task))
...@@ -9753,11 +9786,12 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout) ...@@ -9753,11 +9786,12 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
goto out; goto out;
if (task->tk_status < 0) { if (task->tk_status < 0) {
status = nfs4_layoutget_handle_exception(task, lgp, &exception); exception->retry = 1;
*timeout = exception.timeout; status = nfs4_layoutget_handle_exception(task, lgp, exception);
} else if (lgp->res.layoutp->len == 0) { } else if (lgp->res.layoutp->len == 0) {
exception->retry = 1;
status = -EAGAIN; status = -EAGAIN;
*timeout = nfs4_update_delay(&exception.timeout); nfs4_update_delay(&exception->timeout);
} else } else
lseg = pnfs_layout_process(lgp); lseg = pnfs_layout_process(lgp);
out: out:
......
...@@ -1980,7 +1980,9 @@ pnfs_update_layout(struct inode *ino, ...@@ -1980,7 +1980,9 @@ pnfs_update_layout(struct inode *ino,
struct pnfs_layout_segment *lseg = NULL; struct pnfs_layout_segment *lseg = NULL;
struct nfs4_layoutget *lgp; struct nfs4_layoutget *lgp;
nfs4_stateid stateid; nfs4_stateid stateid;
long timeout = 0; struct nfs4_exception exception = {
.inode = ino,
};
unsigned long giveup = jiffies + (clp->cl_lease_time << 1); unsigned long giveup = jiffies + (clp->cl_lease_time << 1);
bool first; bool first;
...@@ -2144,7 +2146,7 @@ pnfs_update_layout(struct inode *ino, ...@@ -2144,7 +2146,7 @@ pnfs_update_layout(struct inode *ino,
lgp->lo = lo; lgp->lo = lo;
pnfs_get_layout_hdr(lo); pnfs_get_layout_hdr(lo);
lseg = nfs4_proc_layoutget(lgp, &timeout); lseg = nfs4_proc_layoutget(lgp, &exception);
trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
PNFS_UPDATE_LAYOUT_SEND_LAYOUTGET); PNFS_UPDATE_LAYOUT_SEND_LAYOUTGET);
nfs_layoutget_end(lo); nfs_layoutget_end(lo);
...@@ -2171,6 +2173,8 @@ pnfs_update_layout(struct inode *ino, ...@@ -2171,6 +2173,8 @@ pnfs_update_layout(struct inode *ino,
goto out_put_layout_hdr; goto out_put_layout_hdr;
} }
if (lseg) { if (lseg) {
if (!exception.retry)
goto out_put_layout_hdr;
if (first) if (first)
pnfs_clear_first_layoutget(lo); pnfs_clear_first_layoutget(lo);
trace_pnfs_update_layout(ino, pos, count, trace_pnfs_update_layout(ino, pos, count,
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/nfs_page.h> #include <linux/nfs_page.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
struct nfs4_exception;
struct nfs4_opendata; struct nfs4_opendata;
enum { enum {
...@@ -245,7 +246,9 @@ extern size_t max_response_pages(struct nfs_server *server); ...@@ -245,7 +246,9 @@ extern size_t max_response_pages(struct nfs_server *server);
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,
const struct cred *cred); const struct cred *cred);
extern struct pnfs_layout_segment* nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout); extern struct pnfs_layout_segment *
nfs4_proc_layoutget(struct nfs4_layoutget *lgp,
struct nfs4_exception *exception);
extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, bool sync); extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, bool sync);
/* pnfs.c */ /* pnfs.c */
......
...@@ -396,9 +396,10 @@ nfs_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name) ...@@ -396,9 +396,10 @@ nfs_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name)
} }
static int static int
nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct folio *folio,
unsigned int len, struct iattr *sattr) unsigned int len, struct iattr *sattr)
{ {
struct page *page = &folio->page;
struct nfs_fh *fh; struct nfs_fh *fh;
struct nfs_fattr *fattr; struct nfs_fattr *fattr;
struct nfs_symlinkargs arg = { struct nfs_symlinkargs arg = {
......
...@@ -1371,6 +1371,7 @@ unsigned short max_session_cb_slots = NFS4_DEF_CB_SLOT_TABLE_SIZE; ...@@ -1371,6 +1371,7 @@ unsigned short max_session_cb_slots = NFS4_DEF_CB_SLOT_TABLE_SIZE;
unsigned short send_implementation_id = 1; unsigned short send_implementation_id = 1;
char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = ""; char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = "";
bool recover_lost_locks = false; bool recover_lost_locks = false;
short nfs_delay_retrans = -1;
EXPORT_SYMBOL_GPL(nfs_callback_nr_threads); EXPORT_SYMBOL_GPL(nfs_callback_nr_threads);
EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport); EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport);
...@@ -1381,6 +1382,7 @@ EXPORT_SYMBOL_GPL(max_session_cb_slots); ...@@ -1381,6 +1382,7 @@ EXPORT_SYMBOL_GPL(max_session_cb_slots);
EXPORT_SYMBOL_GPL(send_implementation_id); EXPORT_SYMBOL_GPL(send_implementation_id);
EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier); EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier);
EXPORT_SYMBOL_GPL(recover_lost_locks); EXPORT_SYMBOL_GPL(recover_lost_locks);
EXPORT_SYMBOL_GPL(nfs_delay_retrans);
#define NFS_CALLBACK_MAXPORTNR (65535U) #define NFS_CALLBACK_MAXPORTNR (65535U)
...@@ -1429,5 +1431,9 @@ MODULE_PARM_DESC(recover_lost_locks, ...@@ -1429,5 +1431,9 @@ MODULE_PARM_DESC(recover_lost_locks,
"If the server reports that a lock might be lost, " "If the server reports that a lock might be lost, "
"try to recover it risking data corruption."); "try to recover it risking data corruption.");
module_param_named(delay_retrans, nfs_delay_retrans, short, 0644);
MODULE_PARM_DESC(delay_retrans,
"Unless negative, specifies the number of times the NFSv4 "
"client retries a request before returning an EAGAIN error, "
"after a reply of NFS4ERR_DELAY from the server.");
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
...@@ -739,6 +739,8 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) ...@@ -739,6 +739,8 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
&pgio); &pgio);
pgio.pg_error = 0; pgio.pg_error = 0;
nfs_pageio_complete(&pgio); nfs_pageio_complete(&pgio);
if (err == -EAGAIN && mntflags & NFS_MOUNT_SOFTERR)
break;
} while (err < 0 && !nfs_error_is_fatal(err)); } while (err < 0 && !nfs_error_is_fatal(err));
nfs_io_completion_put(ioc); nfs_io_completion_put(ioc);
......
...@@ -239,6 +239,7 @@ struct nfs_server { ...@@ -239,6 +239,7 @@ struct nfs_server {
struct list_head delegations; struct list_head delegations;
struct list_head ss_copies; struct list_head ss_copies;
unsigned long delegation_gen;
unsigned long mig_gen; unsigned long mig_gen;
unsigned long mig_status; unsigned long mig_status;
#define NFS_MIG_IN_TRANSITION (1) #define NFS_MIG_IN_TRANSITION (1)
......
...@@ -1772,7 +1772,7 @@ struct nfs_rpc_ops { ...@@ -1772,7 +1772,7 @@ struct nfs_rpc_ops {
void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *); void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *);
int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir); int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir);
int (*link) (struct inode *, struct inode *, const struct qstr *); int (*link) (struct inode *, struct inode *, const struct qstr *);
int (*symlink) (struct inode *, struct dentry *, struct page *, int (*symlink) (struct inode *, struct dentry *, struct folio *,
unsigned int, struct iattr *); unsigned int, struct iattr *);
int (*mkdir) (struct inode *, struct dentry *, struct iattr *); int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
int (*rmdir) (struct inode *, const struct qstr *); int (*rmdir) (struct inode *, const struct qstr *);
......
...@@ -92,6 +92,7 @@ struct rpc_clnt { ...@@ -92,6 +92,7 @@ struct rpc_clnt {
}; };
const struct cred *cl_cred; const struct cred *cl_cred;
unsigned int cl_max_connect; /* max number of transports not to the same IP */ unsigned int cl_max_connect; /* max number of transports not to the same IP */
struct super_block *pipefs_sb;
}; };
/* /*
......
...@@ -111,7 +111,8 @@ static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) ...@@ -111,7 +111,8 @@ static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
pipefs_sb = rpc_get_sb_net(net); pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) { if (pipefs_sb) {
__rpc_clnt_remove_pipedir(clnt); if (pipefs_sb == clnt->pipefs_sb)
__rpc_clnt_remove_pipedir(clnt);
rpc_put_sb_net(net); rpc_put_sb_net(net);
} }
} }
...@@ -151,6 +152,8 @@ rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt) ...@@ -151,6 +152,8 @@ rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt)
{ {
struct dentry *dentry; struct dentry *dentry;
clnt->pipefs_sb = pipefs_sb;
if (clnt->cl_program->pipe_dir_name != NULL) { if (clnt->cl_program->pipe_dir_name != NULL) {
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt); dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
...@@ -2171,6 +2174,7 @@ call_connect_status(struct rpc_task *task) ...@@ -2171,6 +2174,7 @@ call_connect_status(struct rpc_task *task)
task->tk_status = 0; task->tk_status = 0;
switch (status) { switch (status) {
case -ECONNREFUSED: case -ECONNREFUSED:
case -ECONNRESET:
/* A positive refusal suggests a rebind is needed. */ /* A positive refusal suggests a rebind is needed. */
if (RPC_IS_SOFTCONN(task)) if (RPC_IS_SOFTCONN(task))
break; break;
...@@ -2179,7 +2183,6 @@ call_connect_status(struct rpc_task *task) ...@@ -2179,7 +2183,6 @@ call_connect_status(struct rpc_task *task)
goto out_retry; goto out_retry;
} }
fallthrough; fallthrough;
case -ECONNRESET:
case -ECONNABORTED: case -ECONNABORTED:
case -ENETDOWN: case -ENETDOWN:
case -ENETUNREACH: case -ENETUNREACH:
...@@ -2220,7 +2223,7 @@ call_connect_status(struct rpc_task *task) ...@@ -2220,7 +2223,7 @@ call_connect_status(struct rpc_task *task)
} }
xprt_switch_put(xps); xprt_switch_put(xps);
if (!task->tk_xprt) if (!task->tk_xprt)
return; goto out;
} }
goto out_retry; goto out_retry;
case -ENOBUFS: case -ENOBUFS:
...@@ -2235,6 +2238,7 @@ call_connect_status(struct rpc_task *task) ...@@ -2235,6 +2238,7 @@ call_connect_status(struct rpc_task *task)
out_retry: out_retry:
/* Check for timeouts before looping back to call_bind */ /* Check for timeouts before looping back to call_bind */
task->tk_action = call_bind; task->tk_action = call_bind;
out:
rpc_check_timeout(task); rpc_check_timeout(task);
} }
......
...@@ -769,6 +769,10 @@ void rpcb_getport_async(struct rpc_task *task) ...@@ -769,6 +769,10 @@ void rpcb_getport_async(struct rpc_task *task)
child = rpcb_call_async(rpcb_clnt, map, proc); child = rpcb_call_async(rpcb_clnt, map, proc);
rpc_release_client(rpcb_clnt); rpc_release_client(rpcb_clnt);
if (IS_ERR(child)) {
/* rpcb_map_release() has freed the arguments */
return;
}
xprt->stat.bind_count++; xprt->stat.bind_count++;
rpc_put_task(child); rpc_put_task(child);
......
...@@ -283,7 +283,7 @@ int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task) ...@@ -283,7 +283,7 @@ int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
xprt_clear_locked(xprt); xprt_clear_locked(xprt);
out_sleep: out_sleep:
task->tk_status = -EAGAIN; task->tk_status = -EAGAIN;
if (RPC_IS_SOFT(task)) if (RPC_IS_SOFT(task) || RPC_IS_SOFTCONN(task))
rpc_sleep_on_timeout(&xprt->sending, task, NULL, rpc_sleep_on_timeout(&xprt->sending, task, NULL,
xprt_request_timeout(req)); xprt_request_timeout(req));
else else
...@@ -349,7 +349,7 @@ int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task) ...@@ -349,7 +349,7 @@ int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
xprt_clear_locked(xprt); xprt_clear_locked(xprt);
out_sleep: out_sleep:
task->tk_status = -EAGAIN; task->tk_status = -EAGAIN;
if (RPC_IS_SOFT(task)) if (RPC_IS_SOFT(task) || RPC_IS_SOFTCONN(task))
rpc_sleep_on_timeout(&xprt->sending, task, NULL, rpc_sleep_on_timeout(&xprt->sending, task, NULL,
xprt_request_timeout(req)); xprt_request_timeout(req));
else else
......
...@@ -1181,6 +1181,7 @@ static void xs_sock_reset_state_flags(struct rpc_xprt *xprt) ...@@ -1181,6 +1181,7 @@ static void xs_sock_reset_state_flags(struct rpc_xprt *xprt)
{ {
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
transport->xprt_err = 0;
clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state); clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state); clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state);
clear_bit(XPRT_SOCK_WAKE_WRITE, &transport->sock_state); clear_bit(XPRT_SOCK_WAKE_WRITE, &transport->sock_state);
...@@ -2772,18 +2773,13 @@ static void xs_wake_error(struct sock_xprt *transport) ...@@ -2772,18 +2773,13 @@ static void xs_wake_error(struct sock_xprt *transport)
{ {
int sockerr; int sockerr;
if (!test_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state))
return;
mutex_lock(&transport->recv_mutex);
if (transport->sock == NULL)
goto out;
if (!test_and_clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state)) if (!test_and_clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state))
goto out; return;
sockerr = xchg(&transport->xprt_err, 0); sockerr = xchg(&transport->xprt_err, 0);
if (sockerr < 0) if (sockerr < 0) {
xprt_wake_pending_tasks(&transport->xprt, sockerr); xprt_wake_pending_tasks(&transport->xprt, sockerr);
out: xs_tcp_force_close(&transport->xprt);
mutex_unlock(&transport->recv_mutex); }
} }
static void xs_wake_pending(struct sock_xprt *transport) static void xs_wake_pending(struct sock_xprt *transport)
......
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