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 @@
[NFS] set the TCP port on which the NFSv4 callback
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 64-bit inode numbers.
If zero, the NFS client will fake up a 32-bit inode
......
......@@ -125,7 +125,7 @@ config PNFS_BLOCK
config PNFS_FLEXFILE_LAYOUT
tristate
depends on NFS_V4_1 && NFS_V3
depends on NFS_V4_1
default NFS_V4
config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
......
......@@ -448,6 +448,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
delegation->cred = get_cred(cred);
delegation->inode = inode;
delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
delegation->test_gen = 0;
spin_lock_init(&delegation->lock);
spin_lock(&clp->cl_lock);
......@@ -1294,6 +1295,8 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
struct inode *inode;
const struct cred *cred;
nfs4_stateid stateid;
unsigned long gen = ++server->delegation_gen;
restart:
rcu_read_lock();
restart_locked:
......@@ -1303,7 +1306,8 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
test_bit(NFS_DELEGATION_RETURNING,
&delegation->flags) ||
test_bit(NFS_DELEGATION_TEST_EXPIRED,
&delegation->flags) == 0)
&delegation->flags) == 0 ||
delegation->test_gen == gen)
continue;
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
......@@ -1312,6 +1316,7 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
cred = get_cred_rcu(delegation->cred);
nfs4_stateid_copy(&stateid, &delegation->stateid);
spin_unlock(&delegation->lock);
delegation->test_gen = gen;
clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
rcu_read_unlock();
nfs_delegation_test_free_expired(inode, &stateid, cred);
......
......@@ -21,6 +21,7 @@ struct nfs_delegation {
fmode_t type;
unsigned long pagemod_limit;
__u64 change_attr;
unsigned long test_gen;
unsigned long flags;
refcount_t refcount;
spinlock_t lock;
......
......@@ -2532,7 +2532,7 @@ EXPORT_SYMBOL_GPL(nfs_unlink);
int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *symname)
{
struct page *page;
struct folio *folio;
char *kaddr;
struct iattr attr;
unsigned int pathlen = strlen(symname);
......@@ -2547,24 +2547,24 @@ int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
attr.ia_mode = S_IFLNK | S_IRWXUGO;
attr.ia_valid = ATTR_MODE;
page = alloc_page(GFP_USER);
if (!page)
folio = folio_alloc(GFP_USER, 0);
if (!folio)
return -ENOMEM;
kaddr = page_address(page);
kaddr = folio_address(folio);
memcpy(kaddr, symname, pathlen);
if (pathlen < PAGE_SIZE)
memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
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);
if (error != 0) {
dfprintk(VFS, "NFS: symlink(%s/%lu, %pd, %s) error %d\n",
dir->i_sb->s_id, dir->i_ino,
dentry, symname, error);
d_drop(dentry);
__free_page(page);
folio_put(folio);
return error;
}
......@@ -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.
* READLINK will get the missing page from the server if needed.
*/
if (!add_to_page_cache_lru(page, d_inode(dentry)->i_mapping, 0,
GFP_KERNEL)) {
SetPageUptodate(page);
unlock_page(page);
/*
* 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);
if (filemap_add_folio(d_inode(dentry)->i_mapping, folio, 0,
GFP_KERNEL) == 0) {
folio_mark_uptodate(folio);
folio_unlock(folio);
}
folio_put(folio);
return 0;
}
EXPORT_SYMBOL_GPL(nfs_symlink);
......
......@@ -543,9 +543,10 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name)
}
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)
{
struct page *page = &folio->page;
struct nfs3_createdata *data;
struct dentry *d_alias;
int status = -ENOMEM;
......
......@@ -209,6 +209,7 @@ struct nfs4_exception {
struct inode *inode;
nfs4_stateid *stateid;
long timeout;
unsigned short retrans;
unsigned char task_is_privileged : 1;
unsigned char delay : 1,
recovering : 1,
......@@ -546,6 +547,7 @@ extern unsigned short max_session_slots;
extern unsigned short max_session_cb_slots;
extern unsigned short send_implementation_id;
extern bool recover_lost_locks;
extern short nfs_delay_retrans;
#define NFS4_CLIENT_ID_UNIQ_LEN (64)
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,
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
* to sleep.
*/
......@@ -595,6 +610,11 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
ret = nfs4_do_handle_exception(server, errorcode, exception);
if (exception->delay) {
int ret2 = nfs4_exception_should_retrans(server, exception);
if (ret2 < 0) {
exception->retry = 0;
return ret2;
}
ret = nfs4_delay(&exception->timeout,
exception->interruptible);
goto out_retry;
......@@ -623,6 +643,11 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server,
ret = nfs4_do_handle_exception(server, errorcode, exception);
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));
goto out_retry;
}
......@@ -5011,9 +5036,10 @@ static void nfs4_free_createdata(struct nfs4_createdata *data)
}
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 page *page = &folio->page;
struct nfs4_createdata *data;
int status = -ENAMETOOLONG;
......@@ -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,
struct page *page, unsigned int len, struct iattr *sattr)
struct folio *folio, unsigned int len, struct iattr *sattr)
{
struct nfs4_exception exception = {
.interruptible = true,
......@@ -5049,7 +5075,7 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
label = nfs4_label_init_security(dir, dentry, sattr, &l);
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);
err = nfs4_handle_exception(NFS_SERVER(dir), err,
&exception);
......@@ -5622,7 +5648,7 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
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)
......@@ -5663,7 +5689,8 @@ static void nfs4_proc_commit_setup(struct nfs_commit_data *data, struct rpc_mess
data->res.server = server;
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
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,
......@@ -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);
try_again:
/* Test connection for session trunking. Async exchange_id call */
task = nfs4_run_exchange_id(adata->clp, adata->cred, sp4_how, xprt);
if (IS_ERR(task))
......@@ -8946,11 +8974,15 @@ void nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
if (status == 0)
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))
rpc_clnt_xprt_switch_remove_xprt(clnt, xprt);
rpc_put_task(task);
if (status == -NFS4ERR_DELAY) {
ssleep(1);
goto try_again;
}
}
EXPORT_SYMBOL_GPL(nfs4_test_session_trunk);
......@@ -9621,6 +9653,9 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
nfs4_sequence_free_slot(&lgp->res.seq_res);
exception->state = NULL;
exception->stateid = NULL;
switch (nfs4err) {
case 0:
goto out;
......@@ -9716,7 +9751,8 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = {
};
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 nfs_server *server = NFS_SERVER(inode);
......@@ -9736,13 +9772,10 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
RPC_TASK_MOVEABLE,
};
struct pnfs_layout_segment *lseg = NULL;
struct nfs4_exception exception = {
.inode = inode,
.timeout = *timeout,
};
int status = 0;
nfs4_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0, 0);
exception->retry = 0;
task = rpc_run_task(&task_setup_data);
if (IS_ERR(task))
......@@ -9753,11 +9786,12 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
goto out;
if (task->tk_status < 0) {
status = nfs4_layoutget_handle_exception(task, lgp, &exception);
*timeout = exception.timeout;
exception->retry = 1;
status = nfs4_layoutget_handle_exception(task, lgp, exception);
} else if (lgp->res.layoutp->len == 0) {
exception->retry = 1;
status = -EAGAIN;
*timeout = nfs4_update_delay(&exception.timeout);
nfs4_update_delay(&exception->timeout);
} else
lseg = pnfs_layout_process(lgp);
out:
......
......@@ -1980,7 +1980,9 @@ pnfs_update_layout(struct inode *ino,
struct pnfs_layout_segment *lseg = NULL;
struct nfs4_layoutget *lgp;
nfs4_stateid stateid;
long timeout = 0;
struct nfs4_exception exception = {
.inode = ino,
};
unsigned long giveup = jiffies + (clp->cl_lease_time << 1);
bool first;
......@@ -2144,7 +2146,7 @@ pnfs_update_layout(struct inode *ino,
lgp->lo = 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,
PNFS_UPDATE_LAYOUT_SEND_LAYOUTGET);
nfs_layoutget_end(lo);
......@@ -2171,6 +2173,8 @@ pnfs_update_layout(struct inode *ino,
goto out_put_layout_hdr;
}
if (lseg) {
if (!exception.retry)
goto out_put_layout_hdr;
if (first)
pnfs_clear_first_layoutget(lo);
trace_pnfs_update_layout(ino, pos, count,
......
......@@ -35,6 +35,7 @@
#include <linux/nfs_page.h>
#include <linux/workqueue.h>
struct nfs4_exception;
struct nfs4_opendata;
enum {
......@@ -245,7 +246,9 @@ extern size_t max_response_pages(struct nfs_server *server);
extern int nfs4_proc_getdeviceinfo(struct nfs_server *server,
struct pnfs_device *dev,
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);
/* pnfs.c */
......
......@@ -396,9 +396,10 @@ nfs_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name)
}
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)
{
struct page *page = &folio->page;
struct nfs_fh *fh;
struct nfs_fattr *fattr;
struct nfs_symlinkargs arg = {
......
......@@ -1371,6 +1371,7 @@ unsigned short max_session_cb_slots = NFS4_DEF_CB_SLOT_TABLE_SIZE;
unsigned short send_implementation_id = 1;
char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = "";
bool recover_lost_locks = false;
short nfs_delay_retrans = -1;
EXPORT_SYMBOL_GPL(nfs_callback_nr_threads);
EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport);
......@@ -1381,6 +1382,7 @@ EXPORT_SYMBOL_GPL(max_session_cb_slots);
EXPORT_SYMBOL_GPL(send_implementation_id);
EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier);
EXPORT_SYMBOL_GPL(recover_lost_locks);
EXPORT_SYMBOL_GPL(nfs_delay_retrans);
#define NFS_CALLBACK_MAXPORTNR (65535U)
......@@ -1429,5 +1431,9 @@ MODULE_PARM_DESC(recover_lost_locks,
"If the server reports that a lock might be lost, "
"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 */
......@@ -739,6 +739,8 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
&pgio);
pgio.pg_error = 0;
nfs_pageio_complete(&pgio);
if (err == -EAGAIN && mntflags & NFS_MOUNT_SOFTERR)
break;
} while (err < 0 && !nfs_error_is_fatal(err));
nfs_io_completion_put(ioc);
......
......@@ -239,6 +239,7 @@ struct nfs_server {
struct list_head delegations;
struct list_head ss_copies;
unsigned long delegation_gen;
unsigned long mig_gen;
unsigned long mig_status;
#define NFS_MIG_IN_TRANSITION (1)
......
......@@ -1772,7 +1772,7 @@ struct nfs_rpc_ops {
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 (*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 *);
int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
int (*rmdir) (struct inode *, const struct qstr *);
......
......@@ -92,6 +92,7 @@ struct rpc_clnt {
};
const struct cred *cl_cred;
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)
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
__rpc_clnt_remove_pipedir(clnt);
if (pipefs_sb == clnt->pipefs_sb)
__rpc_clnt_remove_pipedir(clnt);
rpc_put_sb_net(net);
}
}
......@@ -151,6 +152,8 @@ rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt)
{
struct dentry *dentry;
clnt->pipefs_sb = pipefs_sb;
if (clnt->cl_program->pipe_dir_name != NULL) {
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt);
if (IS_ERR(dentry))
......@@ -2171,6 +2174,7 @@ call_connect_status(struct rpc_task *task)
task->tk_status = 0;
switch (status) {
case -ECONNREFUSED:
case -ECONNRESET:
/* A positive refusal suggests a rebind is needed. */
if (RPC_IS_SOFTCONN(task))
break;
......@@ -2179,7 +2183,6 @@ call_connect_status(struct rpc_task *task)
goto out_retry;
}
fallthrough;
case -ECONNRESET:
case -ECONNABORTED:
case -ENETDOWN:
case -ENETUNREACH:
......@@ -2220,7 +2223,7 @@ call_connect_status(struct rpc_task *task)
}
xprt_switch_put(xps);
if (!task->tk_xprt)
return;
goto out;
}
goto out_retry;
case -ENOBUFS:
......@@ -2235,6 +2238,7 @@ call_connect_status(struct rpc_task *task)
out_retry:
/* Check for timeouts before looping back to call_bind */
task->tk_action = call_bind;
out:
rpc_check_timeout(task);
}
......
......@@ -769,6 +769,10 @@ void rpcb_getport_async(struct rpc_task *task)
child = rpcb_call_async(rpcb_clnt, map, proc);
rpc_release_client(rpcb_clnt);
if (IS_ERR(child)) {
/* rpcb_map_release() has freed the arguments */
return;
}
xprt->stat.bind_count++;
rpc_put_task(child);
......
......@@ -283,7 +283,7 @@ int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
xprt_clear_locked(xprt);
out_sleep:
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,
xprt_request_timeout(req));
else
......@@ -349,7 +349,7 @@ int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
xprt_clear_locked(xprt);
out_sleep:
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,
xprt_request_timeout(req));
else
......
......@@ -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);
transport->xprt_err = 0;
clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
clear_bit(XPRT_SOCK_WAKE_ERROR, &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)
{
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))
goto out;
return;
sockerr = xchg(&transport->xprt_err, 0);
if (sockerr < 0)
if (sockerr < 0) {
xprt_wake_pending_tasks(&transport->xprt, sockerr);
out:
mutex_unlock(&transport->recv_mutex);
xs_tcp_force_close(&transport->xprt);
}
}
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