- struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
- struct iscsi_conn *tmp = ERR_PTR(-EEXIST), *conn = cls_conn->dd_data;
- struct sock *sk;
- struct socket *sock;
- int err;
-
- /* lookup for existing socket */
- sock = sockfd_lookup(transport_fd, &err);
- if (!sock) {
- printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err);
- return -EEXIST;
- }
-
- /* lookup for existing connection */
- spin_lock_bh(&session->lock);
- list_for_each_entry(tmp, &session->connections, item) {
- if (tmp == conn) {
- if (conn->c_stage != ISCSI_CONN_STOPPED ||
- conn->stop_stage == STOP_CONN_TERM) {
- printk(KERN_ERR "iscsi_tcp: can't bind "
- "non-stopped connection (%d:%d)\n",
- conn->c_stage, conn->stop_stage);
- spin_unlock_bh(&session->lock);
- return -EIO;
- }
- break;
- }
- }
- if (tmp != conn) {
- /* bind new iSCSI connection to session */
- conn->session = session;
-
- list_add(&conn->item, &session->connections);
- }
- spin_unlock_bh(&session->lock);
-
- if (conn->stop_stage != STOP_CONN_SUSPEND) {
- /* bind iSCSI connection and socket */
- conn->sock = sock;
-
- /* setup Socket parameters */
- sk = sock->sk;
- sk->sk_reuse = 1;
- sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
- sk->sk_allocation = GFP_ATOMIC;
-
- /* FIXME: disable Nagle's algorithm */
-
- /*
- * Intercept TCP callbacks for sendfile like receive
- * processing.
- */
- iscsi_conn_set_callbacks(conn);
-
- conn->sendpage = conn->sock->ops->sendpage;
-
- /*
- * set receive state machine into initial state
- */
- conn->in_progress = IN_PROGRESS_WAIT_HEADER;
- }
-
- if (is_leading)
- session->leadconn = conn;
-
- /*
- * Unblock xmitworker(), Login Phase will pass through.
- */
- clear_bit(SUSPEND_BIT, &conn->suspend_rx);
- clear_bit(SUSPEND_BIT, &conn->suspend_tx);
-
- return 0;
-}
-
-static int
-iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
-{
- struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
- struct sock *sk;
-
- /* FF phase warming up... */
-
- if (session == NULL) {
- printk(KERN_ERR "iscsi_tcp: can't start unbound connection\n");
- return -EPERM;
- }
-
- sk = conn->sock->sk;
-
- write_lock_bh(&sk->sk_callback_lock);
- spin_lock_bh(&session->lock);
- conn->c_stage = ISCSI_CONN_STARTED;
- session->state = ISCSI_STATE_LOGGED_IN;
-
- switch(conn->stop_stage) {
- case STOP_CONN_RECOVER:
- /*
- * unblock eh_abort() if it is blocked. re-try all
- * commands after successful recovery
- */
- session->conn_cnt++;
- conn->stop_stage = 0;
- conn->tmabort_state = TMABORT_INITIAL;
- session->age++;
- wake_up(&conn->ehwait);
- break;
- case STOP_CONN_TERM:
- session->conn_cnt++;
- conn->stop_stage = 0;
- break;
- case STOP_CONN_SUSPEND:
- conn->stop_stage = 0;
- clear_bit(SUSPEND_BIT, &conn->suspend_rx);
- clear_bit(SUSPEND_BIT, &conn->suspend_tx);
- break;
- default:
- break;
- }
- spin_unlock_bh(&session->lock);
- write_unlock_bh(&sk->sk_callback_lock);
-
- return 0;
-}
-
-static void
-iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
-{
- struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
- struct sock *sk;
- unsigned long flags;
-
- BUG_ON(!conn->sock);
- sk = conn->sock->sk;
- write_lock_bh(&sk->sk_callback_lock);
- set_bit(SUSPEND_BIT, &conn->suspend_rx);
- write_unlock_bh(&sk->sk_callback_lock);
-
- mutex_lock(&conn->xmitmutex);
-
- spin_lock_irqsave(session->host->host_lock, flags);
- spin_lock(&session->lock);
- conn->stop_stage = flag;
- conn->c_stage = ISCSI_CONN_STOPPED;
- set_bit(SUSPEND_BIT, &conn->suspend_tx);
-
- if (flag != STOP_CONN_SUSPEND)
- session->conn_cnt--;
-
- if (session->conn_cnt == 0 || session->leadconn == conn)
- session->state = ISCSI_STATE_FAILED;
-
- spin_unlock(&session->lock);
- spin_unlock_irqrestore(session->host->host_lock, flags);
-
- if (flag == STOP_CONN_TERM || flag == STOP_CONN_RECOVER) {
- struct iscsi_cmd_task *ctask;
- struct iscsi_mgmt_task *mtask;
-
- /*
- * Socket must go now.
- */
- sock_hold(conn->sock->sk);
- iscsi_conn_restore_callbacks(conn);
- sock_put(conn->sock->sk);
-
- /*
- * flush xmit queues.
- */
- spin_lock_bh(&session->lock);
- while (__kfifo_get(conn->writequeue, (void*)&ctask,
- sizeof(void*)) ||
- __kfifo_get(conn->xmitqueue, (void*)&ctask,
- sizeof(void*))) {
- struct iscsi_r2t_info *r2t;
-
- /*
- * flush ctask's r2t queues
- */
- while (__kfifo_get(ctask->r2tqueue, (void*)&r2t,
- sizeof(void*)))
- __kfifo_put(ctask->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
-
- spin_unlock_bh(&session->lock);
- local_bh_disable();
- iscsi_ctask_cleanup(conn, ctask);
- local_bh_enable();
- spin_lock_bh(&session->lock);
- }
- conn->ctask = NULL;
- while (__kfifo_get(conn->immqueue, (void*)&mtask,
- sizeof(void*)) ||
- __kfifo_get(conn->mgmtqueue, (void*)&mtask,
- sizeof(void*))) {
- __kfifo_put(session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*));
- }
- conn->mtask = NULL;
- spin_unlock_bh(&session->lock);
-
- /*
- * release socket only after we stopped data_xmit()
- * activity and flushed all outstandings
- */
- sock_release(conn->sock);
- conn->sock = NULL;
-
- /*
- * for connection level recovery we should not calculate
- * header digest. conn->hdr_size used for optimization
- * in hdr_extract() and will be re-negotiated at
- * set_param() time.
- */
- if (flag == STOP_CONN_RECOVER) {
- conn->hdr_size = sizeof(struct iscsi_hdr);
- conn->hdrdgst_en = 0;
- conn->datadgst_en = 0;
- }
- }
- mutex_unlock(&conn->xmitmutex);
-}
-
-static int
-iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
- char *data, uint32_t data_size)
-{
- struct iscsi_session *session = conn->session;
- struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
- struct iscsi_mgmt_task *mtask;
-
- spin_lock_bh(&session->lock);
- if (session->state == ISCSI_STATE_TERMINATE) {
- spin_unlock_bh(&session->lock);
- return -EPERM;
- }
- if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
- hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
- /*
- * Login and Text are sent serially, in
- * request-followed-by-response sequence.
- * Same mtask can be used. Same ITT must be used.
- * Note that login_mtask is preallocated at conn_create().
- */
- mtask = conn->login_mtask;
- else {
- BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
- BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
-
- if (!__kfifo_get(session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*))) {
- spin_unlock_bh(&session->lock);
- return -ENOSPC;
- }
- }
-
- /*
- * pre-format CmdSN and ExpStatSN for outgoing PDU.
- */
- if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
- hdr->itt = mtask->itt | (conn->id << CID_SHIFT) |
- (session->age << AGE_SHIFT);
- nop->cmdsn = cpu_to_be32(session->cmdsn);
- if (conn->c_stage == ISCSI_CONN_STARTED &&
- !(hdr->opcode & ISCSI_OP_IMMEDIATE))
- session->cmdsn++;
- } else
- /* do not advance CmdSN */
- nop->cmdsn = cpu_to_be32(session->cmdsn);
-
- nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
-
- memcpy(&mtask->hdr, hdr, sizeof(struct iscsi_hdr));
-
- iscsi_buf_init_virt(&mtask->headbuf, (char*)&mtask->hdr,
- sizeof(struct iscsi_hdr));
-
- spin_unlock_bh(&session->lock);
-
- if (data_size) {
- memcpy(mtask->data, data, data_size);
- mtask->data_count = data_size;
- } else
- mtask->data_count = 0;
-
- mtask->xmstate = XMSTATE_IMM_HDR;
-
- if (mtask->data_count) {
- iscsi_buf_init_iov(&mtask->sendbuf, (char*)mtask->data,
- mtask->data_count);
- }
-
- debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
- hdr->opcode, hdr->itt, data_size);
-
- /*
- * since send_pdu() could be called at least from two contexts,
- * we need to serialize __kfifo_put, so we don't have to take
- * additional lock on fast data-path
- */
- if (hdr->opcode & ISCSI_OP_IMMEDIATE)
- __kfifo_put(conn->immqueue, (void*)&mtask, sizeof(void*));
- else
- __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*));
-
- scsi_queue_work(session->host, &conn->xmitwork);
- return 0;
-}
-
-static int
-iscsi_eh_host_reset(struct scsi_cmnd *sc)
-{
- struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
- struct iscsi_conn *conn = ctask->conn;
- struct iscsi_session *session = conn->session;
-
- spin_lock_bh(&session->lock);
- if (session->state == ISCSI_STATE_TERMINATE) {
- debug_scsi("failing host reset: session terminated "
- "[CID %d age %d]", conn->id, session->age);
- spin_unlock_bh(&session->lock);
- return FAILED;
- }
- spin_unlock_bh(&session->lock);
-
- debug_scsi("failing connection CID %d due to SCSI host reset "
- "[itt 0x%x age %d]", conn->id, ctask->itt,
- session->age);
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-
- return SUCCESS;
-}
-
-static void
-iscsi_tmabort_timedout(unsigned long data)
-{
- struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)data;
- struct iscsi_conn *conn = ctask->conn;
- struct iscsi_session *session = conn->session;
-
- spin_lock(&session->lock);
- if (conn->tmabort_state == TMABORT_INITIAL) {
- __kfifo_put(session->mgmtpool.queue,
- (void*)&ctask->mtask, sizeof(void*));
- conn->tmabort_state = TMABORT_TIMEDOUT;
- debug_scsi("tmabort timedout [sc %lx itt 0x%x]\n",
- (long)ctask->sc, ctask->itt);
- /* unblock eh_abort() */
- wake_up(&conn->ehwait);
- }
- spin_unlock(&session->lock);
-}
-
-static int
-iscsi_eh_abort(struct scsi_cmnd *sc)
-{
- int rc;
- struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
- struct iscsi_conn *conn = ctask->conn;
- struct iscsi_session *session = conn->session;
-
- conn->eh_abort_cnt++;
- debug_scsi("aborting [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
-
- /*
- * two cases for ERL=0 here:
- *
- * 1) connection-level failure;
- * 2) recovery due protocol error;
- */
- mutex_lock(&conn->xmitmutex);
- spin_lock_bh(&session->lock);
- if (session->state != ISCSI_STATE_LOGGED_IN) {
- if (session->state == ISCSI_STATE_TERMINATE) {
- spin_unlock_bh(&session->lock);
- mutex_unlock(&conn->xmitmutex);
- goto failed;
- }
- spin_unlock_bh(&session->lock);
- } else {
- struct iscsi_tm *hdr = &conn->tmhdr;
-
- /*
- * Still LOGGED_IN...
- */
-
- if (!ctask->sc || sc->SCp.phase != session->age) {
- /*
- * 1) ctask completed before time out. But session
- * is still ok => Happy Retry.
- * 2) session was re-open during time out of ctask.
- */
- spin_unlock_bh(&session->lock);
- mutex_unlock(&conn->xmitmutex);
- goto success;
- }
- conn->tmabort_state = TMABORT_INITIAL;
- spin_unlock_bh(&session->lock);
-
- /*
- * ctask timed out but session is OK
- * ERL=0 requires task mgmt abort to be issued on each
- * failed command. requests must be serialized.
- */
- memset(hdr, 0, sizeof(struct iscsi_tm));
- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
- hdr->flags = ISCSI_TM_FUNC_ABORT_TASK;
- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- memcpy(hdr->lun, ctask->hdr.lun, sizeof(hdr->lun));
- hdr->rtt = ctask->hdr.itt;
- hdr->refcmdsn = ctask->hdr.cmdsn;
-
- rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr,
- NULL, 0);
- if (rc) {
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- debug_scsi("abort sent failure [itt 0x%x]", ctask->itt);
- } else {
- struct iscsi_r2t_info *r2t;
-
- /*
- * TMF abort vs. TMF response race logic
- */
- spin_lock_bh(&session->lock);
- ctask->mtask = (struct iscsi_mgmt_task *)
- session->mgmt_cmds[(hdr->itt & ITT_MASK) -
- ISCSI_MGMT_ITT_OFFSET];
- /*
- * have to flush r2tqueue to avoid r2t leaks
- */
- while (__kfifo_get(ctask->r2tqueue, (void*)&r2t,
- sizeof(void*))) {
- __kfifo_put(ctask->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- }
- if (conn->tmabort_state == TMABORT_INITIAL) {
- conn->tmfcmd_pdus_cnt++;
- conn->tmabort_timer.expires = 3*HZ + jiffies;
- conn->tmabort_timer.function =
- iscsi_tmabort_timedout;
- conn->tmabort_timer.data = (unsigned long)ctask;
- add_timer(&conn->tmabort_timer);
- debug_scsi("abort sent [itt 0x%x]", ctask->itt);
- } else {
- if (!ctask->sc ||
- conn->tmabort_state == TMABORT_SUCCESS) {
- conn->tmabort_state = TMABORT_INITIAL;
- spin_unlock_bh(&session->lock);
- mutex_unlock(&conn->xmitmutex);
- goto success;
- }
- conn->tmabort_state = TMABORT_INITIAL;
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- }
- spin_unlock_bh(&session->lock);
- }
- }
- mutex_unlock(&conn->xmitmutex);
-
-
- /*
- * block eh thread until:
- *
- * 1) abort response;
- * 2) abort timeout;
- * 3) session re-opened;
- * 4) session terminated;
- */
- for (;;) {
- int p_state = session->state;
-
- rc = wait_event_interruptible(conn->ehwait,
- (p_state == ISCSI_STATE_LOGGED_IN ?
- (session->state == ISCSI_STATE_TERMINATE ||
- conn->tmabort_state != TMABORT_INITIAL) :
- (session->state == ISCSI_STATE_TERMINATE ||
- session->state == ISCSI_STATE_LOGGED_IN)));
- if (rc) {
- /* shutdown.. */
- session->state = ISCSI_STATE_TERMINATE;
- goto failed;
- }
-
- if (signal_pending(current))
- flush_signals(current);
-
- if (session->state == ISCSI_STATE_TERMINATE)
- goto failed;
-
- spin_lock_bh(&session->lock);
- if (sc->SCp.phase == session->age &&
- (conn->tmabort_state == TMABORT_TIMEDOUT ||
- conn->tmabort_state == TMABORT_FAILED)) {
- conn->tmabort_state = TMABORT_INITIAL;
- if (!ctask->sc) {
- /*
- * ctask completed before tmf abort response or
- * time out.
- * But session is still ok => Happy Retry.
- */
- spin_unlock_bh(&session->lock);
- break;
- }
- spin_unlock_bh(&session->lock);
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- continue;
- }
- spin_unlock_bh(&session->lock);
- break;
- }
-
-success:
- debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
- rc = SUCCESS;
- goto exit;
-
-failed:
- debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
- rc = FAILED;
-
-exit:
- del_timer_sync(&conn->tmabort_timer);
-
- mutex_lock(&conn->xmitmutex);
- if (conn->sock) {
- struct sock *sk = conn->sock->sk;
-
- write_lock_bh(&sk->sk_callback_lock);
- iscsi_ctask_cleanup(conn, ctask);
- write_unlock_bh(&sk->sk_callback_lock);
- }
- mutex_unlock(&conn->xmitmutex);
- return rc;
-}
-
-static int
-iscsi_r2tpool_alloc(struct iscsi_session *session)
-{
- int i;
- int cmd_i;
-
- /*
- * initialize per-task: R2T pool and xmit queue
- */
- for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
-
- /*
- * pre-allocated x4 as much r2ts to handle race when
- * target acks DataOut faster than we data_xmit() queues
- * could replenish r2tqueue.
- */
-
- /* R2T pool */
- if (iscsi_pool_init(&ctask->r2tpool, session->max_r2t * 4,
- (void***)&ctask->r2ts, sizeof(struct iscsi_r2t_info))) {
- goto r2t_alloc_fail;
- }
-
- /* R2T xmit queue */
- ctask->r2tqueue = kfifo_alloc(
- session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
- if (ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
- iscsi_pool_free(&ctask->r2tpool, (void**)ctask->r2ts);
- goto r2t_alloc_fail;
- }
-
- /*
- * number of
- * Data-Out PDU's within R2T-sequence can be quite big;
- * using mempool
- */
- ctask->datapool = mempool_create_slab_pool(ISCSI_DTASK_DEFAULT_MAX,
- taskcache);
- if (ctask->datapool == NULL) {
- kfifo_free(ctask->r2tqueue);
- iscsi_pool_free(&ctask->r2tpool, (void**)ctask->r2ts);
- goto r2t_alloc_fail;
- }
- INIT_LIST_HEAD(&ctask->dataqueue);
- }
-
- return 0;
-
-r2t_alloc_fail:
- for (i = 0; i < cmd_i; i++) {
- mempool_destroy(session->cmds[i]->datapool);
- kfifo_free(session->cmds[i]->r2tqueue);
- iscsi_pool_free(&session->cmds[i]->r2tpool,
- (void**)session->cmds[i]->r2ts);
- }
- return -ENOMEM;
-}
-
-static void
-iscsi_r2tpool_free(struct iscsi_session *session)
-{
- int i;
-
- for (i = 0; i < session->cmds_max; i++) {
- mempool_destroy(session->cmds[i]->datapool);
- kfifo_free(session->cmds[i]->r2tqueue);
- iscsi_pool_free(&session->cmds[i]->r2tpool,
- (void**)session->cmds[i]->r2ts);
- }
-}