[stgt] [PATCH v3] tgtd: send/recv iSCSI PDUs in worker threads
Hitoshi Mitake
mitake.hitoshi at gmail.com
Tue Dec 10 14:07:24 CET 2013
At Sun, 24 Nov 2013 23:07:52 +0900,
Hitoshi Mitake wrote:
>
> From: Hitoshi Mitake <mitake.hitoshi at lab.ntt.co.jp>
>
> Current tgtd sends and receives iSCSI PDUs in its main event
> loop. This design can cause bottleneck when many iSCSI clients connect
> to single tgtd process. For example, we need multiple tgtd processes
> for utilizing fast network like 10 GbE because typical single
> processor core isn't fast enough for processing bunch of requests.
>
> This patch lets tgtd send/receive iSCSI PDUs and check digests in its
> worker threads. After applying this patch, the bottleneck in the main
> event loop is removed and the performance is improved.
>
> The improvement can be seen even if tgtd and iSCSI initiator are
> running on a single host. Below is a snippet of fio result on my
> linux box. The workload is 128MB random RW. Backingstore is sheepdog.
>
> Original tgtd:
> read : io=65008KB, bw=6259.9KB/s, iops=1564 , runt= 10385msec
> write: io=66064KB, bw=6361.5KB/s, iops=1590 , runt= 10385msec
>
> tgtd with this patch:
> read : io=65416KB, bw=7295.2KB/s, iops=1823 , runt= 8967msec
> write: io=65656KB, bw=7321.1KB/s, iops=1830 , runt= 8967msec
>
> Signed-off-by: Hitoshi Mitake <mitake.hitoshi at lab.ntt.co.jp>
> ---
I benchmarked this patch and found that it has a problem of
performance stability. I'll refine it and send v4 in the near
future. Please ignore this version.
Thanks,
Hitoshi
>
> v3:
> - Exeucte iscsi_task_rx_start() and iscsi_task_tx_start() in a serialized
> manner. Because they manipulate data structures related to iSCSI session.
>
> v2:
> - correct handling of connection closing based on a reference count of an iSCSI
> connection
> - a silly bug in iscsi_tcp_init() introduced in the previous patch is removed
>
> usr/iscsi/conn.c | 12 ++-
> usr/iscsi/iscsi_tcp.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++---
> usr/iscsi/iscsid.c | 110 +++++++++++++------
> usr/iscsi/iscsid.h | 8 +-
> 4 files changed, 372 insertions(+), 48 deletions(-)
>
> diff --git a/usr/iscsi/conn.c b/usr/iscsi/conn.c
> index 17aa3e3..21b552e 100644
> --- a/usr/iscsi/conn.c
> +++ b/usr/iscsi/conn.c
> @@ -83,7 +83,7 @@ void conn_exit(struct iscsi_connection *conn)
> session_put(session);
> }
>
> -void conn_close(struct iscsi_connection *conn)
> +int conn_close(struct iscsi_connection *conn)
> {
> struct iscsi_task *task, *tmp;
> int ret;
> @@ -180,14 +180,18 @@ void conn_close(struct iscsi_connection *conn)
> iscsi_free_task(task);
> }
> done:
> - conn_put(conn);
> + return conn_put(conn);
> }
>
> -void conn_put(struct iscsi_connection *conn)
> +int conn_put(struct iscsi_connection *conn)
> {
> conn->refcount--;
> - if (!conn->refcount)
> + if (!conn->refcount) {
> conn->tp->ep_release(conn);
> + return 0;
> + }
> +
> + return conn->refcount;
> }
>
> int conn_get(struct iscsi_connection *conn)
> diff --git a/usr/iscsi/iscsi_tcp.c b/usr/iscsi/iscsi_tcp.c
> index 4a9532a..cf9d734 100644
> --- a/usr/iscsi/iscsi_tcp.c
> +++ b/usr/iscsi/iscsi_tcp.c
> @@ -31,6 +31,7 @@
> #include <netinet/tcp.h>
> #include <sys/epoll.h>
> #include <sys/socket.h>
> +#include <pthread.h>
>
> #include "iscsid.h"
> #include "tgtd.h"
> @@ -48,6 +49,20 @@ static long nop_ttt;
> static int listen_fds[8];
> static struct iscsi_transport iscsi_tcp;
>
> +enum iscsi_tcp_work_state {
> + ISCSI_TCP_WORK_INIT,
> + ISCSI_TCP_WORK_RX,
> + ISCSI_TCP_WORK_TX,
> + ISCSI_TCP_WORK_TX_EAGAIN,
> +};
> +
> +struct iscsi_tcp_work {
> + /* list: connected to iscsi_tcp_work_list or iscsi_tcp_finished_list */
> + struct list_head list;
> +
> + enum iscsi_tcp_work_state state;
> +};
> +
> struct iscsi_tcp_connection {
> int fd;
>
> @@ -59,13 +74,197 @@ struct iscsi_tcp_connection {
> long ttt;
>
> struct iscsi_connection iscsi_conn;
> + struct iscsi_tcp_work work;
> +
> + int used_in_worker_thread;
> };
>
> +static LIST_HEAD(iscsi_tcp_work_list);
> +static pthread_mutex_t iscsi_tcp_work_mutex = PTHREAD_MUTEX_INITIALIZER;
> +static pthread_cond_t iscsi_tcp_work_cond = PTHREAD_COND_INITIALIZER;
> +
> +static LIST_HEAD(iscsi_tcp_work_finished_list);
> +static pthread_mutex_t iscsi_tcp_work_finished_mutex =
> + PTHREAD_MUTEX_INITIALIZER;
> +
> +static int iscsi_tcp_work_done_fds[2];
> +
> +static pthread_mutex_t iscsi_tcp_worker_startup_mutex =
> + PTHREAD_MUTEX_INITIALIZER;
> +
> +static int iscsi_tcp_worker_stop;
> +
> +static pthread_t *iscsi_tcp_worker_threads;
> +
> +static void iscsi_tcp_work_done_handler(int fd, int events, void *data)
> +{
> + LIST_HEAD(list);
> + struct iscsi_tcp_work *work;
> + struct iscsi_connection *conn;
> + struct iscsi_tcp_connection *tcp_conn;
> + int ret;
> + char dummy = 0;
> +
> + ret = read(fd, &dummy, sizeof(dummy));
> + if (ret != sizeof(dummy)) {
> + eprintf("iscsi tcp work error: %m\n");
> + exit(1);
> + }
> +
> + pthread_mutex_lock(&iscsi_tcp_work_finished_mutex);
> + list_splice_init(&iscsi_tcp_work_finished_list, &list);
> + pthread_mutex_unlock(&iscsi_tcp_work_finished_mutex);
> +
> + while (!list_empty(&list)) {
> + work = list_first_entry(&list, struct iscsi_tcp_work, list);
> + list_del(&work->list);
> +
> + tcp_conn =
> + container_of(work, struct iscsi_tcp_connection, work);
> + conn = &tcp_conn->iscsi_conn;
> +
> + tcp_conn->used_in_worker_thread = 0;
> +
> + ret = tgt_event_add(tcp_conn->fd, EPOLLOUT | EPOLLIN,
> + iscsi_tcp_event_handler, conn);
> + if (ret) {
> + eprintf("failed to add event: %m\n");
> + exit(1);
> + }
> +
> + switch (work->state) {
> + case ISCSI_TCP_WORK_RX:
> + if (conn->state != STATE_CLOSE)
> + iscsi_tcp_work_rx_done_fn(conn);
> + break;
> + case ISCSI_TCP_WORK_TX:
> + iscsi_tcp_work_tx_done_fn(conn);
> + break;
> + case ISCSI_TCP_WORK_TX_EAGAIN:
> + /* do nothing */
> + break;
> + default:
> + eprintf("invalid state of iscsi work tcp: %d\n",
> + work->state);
> + exit(1);
> + }
> +
> + if (conn->state == STATE_CLOSE) {
> + dprintf("connection closed %p\n", conn);
> + if (conn_close(conn))
> + /* reference count of the conn isn't zero */
> + work->state = ISCSI_TCP_WORK_INIT;
> + } else
> + work->state = ISCSI_TCP_WORK_INIT;
> + }
> +}
> +
> +static void *iscsi_tcp_worker_fn(void *arg)
> +{
> + sigset_t set;
> + struct iscsi_tcp_work *work;
> + struct iscsi_connection *conn;
> + struct iscsi_tcp_connection *tcp_conn;
> + int ret;
> + char dummy = 0;
> +
> + sigfillset(&set);
> + sigprocmask(SIG_BLOCK, &set, NULL);
> +
> + pthread_mutex_lock(&iscsi_tcp_worker_startup_mutex);
> + pthread_mutex_unlock(&iscsi_tcp_worker_startup_mutex);
> +
> + dprintf("starting iscsi tcp worker thread: %lu\n", pthread_self());
> +
> + while (!iscsi_tcp_worker_stop) {
> + pthread_mutex_lock(&iscsi_tcp_work_mutex);
> +retest:
> + if (list_empty(&iscsi_tcp_work_list)) {
> + pthread_cond_wait(&iscsi_tcp_work_cond,
> + &iscsi_tcp_work_mutex);
> +
> + if (iscsi_tcp_worker_stop) {
> + pthread_mutex_unlock(&iscsi_tcp_work_mutex);
> + pthread_exit(NULL);
> + }
> +
> + goto retest;
> + }
> +
> + work = list_first_entry(&iscsi_tcp_work_list,
> + struct iscsi_tcp_work, list);
> +
> + list_del(&work->list);
> + pthread_mutex_unlock(&iscsi_tcp_work_mutex);
> +
> + tcp_conn =
> + container_of(work, struct iscsi_tcp_connection, work);
> + conn = &tcp_conn->iscsi_conn;
> +
> + switch (work->state) {
> + case ISCSI_TCP_WORK_RX:
> + do {
> + iscsi_rx_handler(conn);
> + } while (!is_conn_rx_end(conn)
> + && conn->state != STATE_CLOSE);
> +
> + break;
> + case ISCSI_TCP_WORK_TX:
> + do {
> + ret = iscsi_tx_handler(conn);
> + if (ret == -EAGAIN) {
> + /* no data to send */
> + work->state = ISCSI_TCP_WORK_TX_EAGAIN;
> + break;
> + }
> + } while (!is_conn_tx_end(conn)
> + && conn->state != STATE_CLOSE);
> +
> + break;
> + default:
> + eprintf("invalid state of iscsi tcp work: %d\n",
> + work->state);
> + exit(1);
> + }
> +
> + pthread_mutex_lock(&iscsi_tcp_work_finished_mutex);
> + list_add_tail(&work->list, &iscsi_tcp_work_finished_list);
> + pthread_mutex_unlock(&iscsi_tcp_work_finished_mutex);
> +
> + ret = write(iscsi_tcp_work_done_fds[1], &dummy, sizeof(dummy));
> + if (ret != sizeof(dummy)) {
> + eprintf("iscsi tcp work error: %m\n");
> + exit(1);
> + }
> + }
> +
> + pthread_exit(NULL);
> +}
> +
> static inline struct iscsi_tcp_connection *TCP_CONN(struct iscsi_connection *conn)
> {
> return container_of(conn, struct iscsi_tcp_connection, iscsi_conn);
> }
>
> +static void queue_iscsi_tcp_work(struct iscsi_connection *conn)
> +{
> + struct iscsi_tcp_connection *tcp_conn = TCP_CONN(conn);
> + struct iscsi_tcp_work *work = &tcp_conn->work;
> +
> + /*
> + * Delete from main event loop temporaly, because the fd is used by
> + * worker threads.
> + */
> + tcp_conn->used_in_worker_thread = 1;
> + tgt_event_del(tcp_conn->fd);
> +
> + pthread_mutex_lock(&iscsi_tcp_work_mutex);
> + list_add_tail(&work->list, &iscsi_tcp_work_list);
> + pthread_mutex_unlock(&iscsi_tcp_work_mutex);
> +
> + pthread_cond_signal(&iscsi_tcp_work_cond);
> +}
> +
> static struct tgt_work nop_work;
>
> /* all iscsi connections */
> @@ -102,6 +301,9 @@ static void iscsi_tcp_nop_work_handler(void *data)
> if (tcp_conn->nop_tick > 0)
> continue;
>
> + if (tcp_conn->used_in_worker_thread)
> + continue;
> +
> tcp_conn->nop_tick = tcp_conn->nop_interval;
>
> tcp_conn->nop_inflight_count++;
> @@ -241,6 +443,9 @@ static void accept_connection(int afd, int events, void *data)
> if (!tcp_conn)
> goto out;
>
> + INIT_LIST_HEAD(&tcp_conn->work.list);
> + tcp_conn->work.state = ISCSI_TCP_WORK_INIT;
> +
> conn = &tcp_conn->iscsi_conn;
>
> ret = conn_init(conn);
> @@ -273,20 +478,20 @@ out:
> static void iscsi_tcp_event_handler(int fd, int events, void *data)
> {
> struct iscsi_connection *conn = (struct iscsi_connection *) data;
> + struct iscsi_tcp_connection *tcp_conn = TCP_CONN(conn);
> + struct iscsi_tcp_work *work = &tcp_conn->work;
>
> - if (events & EPOLLIN)
> - iscsi_rx_handler(conn);
> -
> - if (conn->state == STATE_CLOSE)
> - dprintf("connection closed\n");
> + if (work->state != ISCSI_TCP_WORK_INIT) {
> + eprintf("invalid state of work: %d\n", work->state);
> + exit(1);
> + }
>
> - if (conn->state != STATE_CLOSE && events & EPOLLOUT)
> - iscsi_tx_handler(conn);
> + if (events & EPOLLIN)
> + work->state = ISCSI_TCP_WORK_RX;
> + else if (events & EPOLLOUT)
> + work->state = ISCSI_TCP_WORK_TX;
>
> - if (conn->state == STATE_CLOSE) {
> - dprintf("connection closed %p\n", conn);
> - conn_close(conn);
> - }
> + queue_iscsi_tcp_work(conn);
> }
>
> int iscsi_tcp_init_portal(char *addr, int port, int tpgt)
> @@ -423,6 +628,8 @@ int iscsi_delete_portal(char *addr, int port)
>
> static int iscsi_tcp_init(void)
> {
> + int ret = 0, i;
> +
> /* If we were passed any portals on the command line */
> if (portal_arguments)
> iscsi_param_parse_portals(portal_arguments, 1, 0);
> @@ -440,17 +647,76 @@ static int iscsi_tcp_init(void)
> nop_work.data = &nop_work;
> add_work(&nop_work, 1);
>
> - return 0;
> + ret = pipe(iscsi_tcp_work_done_fds);
> + if (ret < 0) {
> + eprintf("failed to create pipe for tcp work: %m\n");
> + return -1;
> + }
> +
> + ret = tgt_event_add(iscsi_tcp_work_done_fds[0], EPOLLIN,
> + iscsi_tcp_work_done_handler, NULL);
> + if (ret < 0) {
> + eprintf("failed to register iscsi_tcp_work_done_handler():"\
> + " %m\n");
> + ret = -1;
> + goto close_done_fds;
> + }
> +
> + iscsi_tcp_worker_threads = calloc(nr_iothreads, sizeof(pthread_t));
> + if (!iscsi_tcp_worker_threads) {
> + eprintf("failed to allocate memory for pthread identifier:"\
> + " %m\n");
> + ret = -1;
> +
> + goto close_done_fds;
> + }
> +
> + pthread_mutex_lock(&iscsi_tcp_worker_startup_mutex);
> + for (i = 0; i < nr_iothreads; i++) {
> + ret = pthread_create(&iscsi_tcp_worker_threads[i], NULL,
> + iscsi_tcp_worker_fn, NULL);
> + if (ret) {
> + eprintf("creating worker thread failed: %m\n");
> + ret = -1;
> +
> + goto terminate_workers;
> + }
> + }
> +
> + pthread_mutex_unlock(&iscsi_tcp_worker_startup_mutex);
> + goto out;
> +
> +terminate_workers:
> + iscsi_tcp_worker_stop = 1;
> + pthread_mutex_unlock(&iscsi_tcp_worker_startup_mutex);
> +
> + for (; 0 <= i; i--)
> + pthread_join(iscsi_tcp_worker_threads[i], NULL);
> +
> +close_done_fds:
> + close(iscsi_tcp_work_done_fds[0]);
> + close(iscsi_tcp_work_done_fds[1]);
> +
> +out:
> + return ret;
> }
>
> static void iscsi_tcp_exit(void)
> {
> + int i;
> +
> struct iscsi_portal *portal, *ptmp;
>
> list_for_each_entry_safe(portal, ptmp, &iscsi_portals_list,
> iscsi_portal_siblings) {
> iscsi_delete_portal(portal->addr, portal->port);
> }
> +
> + iscsi_tcp_worker_stop = 1;
> + for (i = 0; i < nr_iothreads; i++) {
> + pthread_cond_signal(&iscsi_tcp_work_cond);
> + pthread_join(iscsi_tcp_worker_threads[i], NULL);
> + }
> }
>
> static int iscsi_tcp_conn_login_complete(struct iscsi_connection *conn)
> diff --git a/usr/iscsi/iscsid.c b/usr/iscsi/iscsid.c
> index 30bd13f..c936553 100644
> --- a/usr/iscsi/iscsid.c
> +++ b/usr/iscsi/iscsid.c
> @@ -33,6 +33,8 @@
> #include <unistd.h>
> #include <sys/epoll.h>
>
> +#include <pthread.h>
> +
> #include "iscsid.h"
> #include "tgtd.h"
> #include "util.h"
> @@ -76,6 +78,16 @@ enum {
> IOSTATE_TX_END,
> };
>
> +int is_conn_rx_end(struct iscsi_connection *conn)
> +{
> + return conn->rx_iostate == IOSTATE_RX_END;
> +}
> +
> +int is_conn_tx_end(struct iscsi_connection *conn)
> +{
> + return conn->tx_iostate == IOSTATE_TX_END;
> +}
> +
> void conn_read_pdu(struct iscsi_connection *conn)
> {
> conn->rx_iostate = IOSTATE_RX_BHS;
> @@ -1754,6 +1766,19 @@ static int iscsi_task_rx_start(struct iscsi_connection *conn)
> uint8_t op;
> int err = 0;
>
> + /*
> + * Below sequence should be executed in a sequential manner. Because
> + * this function is called by multiple threads (iscsi_tcp_worker_fn())
> + * and updates shared data structures related to iSCSI session (e.g.
> + * conn->session->cmd_list).
> + *
> + * TODO: more fine grained locking
> + */
> + static pthread_mutex_t iscsi_task_rx_start_lock =
> + PTHREAD_MUTEX_INITIALIZER;
> +
> + pthread_mutex_lock(&iscsi_task_rx_start_lock);
> +
> op = hdr->opcode & ISCSI_OPCODE_MASK;
> switch (op) {
> case ISCSI_OP_SCSI_CMD:
> @@ -1788,6 +1813,7 @@ static int iscsi_task_rx_start(struct iscsi_connection *conn)
> break;
> }
>
> + pthread_mutex_unlock(&iscsi_task_rx_start_lock);
> return err;
> }
>
> @@ -1976,8 +2002,21 @@ static int iscsi_task_tx_start(struct iscsi_connection *conn)
> struct iscsi_task *task;
> int is_rsp, err = 0;
>
> - if (list_empty(&conn->tx_clist))
> - goto nodata;
> + /*
> + * Below sequence should be executed in a sequential manner for the
> + * same reason of iscsi_task_rx_start().
> + *
> + * TODO: more fine grained locking
> + */
> + static pthread_mutex_t iscsi_task_tx_start_lock =
> + PTHREAD_MUTEX_INITIALIZER;
> +
> + pthread_mutex_lock(&iscsi_task_tx_start_lock);
> +
> + if (list_empty(&conn->tx_clist)) {
> + err = -EAGAIN;
> + goto ret;
> + }
>
> conn_write_pdu(conn);
>
> @@ -1998,8 +2037,10 @@ static int iscsi_task_tx_start(struct iscsi_connection *conn)
> break;
> case ISCSI_OP_NOOP_OUT:
> err = iscsi_noop_out_tx_start(task, &is_rsp);
> - if (!is_rsp)
> - goto nodata;
> + if (!is_rsp) {
> + err = -EAGAIN;
> + goto ret;
> + }
> break;
> case ISCSI_OP_LOGOUT:
> err = iscsi_logout_tx_start(task);
> @@ -2010,12 +2051,13 @@ static int iscsi_task_tx_start(struct iscsi_connection *conn)
> }
>
> conn->tx_task = task;
> - return err;
> +ret:
> + pthread_mutex_unlock(&iscsi_task_tx_start_lock);
>
> -nodata:
> - dprintf("no more data\n");
> - conn->tp->ep_event_modify(conn, EPOLLIN);
> - return -EAGAIN;
> + if (err == -EAGAIN)
> + dprintf("no more data\n");
> +
> + return err;
> }
>
> static int do_recv(struct iscsi_connection *conn, int next_state)
> @@ -2051,7 +2093,6 @@ void iscsi_rx_handler(struct iscsi_connection *conn)
> int ret = 0, hdigest, ddigest;
> uint32_t crc;
>
> -
> if (conn->state == STATE_SCSI) {
> struct param *p = conn->session_param;
> hdigest = p[ISCSI_PARAM_HDRDGST_EN].val & DIGEST_CRC32C;
> @@ -2182,9 +2223,8 @@ again:
> exit(1);
> }
>
> - if (ret < 0 ||
> - conn->rx_iostate != IOSTATE_RX_END ||
> - conn->state == STATE_CLOSE)
> + if (ret < 0 || conn->rx_iostate != IOSTATE_RX_END
> + || conn->state == STATE_CLOSE)
> return;
>
> if (conn->rx_size) {
> @@ -2192,20 +2232,6 @@ again:
> conn->rx_size);
> exit(1);
> }
> -
> - if (conn->state == STATE_SCSI) {
> - ret = iscsi_task_rx_done(conn);
> - if (ret)
> - conn->state = STATE_CLOSE;
> - else
> - conn_read_pdu(conn);
> - } else {
> - conn_write_pdu(conn);
> - conn->tp->ep_event_modify(conn, EPOLLOUT);
> - ret = cmnd_execute(conn);
> - if (ret)
> - conn->state = STATE_CLOSE;
> - }
> }
>
> static int do_send(struct iscsi_connection *conn, int next_state)
> @@ -2373,6 +2399,33 @@ again:
> finish:
> cmnd_finish(conn);
>
> +out:
> + return ret;
> +}
> +
> +void iscsi_tcp_work_rx_done_fn(struct iscsi_connection *conn)
> +{
> + int ret;
> +
> + if (conn->state == STATE_SCSI) {
> + ret = iscsi_task_rx_done(conn);
> + if (ret)
> + conn->state = STATE_CLOSE;
> + else
> + conn_read_pdu(conn);
> + } else {
> + conn_write_pdu(conn);
> + conn->tp->ep_event_modify(conn, EPOLLOUT);
> + ret = cmnd_execute(conn);
> + if (ret)
> + conn->state = STATE_CLOSE;
> + }
> +}
> +
> +void iscsi_tcp_work_tx_done_fn(struct iscsi_connection *conn)
> +{
> + int ret;
> +
> switch (conn->state) {
> case STATE_KERNEL:
> ret = conn_take_fd(conn);
> @@ -2395,9 +2448,6 @@ finish:
> conn->tp->ep_event_modify(conn, EPOLLIN);
> break;
> }
> -
> -out:
> - return ret;
> }
>
> int iscsi_transportid(int tid, uint64_t itn_id, char *buf, int size)
> diff --git a/usr/iscsi/iscsid.h b/usr/iscsi/iscsid.h
> index c7f6801..aabbeca 100644
> --- a/usr/iscsi/iscsid.h
> +++ b/usr/iscsi/iscsid.h
> @@ -309,8 +309,8 @@ extern int cmnd_exec_auth_chap(struct iscsi_connection *conn);
> /* conn.c */
> extern int conn_init(struct iscsi_connection *conn);
> extern void conn_exit(struct iscsi_connection *conn);
> -extern void conn_close(struct iscsi_connection *conn);
> -extern void conn_put(struct iscsi_connection *conn);
> +extern int conn_close(struct iscsi_connection *conn);
> +extern int conn_put(struct iscsi_connection *conn);
> extern int conn_get(struct iscsi_connection *conn);
> extern struct iscsi_connection * conn_find(struct iscsi_session *session, uint32_t cid);
> extern int conn_take_fd(struct iscsi_connection *conn);
> @@ -336,6 +336,10 @@ extern int iscsi_param_parse_portals(char *p, int do_add, int do_delete);
> extern void iscsi_update_conn_stats_rx(struct iscsi_connection *conn, int size, int opcode);
> extern void iscsi_update_conn_stats_tx(struct iscsi_connection *conn, int size, int opcode);
> extern void iscsi_rsp_set_residual(struct iscsi_cmd_rsp *rsp, struct scsi_cmd *scmd);
> +extern void iscsi_tcp_work_rx_done_fn(struct iscsi_connection *conn);
> +extern void iscsi_tcp_work_tx_done_fn(struct iscsi_connection *conn);
> +extern int is_conn_rx_end(struct iscsi_connection *conn);
> +extern int is_conn_tx_end(struct iscsi_connection *conn);
>
> /* iscsid.c iscsi_task */
> extern void iscsi_free_task(struct iscsi_task *task);
> --
> 1.8.1.2
>
--
To unsubscribe from this list: send the line "unsubscribe stgt" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
More information about the stgt
mailing list