提交 99091700 编写于 作者: L Linus Torvalds

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

Pull NFS client bugfixes from Trond Myklebust:
 "Highlights include:

   - Stable patch from Olga to fix RPCSEC_GSS upcalls when the same user
     needs multiple different security services (e.g.  krb5i and krb5p).

   - Stable patch to fix a regression introduced by the use of
     SO_REUSEPORT, and that prevented the use of multiple different NFS
     versions to the same server.

   - TCP socket reconnection timer fixes.

   - Patch from Neil to disable the use of IPv6 temporary addresses"

* tag 'nfs-for-4.8-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFSv4: Cap the transport reconnection timer at 1/2 lease period
  NFSv4: Cleanup the setting of the nfs4 lease period
  SUNRPC: Limit the reconnect backoff timer to the max RPC message timeout
  SUNRPC: Fix reconnection timeouts
  NFSv4.2: LAYOUTSTATS may return NFS4ERR_ADMIN/DELEG_REVOKED
  SUNRPC: disable the use of IPv6 temporary addresses.
  SUNRPC: allow for upcalls for same uid but different gss service
  SUNRPC: Fix up socket autodisconnect
  SUNRPC: Handle EADDRNOTAVAIL on connection failures
...@@ -338,6 +338,8 @@ nfs42_layoutstat_done(struct rpc_task *task, void *calldata) ...@@ -338,6 +338,8 @@ nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
case 0: case 0:
break; break;
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_OLD_STATEID: case -NFS4ERR_OLD_STATEID:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
......
...@@ -396,6 +396,10 @@ extern void nfs4_schedule_state_renewal(struct nfs_client *); ...@@ -396,6 +396,10 @@ extern void nfs4_schedule_state_renewal(struct nfs_client *);
extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
extern void nfs4_kill_renewd(struct nfs_client *); extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(struct work_struct *); extern void nfs4_renew_state(struct work_struct *);
extern void nfs4_set_lease_period(struct nfs_client *clp,
unsigned long lease,
unsigned long lastrenewed);
/* nfs4state.c */ /* nfs4state.c */
struct rpc_cred *nfs4_get_clid_cred(struct nfs_client *clp); struct rpc_cred *nfs4_get_clid_cred(struct nfs_client *clp);
......
...@@ -4237,12 +4237,9 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str ...@@ -4237,12 +4237,9 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str
err = _nfs4_do_fsinfo(server, fhandle, fsinfo); err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err); trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err);
if (err == 0) { if (err == 0) {
struct nfs_client *clp = server->nfs_client; nfs4_set_lease_period(server->nfs_client,
fsinfo->lease_time * HZ,
spin_lock(&clp->cl_lock); now);
clp->cl_lease_time = fsinfo->lease_time * HZ;
clp->cl_last_renewal = now;
spin_unlock(&clp->cl_lock);
break; break;
} }
err = nfs4_handle_exception(server, err, &exception); err = nfs4_handle_exception(server, err, &exception);
......
...@@ -136,6 +136,26 @@ nfs4_kill_renewd(struct nfs_client *clp) ...@@ -136,6 +136,26 @@ nfs4_kill_renewd(struct nfs_client *clp)
cancel_delayed_work_sync(&clp->cl_renewd); cancel_delayed_work_sync(&clp->cl_renewd);
} }
/**
* nfs4_set_lease_period - Sets the lease period on a nfs_client
*
* @clp: pointer to nfs_client
* @lease: new value for lease period
* @lastrenewed: time at which lease was last renewed
*/
void nfs4_set_lease_period(struct nfs_client *clp,
unsigned long lease,
unsigned long lastrenewed)
{
spin_lock(&clp->cl_lock);
clp->cl_lease_time = lease;
clp->cl_last_renewal = lastrenewed;
spin_unlock(&clp->cl_lock);
/* Cap maximum reconnect timeout at 1/2 lease period */
rpc_cap_max_reconnect_timeout(clp->cl_rpcclient, lease >> 1);
}
/* /*
* Local variables: * Local variables:
* c-basic-offset: 8 * c-basic-offset: 8
......
...@@ -277,20 +277,17 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp) ...@@ -277,20 +277,17 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp)
{ {
int status; int status;
struct nfs_fsinfo fsinfo; struct nfs_fsinfo fsinfo;
unsigned long now;
if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) { if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) {
nfs4_schedule_state_renewal(clp); nfs4_schedule_state_renewal(clp);
return 0; return 0;
} }
now = jiffies;
status = nfs4_proc_get_lease_time(clp, &fsinfo); status = nfs4_proc_get_lease_time(clp, &fsinfo);
if (status == 0) { if (status == 0) {
/* Update lease time and schedule renewal */ nfs4_set_lease_period(clp, fsinfo.lease_time * HZ, now);
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ;
clp->cl_last_renewal = jiffies;
spin_unlock(&clp->cl_lock);
nfs4_schedule_state_renewal(clp); nfs4_schedule_state_renewal(clp);
} }
......
...@@ -195,6 +195,8 @@ int rpc_clnt_add_xprt(struct rpc_clnt *, struct xprt_create *, ...@@ -195,6 +195,8 @@ int rpc_clnt_add_xprt(struct rpc_clnt *, struct xprt_create *,
struct rpc_xprt *, struct rpc_xprt *,
void *), void *),
void *data); void *data);
void rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt,
unsigned long timeo);
const char *rpc_proc_name(const struct rpc_task *task); const char *rpc_proc_name(const struct rpc_task *task);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -218,7 +218,8 @@ struct rpc_xprt { ...@@ -218,7 +218,8 @@ struct rpc_xprt {
struct work_struct task_cleanup; struct work_struct task_cleanup;
struct timer_list timer; struct timer_list timer;
unsigned long last_used, unsigned long last_used,
idle_timeout; idle_timeout,
max_reconnect_timeout;
/* /*
* Send stuff * Send stuff
......
...@@ -340,12 +340,14 @@ gss_release_msg(struct gss_upcall_msg *gss_msg) ...@@ -340,12 +340,14 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
} }
static struct gss_upcall_msg * static struct gss_upcall_msg *
__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid) __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth)
{ {
struct gss_upcall_msg *pos; struct gss_upcall_msg *pos;
list_for_each_entry(pos, &pipe->in_downcall, list) { list_for_each_entry(pos, &pipe->in_downcall, list) {
if (!uid_eq(pos->uid, uid)) if (!uid_eq(pos->uid, uid))
continue; continue;
if (auth && pos->auth->service != auth->service)
continue;
atomic_inc(&pos->count); atomic_inc(&pos->count);
dprintk("RPC: %s found msg %p\n", __func__, pos); dprintk("RPC: %s found msg %p\n", __func__, pos);
return pos; return pos;
...@@ -365,7 +367,7 @@ gss_add_msg(struct gss_upcall_msg *gss_msg) ...@@ -365,7 +367,7 @@ gss_add_msg(struct gss_upcall_msg *gss_msg)
struct gss_upcall_msg *old; struct gss_upcall_msg *old;
spin_lock(&pipe->lock); spin_lock(&pipe->lock);
old = __gss_find_upcall(pipe, gss_msg->uid); old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth);
if (old == NULL) { if (old == NULL) {
atomic_inc(&gss_msg->count); atomic_inc(&gss_msg->count);
list_add(&gss_msg->list, &pipe->in_downcall); list_add(&gss_msg->list, &pipe->in_downcall);
...@@ -714,7 +716,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ...@@ -714,7 +716,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
err = -ENOENT; err = -ENOENT;
/* Find a matching upcall */ /* Find a matching upcall */
spin_lock(&pipe->lock); spin_lock(&pipe->lock);
gss_msg = __gss_find_upcall(pipe, uid); gss_msg = __gss_find_upcall(pipe, uid, NULL);
if (gss_msg == NULL) { if (gss_msg == NULL) {
spin_unlock(&pipe->lock); spin_unlock(&pipe->lock);
goto err_put_ctx; goto err_put_ctx;
......
...@@ -2638,6 +2638,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2638,6 +2638,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
{ {
struct rpc_xprt_switch *xps; struct rpc_xprt_switch *xps;
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
unsigned long reconnect_timeout;
unsigned char resvport; unsigned char resvport;
int ret = 0; int ret = 0;
...@@ -2649,6 +2650,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2649,6 +2650,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
return -EAGAIN; return -EAGAIN;
} }
resvport = xprt->resvport; resvport = xprt->resvport;
reconnect_timeout = xprt->max_reconnect_timeout;
rcu_read_unlock(); rcu_read_unlock();
xprt = xprt_create_transport(xprtargs); xprt = xprt_create_transport(xprtargs);
...@@ -2657,6 +2659,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2657,6 +2659,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
goto out_put_switch; goto out_put_switch;
} }
xprt->resvport = resvport; xprt->resvport = resvport;
xprt->max_reconnect_timeout = reconnect_timeout;
rpc_xprt_switch_set_roundrobin(xps); rpc_xprt_switch_set_roundrobin(xps);
if (setup) { if (setup) {
...@@ -2673,6 +2676,27 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2673,6 +2676,27 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
} }
EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt); EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt);
static int
rpc_xprt_cap_max_reconnect_timeout(struct rpc_clnt *clnt,
struct rpc_xprt *xprt,
void *data)
{
unsigned long timeout = *((unsigned long *)data);
if (timeout < xprt->max_reconnect_timeout)
xprt->max_reconnect_timeout = timeout;
return 0;
}
void
rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt, unsigned long timeo)
{
rpc_clnt_iterate_for_each_xprt(clnt,
rpc_xprt_cap_max_reconnect_timeout,
&timeo);
}
EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout);
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static void rpc_show_header(void) static void rpc_show_header(void)
{ {
......
...@@ -680,6 +680,20 @@ void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie) ...@@ -680,6 +680,20 @@ void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie)
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
} }
static bool
xprt_has_timer(const struct rpc_xprt *xprt)
{
return xprt->idle_timeout != 0;
}
static void
xprt_schedule_autodisconnect(struct rpc_xprt *xprt)
__must_hold(&xprt->transport_lock)
{
if (list_empty(&xprt->recv) && xprt_has_timer(xprt))
mod_timer(&xprt->timer, xprt->last_used + xprt->idle_timeout);
}
static void static void
xprt_init_autodisconnect(unsigned long data) xprt_init_autodisconnect(unsigned long data)
{ {
...@@ -688,6 +702,8 @@ xprt_init_autodisconnect(unsigned long data) ...@@ -688,6 +702,8 @@ xprt_init_autodisconnect(unsigned long data)
spin_lock(&xprt->transport_lock); spin_lock(&xprt->transport_lock);
if (!list_empty(&xprt->recv)) if (!list_empty(&xprt->recv))
goto out_abort; goto out_abort;
/* Reset xprt->last_used to avoid connect/autodisconnect cycling */
xprt->last_used = jiffies;
if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
goto out_abort; goto out_abort;
spin_unlock(&xprt->transport_lock); spin_unlock(&xprt->transport_lock);
...@@ -725,6 +741,7 @@ void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie) ...@@ -725,6 +741,7 @@ void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie)
goto out; goto out;
xprt->snd_task =NULL; xprt->snd_task =NULL;
xprt->ops->release_xprt(xprt, NULL); xprt->ops->release_xprt(xprt, NULL);
xprt_schedule_autodisconnect(xprt);
out: out:
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
wake_up_bit(&xprt->state, XPRT_LOCKED); wake_up_bit(&xprt->state, XPRT_LOCKED);
...@@ -888,11 +905,6 @@ static void xprt_timer(struct rpc_task *task) ...@@ -888,11 +905,6 @@ static void xprt_timer(struct rpc_task *task)
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
} }
static inline int xprt_has_timer(struct rpc_xprt *xprt)
{
return xprt->idle_timeout != 0;
}
/** /**
* xprt_prepare_transmit - reserve the transport before sending a request * xprt_prepare_transmit - reserve the transport before sending a request
* @task: RPC task about to send a request * @task: RPC task about to send a request
...@@ -1280,9 +1292,7 @@ void xprt_release(struct rpc_task *task) ...@@ -1280,9 +1292,7 @@ void xprt_release(struct rpc_task *task)
if (!list_empty(&req->rq_list)) if (!list_empty(&req->rq_list))
list_del(&req->rq_list); list_del(&req->rq_list);
xprt->last_used = jiffies; xprt->last_used = jiffies;
if (list_empty(&xprt->recv) && xprt_has_timer(xprt)) xprt_schedule_autodisconnect(xprt);
mod_timer(&xprt->timer,
xprt->last_used + xprt->idle_timeout);
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
if (req->rq_buffer) if (req->rq_buffer)
xprt->ops->buf_free(req->rq_buffer); xprt->ops->buf_free(req->rq_buffer);
......
...@@ -177,7 +177,6 @@ static struct ctl_table sunrpc_table[] = { ...@@ -177,7 +177,6 @@ static struct ctl_table sunrpc_table[] = {
* increase over time if the server is down or not responding. * increase over time if the server is down or not responding.
*/ */
#define XS_TCP_INIT_REEST_TO (3U * HZ) #define XS_TCP_INIT_REEST_TO (3U * HZ)
#define XS_TCP_MAX_REEST_TO (5U * 60 * HZ)
/* /*
* TCP idle timeout; client drops the transport socket if it is idle * TCP idle timeout; client drops the transport socket if it is idle
...@@ -2173,6 +2172,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) ...@@ -2173,6 +2172,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
write_unlock_bh(&sk->sk_callback_lock); write_unlock_bh(&sk->sk_callback_lock);
} }
xs_udp_do_set_buffer_size(xprt); xs_udp_do_set_buffer_size(xprt);
xprt->stat.connect_start = jiffies;
} }
static void xs_udp_setup_socket(struct work_struct *work) static void xs_udp_setup_socket(struct work_struct *work)
...@@ -2236,6 +2237,7 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) ...@@ -2236,6 +2237,7 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
unsigned int keepcnt = xprt->timeout->to_retries + 1; unsigned int keepcnt = xprt->timeout->to_retries + 1;
unsigned int opt_on = 1; unsigned int opt_on = 1;
unsigned int timeo; unsigned int timeo;
unsigned int addr_pref = IPV6_PREFER_SRC_PUBLIC;
/* TCP Keepalive options */ /* TCP Keepalive options */
kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
...@@ -2247,6 +2249,16 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) ...@@ -2247,6 +2249,16 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
kernel_setsockopt(sock, SOL_TCP, TCP_KEEPCNT, kernel_setsockopt(sock, SOL_TCP, TCP_KEEPCNT,
(char *)&keepcnt, sizeof(keepcnt)); (char *)&keepcnt, sizeof(keepcnt));
/* Avoid temporary address, they are bad for long-lived
* connections such as NFS mounts.
* RFC4941, section 3.6 suggests that:
* Individual applications, which have specific
* knowledge about the normal duration of connections,
* MAY override this as appropriate.
*/
kernel_setsockopt(sock, SOL_IPV6, IPV6_ADDR_PREFERENCES,
(char *)&addr_pref, sizeof(addr_pref));
/* TCP user timeout (see RFC5482) */ /* TCP user timeout (see RFC5482) */
timeo = jiffies_to_msecs(xprt->timeout->to_initval) * timeo = jiffies_to_msecs(xprt->timeout->to_initval) *
(xprt->timeout->to_retries + 1); (xprt->timeout->to_retries + 1);
...@@ -2295,6 +2307,10 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) ...@@ -2295,6 +2307,10 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
/* SYN_SENT! */ /* SYN_SENT! */
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO) if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO; xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
break;
case -EADDRNOTAVAIL:
/* Source port number is unavailable. Try a new one! */
transport->srcport = 0;
} }
out: out:
return ret; return ret;
...@@ -2369,6 +2385,25 @@ static void xs_tcp_setup_socket(struct work_struct *work) ...@@ -2369,6 +2385,25 @@ static void xs_tcp_setup_socket(struct work_struct *work)
xprt_wake_pending_tasks(xprt, status); xprt_wake_pending_tasks(xprt, status);
} }
static unsigned long xs_reconnect_delay(const struct rpc_xprt *xprt)
{
unsigned long start, now = jiffies;
start = xprt->stat.connect_start + xprt->reestablish_timeout;
if (time_after(start, now))
return start - now;
return 0;
}
static void xs_reconnect_backoff(struct rpc_xprt *xprt)
{
xprt->reestablish_timeout <<= 1;
if (xprt->reestablish_timeout > xprt->max_reconnect_timeout)
xprt->reestablish_timeout = xprt->max_reconnect_timeout;
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
}
/** /**
* xs_connect - connect a socket to a remote endpoint * xs_connect - connect a socket to a remote endpoint
* @xprt: pointer to transport structure * @xprt: pointer to transport structure
...@@ -2386,6 +2421,7 @@ static void xs_tcp_setup_socket(struct work_struct *work) ...@@ -2386,6 +2421,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task) static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
{ {
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
unsigned long delay = 0;
WARN_ON_ONCE(!xprt_lock_connect(xprt, task, transport)); WARN_ON_ONCE(!xprt_lock_connect(xprt, task, transport));
...@@ -2397,19 +2433,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task) ...@@ -2397,19 +2433,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
/* Start by resetting any existing state */ /* Start by resetting any existing state */
xs_reset_transport(transport); xs_reset_transport(transport);
queue_delayed_work(xprtiod_workqueue, delay = xs_reconnect_delay(xprt);
&transport->connect_worker, xs_reconnect_backoff(xprt);
xprt->reestablish_timeout);
xprt->reestablish_timeout <<= 1; } else
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
if (xprt->reestablish_timeout > XS_TCP_MAX_REEST_TO)
xprt->reestablish_timeout = XS_TCP_MAX_REEST_TO;
} else {
dprintk("RPC: xs_connect scheduled xprt %p\n", xprt); dprintk("RPC: xs_connect scheduled xprt %p\n", xprt);
queue_delayed_work(xprtiod_workqueue,
&transport->connect_worker, 0); queue_delayed_work(xprtiod_workqueue,
} &transport->connect_worker,
delay);
} }
/** /**
...@@ -2961,6 +2993,8 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args) ...@@ -2961,6 +2993,8 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
xprt->ops = &xs_tcp_ops; xprt->ops = &xs_tcp_ops;
xprt->timeout = &xs_tcp_default_timeout; xprt->timeout = &xs_tcp_default_timeout;
xprt->max_reconnect_timeout = xprt->timeout->to_maxval;
INIT_WORK(&transport->recv_worker, xs_tcp_data_receive_workfn); INIT_WORK(&transport->recv_worker, xs_tcp_data_receive_workfn);
INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_setup_socket); INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_setup_socket);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册