Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/iscsi/icl_soft_proxy.c
Show First 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | |||||
#include <dev/iscsi/icl.h> | #include <dev/iscsi/icl.h> | ||||
struct icl_listen_sock { | struct icl_listen_sock { | ||||
TAILQ_ENTRY(icl_listen_sock) ils_next; | TAILQ_ENTRY(icl_listen_sock) ils_next; | ||||
struct icl_listen *ils_listen; | struct icl_listen *ils_listen; | ||||
struct socket *ils_socket; | struct socket *ils_socket; | ||||
bool ils_running; | bool ils_running; | ||||
bool ils_disconnecting; | |||||
int ils_id; | int ils_id; | ||||
}; | }; | ||||
struct icl_listen { | struct icl_listen { | ||||
TAILQ_HEAD(, icl_listen_sock) il_sockets; | TAILQ_HEAD(, icl_listen_sock) il_sockets; | ||||
struct sx il_lock; | struct sx il_lock; | ||||
void (*il_accept)(struct socket *, | void (*il_accept)(struct socket *, | ||||
struct sockaddr *, int); | struct sockaddr *, int); | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | icl_listen_free(struct icl_listen *il) | ||||
struct icl_listen_sock *ils; | struct icl_listen_sock *ils; | ||||
sx_xlock(&il->il_lock); | sx_xlock(&il->il_lock); | ||||
while (!TAILQ_EMPTY(&il->il_sockets)) { | while (!TAILQ_EMPTY(&il->il_sockets)) { | ||||
ils = TAILQ_FIRST(&il->il_sockets); | ils = TAILQ_FIRST(&il->il_sockets); | ||||
while (ils->ils_running) { | while (ils->ils_running) { | ||||
ICL_DEBUG("waiting for accept thread to terminate"); | ICL_DEBUG("waiting for accept thread to terminate"); | ||||
sx_xunlock(&il->il_lock); | sx_xunlock(&il->il_lock); | ||||
ils->ils_disconnecting = true; | SOLISTEN_LOCK(ils->ils_socket); | ||||
ils->ils_socket->so_error = ENOTCONN; | |||||
SOLISTEN_UNLOCK(ils->ils_socket); | |||||
wakeup(&ils->ils_socket->so_timeo); | wakeup(&ils->ils_socket->so_timeo); | ||||
pause("icl_unlisten", 1 * hz); | pause("icl_unlisten", 1 * hz); | ||||
sx_xlock(&il->il_lock); | sx_xlock(&il->il_lock); | ||||
} | } | ||||
TAILQ_REMOVE(&il->il_sockets, ils, ils_next); | TAILQ_REMOVE(&il->il_sockets, ils, ils_next); | ||||
soclose(ils->ils_socket); | soclose(ils->ils_socket); | ||||
free(ils, M_ICL_PROXY); | free(ils, M_ICL_PROXY); | ||||
} | } | ||||
sx_xunlock(&il->il_lock); | sx_xunlock(&il->il_lock); | ||||
free(il, M_ICL_PROXY); | free(il, M_ICL_PROXY); | ||||
} | } | ||||
/* | /* | ||||
* XXX: Doing accept in a separate thread in each socket might not be the best way | * XXX: Doing accept in a separate thread in each socket might not be the | ||||
* to do stuff, but it's pretty clean and debuggable - and you probably won't | * best way to do stuff, but it's pretty clean and debuggable - and you | ||||
* have hundreds of listening sockets anyway. | * probably won't have hundreds of listening sockets anyway. | ||||
*/ | */ | ||||
static void | static void | ||||
icl_accept_thread(void *arg) | icl_accept_thread(void *arg) | ||||
{ | { | ||||
struct icl_listen_sock *ils; | struct icl_listen_sock *ils; | ||||
struct socket *head, *so; | struct socket *head, *so; | ||||
struct sockaddr *sa; | struct sockaddr *sa; | ||||
int error; | int error; | ||||
ils = arg; | ils = arg; | ||||
head = ils->ils_socket; | head = ils->ils_socket; | ||||
ils->ils_running = true; | ils->ils_running = true; | ||||
for (;;) { | for (;;) { | ||||
ACCEPT_LOCK(); | SOLISTEN_LOCK(head); | ||||
while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0 && ils->ils_disconnecting == false) { | error = solisten_dequeue(head, &so, 0); | ||||
if (head->so_rcv.sb_state & SBS_CANTRCVMORE) { | if (error == ENOTCONN) { | ||||
head->so_error = ECONNABORTED; | /* | ||||
break; | * XXXGL: ENOTCONN is our mark from icl_listen_free(). | ||||
} | * Neither socket code, nor msleep(9) may return it. | ||||
error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH, | */ | ||||
"accept", 0); | |||||
if (error) { | |||||
ACCEPT_UNLOCK(); | |||||
ICL_WARN("msleep failed with error %d", error); | |||||
continue; | |||||
} | |||||
if (ils->ils_disconnecting) { | |||||
ACCEPT_UNLOCK(); | |||||
ICL_DEBUG("terminating"); | ICL_DEBUG("terminating"); | ||||
ils->ils_running = false; | ils->ils_running = false; | ||||
kthread_exit(); | kthread_exit(); | ||||
return; | return; | ||||
} | } | ||||
} | if (error) { | ||||
if (head->so_error) { | ICL_WARN("solisten_dequeue error %d", error); | ||||
error = head->so_error; | |||||
head->so_error = 0; | |||||
ACCEPT_UNLOCK(); | |||||
ICL_WARN("socket error %d", error); | |||||
continue; | continue; | ||||
} | } | ||||
so = TAILQ_FIRST(&head->so_comp); | |||||
KASSERT(so != NULL, ("NULL so")); | |||||
KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP")); | |||||
KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP")); | |||||
/* | |||||
* Before changing the flags on the socket, we have to bump the | |||||
* reference count. Otherwise, if the protocol calls sofree(), | |||||
* the socket will be released due to a zero refcount. | |||||
*/ | |||||
SOCK_LOCK(so); /* soref() and so_state update */ | |||||
soref(so); /* file descriptor reference */ | |||||
TAILQ_REMOVE(&head->so_comp, so, so_list); | |||||
head->so_qlen--; | |||||
so->so_state |= (head->so_state & SS_NBIO); | |||||
so->so_qstate &= ~SQ_COMP; | |||||
so->so_head = NULL; | |||||
SOCK_UNLOCK(so); | |||||
ACCEPT_UNLOCK(); | |||||
sa = NULL; | sa = NULL; | ||||
error = soaccept(so, &sa); | error = soaccept(so, &sa); | ||||
if (error != 0) { | if (error != 0) { | ||||
ICL_WARN("soaccept error %d", error); | ICL_WARN("soaccept error %d", error); | ||||
if (sa != NULL) | if (sa != NULL) | ||||
free(sa, M_SONAME); | free(sa, M_SONAME); | ||||
soclose(so); | soclose(so); | ||||
▲ Show 20 Lines • Show All 98 Lines • Show Last 20 Lines |