17996a778SMike Christie /* 27996a778SMike Christie * iSCSI lib functions 37996a778SMike Christie * 47996a778SMike Christie * Copyright (C) 2006 Red Hat, Inc. All rights reserved. 57996a778SMike Christie * Copyright (C) 2004 - 2006 Mike Christie 67996a778SMike Christie * Copyright (C) 2004 - 2005 Dmitry Yusupov 77996a778SMike Christie * Copyright (C) 2004 - 2005 Alex Aizman 87996a778SMike Christie * maintained by open-iscsi@googlegroups.com 97996a778SMike Christie * 107996a778SMike Christie * This program is free software; you can redistribute it and/or modify 117996a778SMike Christie * it under the terms of the GNU General Public License as published by 127996a778SMike Christie * the Free Software Foundation; either version 2 of the License, or 137996a778SMike Christie * (at your option) any later version. 147996a778SMike Christie * 157996a778SMike Christie * This program is distributed in the hope that it will be useful, 167996a778SMike Christie * but WITHOUT ANY WARRANTY; without even the implied warranty of 177996a778SMike Christie * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 187996a778SMike Christie * GNU General Public License for more details. 197996a778SMike Christie * 207996a778SMike Christie * You should have received a copy of the GNU General Public License 217996a778SMike Christie * along with this program; if not, write to the Free Software 227996a778SMike Christie * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 237996a778SMike Christie */ 247996a778SMike Christie #include <linux/types.h> 257996a778SMike Christie #include <linux/mutex.h> 267996a778SMike Christie #include <linux/kfifo.h> 277996a778SMike Christie #include <linux/delay.h> 287996a778SMike Christie #include <net/tcp.h> 297996a778SMike Christie #include <scsi/scsi_cmnd.h> 307996a778SMike Christie #include <scsi/scsi_device.h> 317996a778SMike Christie #include <scsi/scsi_eh.h> 327996a778SMike Christie #include <scsi/scsi_tcq.h> 337996a778SMike Christie #include <scsi/scsi_host.h> 347996a778SMike Christie #include <scsi/scsi.h> 357996a778SMike Christie #include <scsi/iscsi_proto.h> 367996a778SMike Christie #include <scsi/scsi_transport.h> 377996a778SMike Christie #include <scsi/scsi_transport_iscsi.h> 387996a778SMike Christie #include <scsi/libiscsi.h> 397996a778SMike Christie 407996a778SMike Christie struct iscsi_session * 417996a778SMike Christie class_to_transport_session(struct iscsi_cls_session *cls_session) 427996a778SMike Christie { 437996a778SMike Christie struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); 447996a778SMike Christie return iscsi_hostdata(shost->hostdata); 457996a778SMike Christie } 467996a778SMike Christie EXPORT_SYMBOL_GPL(class_to_transport_session); 477996a778SMike Christie 487996a778SMike Christie #define INVALID_SN_DELTA 0xffff 497996a778SMike Christie 507996a778SMike Christie int 517996a778SMike Christie iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) 527996a778SMike Christie { 537996a778SMike Christie uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn); 547996a778SMike Christie uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn); 557996a778SMike Christie 567996a778SMike Christie if (max_cmdsn < exp_cmdsn -1 && 577996a778SMike Christie max_cmdsn > exp_cmdsn - INVALID_SN_DELTA) 587996a778SMike Christie return ISCSI_ERR_MAX_CMDSN; 597996a778SMike Christie if (max_cmdsn > session->max_cmdsn || 607996a778SMike Christie max_cmdsn < session->max_cmdsn - INVALID_SN_DELTA) 617996a778SMike Christie session->max_cmdsn = max_cmdsn; 627996a778SMike Christie if (exp_cmdsn > session->exp_cmdsn || 637996a778SMike Christie exp_cmdsn < session->exp_cmdsn - INVALID_SN_DELTA) 647996a778SMike Christie session->exp_cmdsn = exp_cmdsn; 657996a778SMike Christie 667996a778SMike Christie return 0; 677996a778SMike Christie } 687996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn); 697996a778SMike Christie 707996a778SMike Christie void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, 717996a778SMike Christie struct iscsi_data *hdr, 727996a778SMike Christie int transport_data_cnt) 737996a778SMike Christie { 747996a778SMike Christie struct iscsi_conn *conn = ctask->conn; 757996a778SMike Christie 767996a778SMike Christie memset(hdr, 0, sizeof(struct iscsi_data)); 777996a778SMike Christie hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); 787996a778SMike Christie hdr->datasn = cpu_to_be32(ctask->unsol_datasn); 797996a778SMike Christie ctask->unsol_datasn++; 807996a778SMike Christie hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; 817996a778SMike Christie memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); 827996a778SMike Christie 837996a778SMike Christie hdr->itt = ctask->hdr->itt; 847996a778SMike Christie hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); 857996a778SMike Christie 867996a778SMike Christie hdr->offset = cpu_to_be32(ctask->total_length - 877996a778SMike Christie transport_data_cnt - 887996a778SMike Christie ctask->unsol_count); 897996a778SMike Christie 907996a778SMike Christie if (ctask->unsol_count > conn->max_xmit_dlength) { 917996a778SMike Christie hton24(hdr->dlength, conn->max_xmit_dlength); 927996a778SMike Christie ctask->data_count = conn->max_xmit_dlength; 937996a778SMike Christie hdr->flags = 0; 947996a778SMike Christie } else { 957996a778SMike Christie hton24(hdr->dlength, ctask->unsol_count); 967996a778SMike Christie ctask->data_count = ctask->unsol_count; 977996a778SMike Christie hdr->flags = ISCSI_FLAG_CMD_FINAL; 987996a778SMike Christie } 997996a778SMike Christie } 1007996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu); 1017996a778SMike Christie 1027996a778SMike Christie /** 1037996a778SMike Christie * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu 1047996a778SMike Christie * @ctask: iscsi cmd task 1057996a778SMike Christie * 1067996a778SMike Christie * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set 1077996a778SMike Christie * fields like dlength or final based on how much data it sends 1087996a778SMike Christie */ 1097996a778SMike Christie static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) 1107996a778SMike Christie { 1117996a778SMike Christie struct iscsi_conn *conn = ctask->conn; 1127996a778SMike Christie struct iscsi_session *session = conn->session; 1137996a778SMike Christie struct iscsi_cmd *hdr = ctask->hdr; 1147996a778SMike Christie struct scsi_cmnd *sc = ctask->sc; 1157996a778SMike Christie 1167996a778SMike Christie hdr->opcode = ISCSI_OP_SCSI_CMD; 1177996a778SMike Christie hdr->flags = ISCSI_ATTR_SIMPLE; 1187996a778SMike Christie int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); 1197996a778SMike Christie hdr->itt = ctask->itt | (conn->id << ISCSI_CID_SHIFT) | 1207996a778SMike Christie (session->age << ISCSI_AGE_SHIFT); 1217996a778SMike Christie hdr->data_length = cpu_to_be32(sc->request_bufflen); 1227996a778SMike Christie hdr->cmdsn = cpu_to_be32(session->cmdsn); 1237996a778SMike Christie session->cmdsn++; 1247996a778SMike Christie hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); 1257996a778SMike Christie memcpy(hdr->cdb, sc->cmnd, sc->cmd_len); 1267996a778SMike Christie memset(&hdr->cdb[sc->cmd_len], 0, MAX_COMMAND_SIZE - sc->cmd_len); 1277996a778SMike Christie 1287996a778SMike Christie if (sc->sc_data_direction == DMA_TO_DEVICE) { 1297996a778SMike Christie hdr->flags |= ISCSI_FLAG_CMD_WRITE; 1307996a778SMike Christie /* 1317996a778SMike Christie * Write counters: 1327996a778SMike Christie * 1337996a778SMike Christie * imm_count bytes to be sent right after 1347996a778SMike Christie * SCSI PDU Header 1357996a778SMike Christie * 1367996a778SMike Christie * unsol_count bytes(as Data-Out) to be sent 1377996a778SMike Christie * without R2T ack right after 1387996a778SMike Christie * immediate data 1397996a778SMike Christie * 1407996a778SMike Christie * r2t_data_count bytes to be sent via R2T ack's 1417996a778SMike Christie * 1427996a778SMike Christie * pad_count bytes to be sent as zero-padding 1437996a778SMike Christie */ 1447996a778SMike Christie ctask->imm_count = 0; 1457996a778SMike Christie ctask->unsol_count = 0; 1467996a778SMike Christie ctask->unsol_datasn = 0; 1477996a778SMike Christie 1487996a778SMike Christie if (session->imm_data_en) { 1497996a778SMike Christie if (ctask->total_length >= session->first_burst) 1507996a778SMike Christie ctask->imm_count = min(session->first_burst, 1517996a778SMike Christie conn->max_xmit_dlength); 1527996a778SMike Christie else 1537996a778SMike Christie ctask->imm_count = min(ctask->total_length, 1547996a778SMike Christie conn->max_xmit_dlength); 1557996a778SMike Christie hton24(ctask->hdr->dlength, ctask->imm_count); 1567996a778SMike Christie } else 1577996a778SMike Christie zero_data(ctask->hdr->dlength); 1587996a778SMike Christie 1597996a778SMike Christie if (!session->initial_r2t_en) 1607996a778SMike Christie ctask->unsol_count = min(session->first_burst, 1617996a778SMike Christie ctask->total_length) - ctask->imm_count; 1627996a778SMike Christie if (!ctask->unsol_count) 1637996a778SMike Christie /* No unsolicit Data-Out's */ 1647996a778SMike Christie ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL; 1657996a778SMike Christie } else { 1667996a778SMike Christie ctask->datasn = 0; 1677996a778SMike Christie hdr->flags |= ISCSI_FLAG_CMD_FINAL; 1687996a778SMike Christie zero_data(hdr->dlength); 1697996a778SMike Christie 1707996a778SMike Christie if (sc->sc_data_direction == DMA_FROM_DEVICE) 1717996a778SMike Christie hdr->flags |= ISCSI_FLAG_CMD_READ; 1727996a778SMike Christie } 1737996a778SMike Christie 1747996a778SMike Christie conn->scsicmd_pdus_cnt++; 1757996a778SMike Christie } 1767996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu); 1777996a778SMike Christie 1787996a778SMike Christie /** 1797996a778SMike Christie * iscsi_complete_command - return command back to scsi-ml 1807996a778SMike Christie * @session: iscsi session 1817996a778SMike Christie * @ctask: iscsi cmd task 1827996a778SMike Christie * 1837996a778SMike Christie * Must be called with session lock. 1847996a778SMike Christie * This function returns the scsi command to scsi-ml and returns 1857996a778SMike Christie * the cmd task to the pool of available cmd tasks. 1867996a778SMike Christie */ 1877996a778SMike Christie static void iscsi_complete_command(struct iscsi_session *session, 1887996a778SMike Christie struct iscsi_cmd_task *ctask) 1897996a778SMike Christie { 1907996a778SMike Christie struct scsi_cmnd *sc = ctask->sc; 1917996a778SMike Christie 1927996a778SMike Christie ctask->sc = NULL; 1937996a778SMike Christie list_del_init(&ctask->running); 1947996a778SMike Christie __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); 1957996a778SMike Christie sc->scsi_done(sc); 1967996a778SMike Christie } 1977996a778SMike Christie 1987996a778SMike Christie /** 1997996a778SMike Christie * iscsi_cmd_rsp - SCSI Command Response processing 2007996a778SMike Christie * @conn: iscsi connection 2017996a778SMike Christie * @hdr: iscsi header 2027996a778SMike Christie * @ctask: scsi command task 2037996a778SMike Christie * @data: cmd data buffer 2047996a778SMike Christie * @datalen: len of buffer 2057996a778SMike Christie * 2067996a778SMike Christie * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and 2077996a778SMike Christie * then completes the command and task. 2087996a778SMike Christie **/ 2097996a778SMike Christie static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 2107996a778SMike Christie struct iscsi_cmd_task *ctask, char *data, 2117996a778SMike Christie int datalen) 2127996a778SMike Christie { 2137996a778SMike Christie int rc; 2147996a778SMike Christie struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr; 2157996a778SMike Christie struct iscsi_session *session = conn->session; 2167996a778SMike Christie struct scsi_cmnd *sc = ctask->sc; 2177996a778SMike Christie 2187996a778SMike Christie rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr); 2197996a778SMike Christie if (rc) { 2207996a778SMike Christie sc->result = DID_ERROR << 16; 2217996a778SMike Christie goto out; 2227996a778SMike Christie } 2237996a778SMike Christie 2247996a778SMike Christie conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; 2257996a778SMike Christie 2267996a778SMike Christie sc->result = (DID_OK << 16) | rhdr->cmd_status; 2277996a778SMike Christie 2287996a778SMike Christie if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) { 2297996a778SMike Christie sc->result = DID_ERROR << 16; 2307996a778SMike Christie goto out; 2317996a778SMike Christie } 2327996a778SMike Christie 2337996a778SMike Christie if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) { 2347996a778SMike Christie int senselen; 2357996a778SMike Christie 2367996a778SMike Christie if (datalen < 2) { 2377996a778SMike Christie invalid_datalen: 238be2df72eSOr Gerlitz printk(KERN_ERR "iscsi: Got CHECK_CONDITION but " 239be2df72eSOr Gerlitz "invalid data buffer size of %d\n", datalen); 2407996a778SMike Christie sc->result = DID_BAD_TARGET << 16; 2417996a778SMike Christie goto out; 2427996a778SMike Christie } 2437996a778SMike Christie 2447996a778SMike Christie senselen = (data[0] << 8) | data[1]; 2457996a778SMike Christie if (datalen < senselen) 2467996a778SMike Christie goto invalid_datalen; 2477996a778SMike Christie 2487996a778SMike Christie memcpy(sc->sense_buffer, data + 2, 2497996a778SMike Christie min(senselen, SCSI_SENSE_BUFFERSIZE)); 2507996a778SMike Christie debug_scsi("copied %d bytes of sense\n", 2517996a778SMike Christie min(senselen, SCSI_SENSE_BUFFERSIZE)); 2527996a778SMike Christie } 2537996a778SMike Christie 2547996a778SMike Christie if (sc->sc_data_direction == DMA_TO_DEVICE) 2557996a778SMike Christie goto out; 2567996a778SMike Christie 2577996a778SMike Christie if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) { 2587996a778SMike Christie int res_count = be32_to_cpu(rhdr->residual_count); 2597996a778SMike Christie 2607996a778SMike Christie if (res_count > 0 && res_count <= sc->request_bufflen) 2617996a778SMike Christie sc->resid = res_count; 2627996a778SMike Christie else 2637996a778SMike Christie sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 2647996a778SMike Christie } else if (rhdr->flags & ISCSI_FLAG_CMD_BIDI_UNDERFLOW) 2657996a778SMike Christie sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 2667996a778SMike Christie else if (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW) 2677996a778SMike Christie sc->resid = be32_to_cpu(rhdr->residual_count); 2687996a778SMike Christie 2697996a778SMike Christie out: 2707996a778SMike Christie debug_scsi("done [sc %lx res %d itt 0x%x]\n", 2717996a778SMike Christie (long)sc, sc->result, ctask->itt); 2727996a778SMike Christie conn->scsirsp_pdus_cnt++; 2737996a778SMike Christie 2747996a778SMike Christie iscsi_complete_command(conn->session, ctask); 2757996a778SMike Christie return rc; 2767996a778SMike Christie } 2777996a778SMike Christie 2787996a778SMike Christie /** 2797996a778SMike Christie * __iscsi_complete_pdu - complete pdu 2807996a778SMike Christie * @conn: iscsi conn 2817996a778SMike Christie * @hdr: iscsi header 2827996a778SMike Christie * @data: data buffer 2837996a778SMike Christie * @datalen: len of data buffer 2847996a778SMike Christie * 2857996a778SMike Christie * Completes pdu processing by freeing any resources allocated at 2867996a778SMike Christie * queuecommand or send generic. session lock must be held and verify 2877996a778SMike Christie * itt must have been called. 2887996a778SMike Christie */ 2897996a778SMike Christie int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 2907996a778SMike Christie char *data, int datalen) 2917996a778SMike Christie { 2927996a778SMike Christie struct iscsi_session *session = conn->session; 2937996a778SMike Christie int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; 2947996a778SMike Christie struct iscsi_cmd_task *ctask; 2957996a778SMike Christie struct iscsi_mgmt_task *mtask; 2967996a778SMike Christie uint32_t itt; 2977996a778SMike Christie 2987996a778SMike Christie if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) 2997996a778SMike Christie itt = hdr->itt & ISCSI_ITT_MASK; 3007996a778SMike Christie else 3017996a778SMike Christie itt = hdr->itt; 3027996a778SMike Christie 3037996a778SMike Christie if (itt < session->cmds_max) { 3047996a778SMike Christie ctask = session->cmds[itt]; 3057996a778SMike Christie 3067996a778SMike Christie debug_scsi("cmdrsp [op 0x%x cid %d itt 0x%x len %d]\n", 3077996a778SMike Christie opcode, conn->id, ctask->itt, datalen); 3087996a778SMike Christie 3097996a778SMike Christie switch(opcode) { 3107996a778SMike Christie case ISCSI_OP_SCSI_CMD_RSP: 3117996a778SMike Christie BUG_ON((void*)ctask != ctask->sc->SCp.ptr); 3127996a778SMike Christie rc = iscsi_scsi_cmd_rsp(conn, hdr, ctask, data, 3137996a778SMike Christie datalen); 3147996a778SMike Christie break; 3157996a778SMike Christie case ISCSI_OP_SCSI_DATA_IN: 3167996a778SMike Christie BUG_ON((void*)ctask != ctask->sc->SCp.ptr); 3177996a778SMike Christie if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { 3187996a778SMike Christie conn->scsirsp_pdus_cnt++; 3197996a778SMike Christie iscsi_complete_command(session, ctask); 3207996a778SMike Christie } 3217996a778SMike Christie break; 3227996a778SMike Christie case ISCSI_OP_R2T: 3237996a778SMike Christie /* LLD handles this for now */ 3247996a778SMike Christie break; 3257996a778SMike Christie default: 3267996a778SMike Christie rc = ISCSI_ERR_BAD_OPCODE; 3277996a778SMike Christie break; 3287996a778SMike Christie } 3297996a778SMike Christie } else if (itt >= ISCSI_MGMT_ITT_OFFSET && 3307996a778SMike Christie itt < ISCSI_MGMT_ITT_OFFSET + session->mgmtpool_max) { 3317996a778SMike Christie mtask = session->mgmt_cmds[itt - ISCSI_MGMT_ITT_OFFSET]; 3327996a778SMike Christie 3337996a778SMike Christie debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n", 3347996a778SMike Christie opcode, conn->id, mtask->itt, datalen); 3357996a778SMike Christie 3367996a778SMike Christie rc = iscsi_check_assign_cmdsn(session, 3377996a778SMike Christie (struct iscsi_nopin*)hdr); 3387996a778SMike Christie if (rc) 3398d2860b3SMike Christie goto done; 3407996a778SMike Christie 3418d2860b3SMike Christie switch(opcode) { 3428d2860b3SMike Christie case ISCSI_OP_LOGOUT_RSP: 3438d2860b3SMike Christie conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 3448d2860b3SMike Christie /* fall through */ 3458d2860b3SMike Christie case ISCSI_OP_LOGIN_RSP: 3468d2860b3SMike Christie case ISCSI_OP_TEXT_RSP: 3478d2860b3SMike Christie /* 3488d2860b3SMike Christie * login related PDU's exp_statsn is handled in 3498d2860b3SMike Christie * userspace 3508d2860b3SMike Christie */ 3517996a778SMike Christie rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen); 3527996a778SMike Christie list_del(&mtask->running); 3537996a778SMike Christie if (conn->login_mtask != mtask) 3547996a778SMike Christie __kfifo_put(session->mgmtpool.queue, 3557996a778SMike Christie (void*)&mtask, sizeof(void*)); 3567996a778SMike Christie break; 3577996a778SMike Christie case ISCSI_OP_SCSI_TMFUNC_RSP: 3587996a778SMike Christie if (datalen) { 3597996a778SMike Christie rc = ISCSI_ERR_PROTO; 3607996a778SMike Christie break; 3617996a778SMike Christie } 3628d2860b3SMike Christie 3638d2860b3SMike Christie conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 3647996a778SMike Christie conn->tmfrsp_pdus_cnt++; 3657996a778SMike Christie if (conn->tmabort_state == TMABORT_INITIAL) { 3667996a778SMike Christie conn->tmabort_state = 3677996a778SMike Christie ((struct iscsi_tm_rsp *)hdr)-> 3687996a778SMike Christie response == ISCSI_TMF_RSP_COMPLETE ? 3697996a778SMike Christie TMABORT_SUCCESS:TMABORT_FAILED; 3707996a778SMike Christie /* unblock eh_abort() */ 3717996a778SMike Christie wake_up(&conn->ehwait); 3727996a778SMike Christie } 3737996a778SMike Christie break; 3747996a778SMike Christie case ISCSI_OP_NOOP_IN: 3757996a778SMike Christie if (hdr->ttt != ISCSI_RESERVED_TAG) { 3767996a778SMike Christie rc = ISCSI_ERR_PROTO; 3777996a778SMike Christie break; 3787996a778SMike Christie } 3797996a778SMike Christie conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 3807996a778SMike Christie 3817996a778SMike Christie rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen); 3827996a778SMike Christie list_del(&mtask->running); 3837996a778SMike Christie if (conn->login_mtask != mtask) 3847996a778SMike Christie __kfifo_put(session->mgmtpool.queue, 3857996a778SMike Christie (void*)&mtask, sizeof(void*)); 3867996a778SMike Christie break; 3877996a778SMike Christie default: 3887996a778SMike Christie rc = ISCSI_ERR_BAD_OPCODE; 3897996a778SMike Christie break; 3907996a778SMike Christie } 3917996a778SMike Christie } else if (itt == ISCSI_RESERVED_TAG) { 3927996a778SMike Christie switch(opcode) { 3937996a778SMike Christie case ISCSI_OP_NOOP_IN: 3947996a778SMike Christie if (!datalen) { 3957996a778SMike Christie rc = iscsi_check_assign_cmdsn(session, 3967996a778SMike Christie (struct iscsi_nopin*)hdr); 3977996a778SMike Christie if (!rc && hdr->ttt != ISCSI_RESERVED_TAG) 3987996a778SMike Christie rc = iscsi_recv_pdu(conn->cls_conn, 3997996a778SMike Christie hdr, NULL, 0); 4007996a778SMike Christie } else 4017996a778SMike Christie rc = ISCSI_ERR_PROTO; 4027996a778SMike Christie break; 4037996a778SMike Christie case ISCSI_OP_REJECT: 4047996a778SMike Christie /* we need sth like iscsi_reject_rsp()*/ 4057996a778SMike Christie case ISCSI_OP_ASYNC_EVENT: 4068d2860b3SMike Christie conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 4077996a778SMike Christie /* we need sth like iscsi_async_event_rsp() */ 4087996a778SMike Christie rc = ISCSI_ERR_BAD_OPCODE; 4097996a778SMike Christie break; 4107996a778SMike Christie default: 4117996a778SMike Christie rc = ISCSI_ERR_BAD_OPCODE; 4127996a778SMike Christie break; 4137996a778SMike Christie } 4147996a778SMike Christie } else 4157996a778SMike Christie rc = ISCSI_ERR_BAD_ITT; 4167996a778SMike Christie 4178d2860b3SMike Christie done: 4187996a778SMike Christie return rc; 4197996a778SMike Christie } 4207996a778SMike Christie EXPORT_SYMBOL_GPL(__iscsi_complete_pdu); 4217996a778SMike Christie 4227996a778SMike Christie int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 4237996a778SMike Christie char *data, int datalen) 4247996a778SMike Christie { 4257996a778SMike Christie int rc; 4267996a778SMike Christie 4277996a778SMike Christie spin_lock(&conn->session->lock); 4287996a778SMike Christie rc = __iscsi_complete_pdu(conn, hdr, data, datalen); 4297996a778SMike Christie spin_unlock(&conn->session->lock); 4307996a778SMike Christie return rc; 4317996a778SMike Christie } 4327996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_complete_pdu); 4337996a778SMike Christie 4347996a778SMike Christie /* verify itt (itt encoding: age+cid+itt) */ 4357996a778SMike Christie int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 4367996a778SMike Christie uint32_t *ret_itt) 4377996a778SMike Christie { 4387996a778SMike Christie struct iscsi_session *session = conn->session; 4397996a778SMike Christie struct iscsi_cmd_task *ctask; 4407996a778SMike Christie uint32_t itt; 4417996a778SMike Christie 4427996a778SMike Christie if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) { 4437996a778SMike Christie if ((hdr->itt & ISCSI_AGE_MASK) != 4447996a778SMike Christie (session->age << ISCSI_AGE_SHIFT)) { 445be2df72eSOr Gerlitz printk(KERN_ERR "iscsi: received itt %x expected " 4467996a778SMike Christie "session age (%x)\n", hdr->itt, 4477996a778SMike Christie session->age & ISCSI_AGE_MASK); 4487996a778SMike Christie return ISCSI_ERR_BAD_ITT; 4497996a778SMike Christie } 4507996a778SMike Christie 4517996a778SMike Christie if ((hdr->itt & ISCSI_CID_MASK) != 4527996a778SMike Christie (conn->id << ISCSI_CID_SHIFT)) { 453be2df72eSOr Gerlitz printk(KERN_ERR "iscsi: received itt %x, expected " 4547996a778SMike Christie "CID (%x)\n", hdr->itt, conn->id); 4557996a778SMike Christie return ISCSI_ERR_BAD_ITT; 4567996a778SMike Christie } 4577996a778SMike Christie itt = hdr->itt & ISCSI_ITT_MASK; 4587996a778SMike Christie } else 4597996a778SMike Christie itt = hdr->itt; 4607996a778SMike Christie 4617996a778SMike Christie if (itt < session->cmds_max) { 4627996a778SMike Christie ctask = session->cmds[itt]; 4637996a778SMike Christie 4647996a778SMike Christie if (!ctask->sc) { 465be2df72eSOr Gerlitz printk(KERN_INFO "iscsi: dropping ctask with " 4667996a778SMike Christie "itt 0x%x\n", ctask->itt); 4677996a778SMike Christie /* force drop */ 4687996a778SMike Christie return ISCSI_ERR_NO_SCSI_CMD; 4697996a778SMike Christie } 4707996a778SMike Christie 4717996a778SMike Christie if (ctask->sc->SCp.phase != session->age) { 472be2df72eSOr Gerlitz printk(KERN_ERR "iscsi: ctask's session age %d, " 4737996a778SMike Christie "expected %d\n", ctask->sc->SCp.phase, 4747996a778SMike Christie session->age); 4757996a778SMike Christie return ISCSI_ERR_SESSION_FAILED; 4767996a778SMike Christie } 4777996a778SMike Christie } 4787996a778SMike Christie 4797996a778SMike Christie *ret_itt = itt; 4807996a778SMike Christie return 0; 4817996a778SMike Christie } 4827996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_verify_itt); 4837996a778SMike Christie 4847996a778SMike Christie void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) 4857996a778SMike Christie { 4867996a778SMike Christie struct iscsi_session *session = conn->session; 4877996a778SMike Christie unsigned long flags; 4887996a778SMike Christie 4897996a778SMike Christie spin_lock_irqsave(&session->lock, flags); 490656cffc9SMike Christie if (session->state == ISCSI_STATE_FAILED) { 491656cffc9SMike Christie spin_unlock_irqrestore(&session->lock, flags); 492656cffc9SMike Christie return; 493656cffc9SMike Christie } 494656cffc9SMike Christie 49567a61114SMike Christie if (conn->stop_stage == 0) 4967996a778SMike Christie session->state = ISCSI_STATE_FAILED; 4977996a778SMike Christie spin_unlock_irqrestore(&session->lock, flags); 4987996a778SMike Christie set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 4997996a778SMike Christie set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); 5007996a778SMike Christie iscsi_conn_error(conn->cls_conn, err); 5017996a778SMike Christie } 5027996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_conn_failure); 5037996a778SMike Christie 5047996a778SMike Christie /** 5057996a778SMike Christie * iscsi_data_xmit - xmit any command into the scheduled connection 5067996a778SMike Christie * @conn: iscsi connection 5077996a778SMike Christie * 5087996a778SMike Christie * Notes: 5097996a778SMike Christie * The function can return -EAGAIN in which case the caller must 5107996a778SMike Christie * re-schedule it again later or recover. '0' return code means 5117996a778SMike Christie * successful xmit. 5127996a778SMike Christie **/ 5137996a778SMike Christie static int iscsi_data_xmit(struct iscsi_conn *conn) 5147996a778SMike Christie { 5157996a778SMike Christie struct iscsi_transport *tt; 5167996a778SMike Christie 5177996a778SMike Christie if (unlikely(conn->suspend_tx)) { 5187996a778SMike Christie debug_scsi("conn %d Tx suspended!\n", conn->id); 5197996a778SMike Christie return 0; 5207996a778SMike Christie } 5217996a778SMike Christie tt = conn->session->tt; 5227996a778SMike Christie 5237996a778SMike Christie /* 5247996a778SMike Christie * Transmit in the following order: 5257996a778SMike Christie * 5267996a778SMike Christie * 1) un-finished xmit (ctask or mtask) 5277996a778SMike Christie * 2) immediate control PDUs 5287996a778SMike Christie * 3) write data 5297996a778SMike Christie * 4) SCSI commands 5307996a778SMike Christie * 5) non-immediate control PDUs 5317996a778SMike Christie * 5327996a778SMike Christie * No need to lock around __kfifo_get as long as 5337996a778SMike Christie * there's one producer and one consumer. 5347996a778SMike Christie */ 5357996a778SMike Christie 5367996a778SMike Christie BUG_ON(conn->ctask && conn->mtask); 5377996a778SMike Christie 5387996a778SMike Christie if (conn->ctask) { 5397996a778SMike Christie if (tt->xmit_cmd_task(conn, conn->ctask)) 5407996a778SMike Christie goto again; 5417996a778SMike Christie /* done with this in-progress ctask */ 5427996a778SMike Christie conn->ctask = NULL; 5437996a778SMike Christie } 5447996a778SMike Christie if (conn->mtask) { 5457996a778SMike Christie if (tt->xmit_mgmt_task(conn, conn->mtask)) 5467996a778SMike Christie goto again; 5477996a778SMike Christie /* done with this in-progress mtask */ 5487996a778SMike Christie conn->mtask = NULL; 5497996a778SMike Christie } 5507996a778SMike Christie 5517996a778SMike Christie /* process immediate first */ 5527996a778SMike Christie if (unlikely(__kfifo_len(conn->immqueue))) { 5537996a778SMike Christie while (__kfifo_get(conn->immqueue, (void*)&conn->mtask, 5547996a778SMike Christie sizeof(void*))) { 555994442e8SMike Christie spin_lock_bh(&conn->session->lock); 5567996a778SMike Christie list_add_tail(&conn->mtask->running, 5577996a778SMike Christie &conn->mgmt_run_list); 558994442e8SMike Christie spin_unlock_bh(&conn->session->lock); 5597996a778SMike Christie if (tt->xmit_mgmt_task(conn, conn->mtask)) 5607996a778SMike Christie goto again; 5617996a778SMike Christie } 5627996a778SMike Christie /* done with this mtask */ 5637996a778SMike Christie conn->mtask = NULL; 5647996a778SMike Christie } 5657996a778SMike Christie 5667996a778SMike Christie /* process command queue */ 5677996a778SMike Christie while (__kfifo_get(conn->xmitqueue, (void*)&conn->ctask, 5687996a778SMike Christie sizeof(void*))) { 5697996a778SMike Christie /* 5707996a778SMike Christie * iscsi tcp may readd the task to the xmitqueue to send 5717996a778SMike Christie * write data 5727996a778SMike Christie */ 573994442e8SMike Christie spin_lock_bh(&conn->session->lock); 5747996a778SMike Christie if (list_empty(&conn->ctask->running)) 5757996a778SMike Christie list_add_tail(&conn->ctask->running, &conn->run_list); 576994442e8SMike Christie spin_unlock_bh(&conn->session->lock); 5777996a778SMike Christie if (tt->xmit_cmd_task(conn, conn->ctask)) 5787996a778SMike Christie goto again; 5797996a778SMike Christie } 5807996a778SMike Christie /* done with this ctask */ 5817996a778SMike Christie conn->ctask = NULL; 5827996a778SMike Christie 5837996a778SMike Christie /* process the rest control plane PDUs, if any */ 5847996a778SMike Christie if (unlikely(__kfifo_len(conn->mgmtqueue))) { 5857996a778SMike Christie while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask, 5867996a778SMike Christie sizeof(void*))) { 587994442e8SMike Christie spin_lock_bh(&conn->session->lock); 5887996a778SMike Christie list_add_tail(&conn->mtask->running, 5897996a778SMike Christie &conn->mgmt_run_list); 590994442e8SMike Christie spin_unlock_bh(&conn->session->lock); 5917996a778SMike Christie if (tt->xmit_mgmt_task(conn, conn->mtask)) 5927996a778SMike Christie goto again; 5937996a778SMike Christie } 5947996a778SMike Christie /* done with this mtask */ 5957996a778SMike Christie conn->mtask = NULL; 5967996a778SMike Christie } 5977996a778SMike Christie 5987996a778SMike Christie return 0; 5997996a778SMike Christie 6007996a778SMike Christie again: 6017996a778SMike Christie if (unlikely(conn->suspend_tx)) 6027996a778SMike Christie return 0; 6037996a778SMike Christie 6047996a778SMike Christie return -EAGAIN; 6057996a778SMike Christie } 6067996a778SMike Christie 6077996a778SMike Christie static void iscsi_xmitworker(void *data) 6087996a778SMike Christie { 6097996a778SMike Christie struct iscsi_conn *conn = data; 6107996a778SMike Christie 6117996a778SMike Christie /* 6127996a778SMike Christie * serialize Xmit worker on a per-connection basis. 6137996a778SMike Christie */ 6147996a778SMike Christie mutex_lock(&conn->xmitmutex); 6157996a778SMike Christie if (iscsi_data_xmit(conn)) 6167996a778SMike Christie scsi_queue_work(conn->session->host, &conn->xmitwork); 6177996a778SMike Christie mutex_unlock(&conn->xmitmutex); 6187996a778SMike Christie } 6197996a778SMike Christie 6207996a778SMike Christie enum { 6217996a778SMike Christie FAILURE_BAD_HOST = 1, 6227996a778SMike Christie FAILURE_SESSION_FAILED, 6237996a778SMike Christie FAILURE_SESSION_FREED, 6247996a778SMike Christie FAILURE_WINDOW_CLOSED, 6257996a778SMike Christie FAILURE_SESSION_TERMINATE, 626656cffc9SMike Christie FAILURE_SESSION_IN_RECOVERY, 6277996a778SMike Christie FAILURE_SESSION_RECOVERY_TIMEOUT, 6287996a778SMike Christie }; 6297996a778SMike Christie 6307996a778SMike Christie int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) 6317996a778SMike Christie { 6327996a778SMike Christie struct Scsi_Host *host; 6337996a778SMike Christie int reason = 0; 6347996a778SMike Christie struct iscsi_session *session; 6357996a778SMike Christie struct iscsi_conn *conn; 6367996a778SMike Christie struct iscsi_cmd_task *ctask = NULL; 6377996a778SMike Christie 6387996a778SMike Christie sc->scsi_done = done; 6397996a778SMike Christie sc->result = 0; 6407996a778SMike Christie 6417996a778SMike Christie host = sc->device->host; 6427996a778SMike Christie session = iscsi_hostdata(host->hostdata); 6437996a778SMike Christie 6447996a778SMike Christie spin_lock(&session->lock); 6457996a778SMike Christie 646656cffc9SMike Christie /* 647656cffc9SMike Christie * ISCSI_STATE_FAILED is a temp. state. The recovery 648656cffc9SMike Christie * code will decide what is best to do with command queued 649656cffc9SMike Christie * during this time 650656cffc9SMike Christie */ 651656cffc9SMike Christie if (session->state != ISCSI_STATE_LOGGED_IN && 652656cffc9SMike Christie session->state != ISCSI_STATE_FAILED) { 653656cffc9SMike Christie /* 654656cffc9SMike Christie * to handle the race between when we set the recovery state 655656cffc9SMike Christie * and block the session we requeue here (commands could 656656cffc9SMike Christie * be entering our queuecommand while a block is starting 657656cffc9SMike Christie * up because the block code is not locked) 658656cffc9SMike Christie */ 659656cffc9SMike Christie if (session->state == ISCSI_STATE_IN_RECOVERY) { 660656cffc9SMike Christie reason = FAILURE_SESSION_IN_RECOVERY; 66167a61114SMike Christie goto reject; 6627996a778SMike Christie } 663656cffc9SMike Christie 664656cffc9SMike Christie if (session->state == ISCSI_STATE_RECOVERY_FAILED) 665656cffc9SMike Christie reason = FAILURE_SESSION_RECOVERY_TIMEOUT; 666656cffc9SMike Christie else if (session->state == ISCSI_STATE_TERMINATE) 667656cffc9SMike Christie reason = FAILURE_SESSION_TERMINATE; 668656cffc9SMike Christie else 6697996a778SMike Christie reason = FAILURE_SESSION_FREED; 6707996a778SMike Christie goto fault; 6717996a778SMike Christie } 6727996a778SMike Christie 6737996a778SMike Christie /* 6747996a778SMike Christie * Check for iSCSI window and take care of CmdSN wrap-around 6757996a778SMike Christie */ 6767996a778SMike Christie if ((int)(session->max_cmdsn - session->cmdsn) < 0) { 6777996a778SMike Christie reason = FAILURE_WINDOW_CLOSED; 6787996a778SMike Christie goto reject; 6797996a778SMike Christie } 6807996a778SMike Christie 6817996a778SMike Christie conn = session->leadconn; 6827996a778SMike Christie 6837996a778SMike Christie __kfifo_get(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); 6847996a778SMike Christie sc->SCp.phase = session->age; 6857996a778SMike Christie sc->SCp.ptr = (char *)ctask; 6867996a778SMike Christie 6877996a778SMike Christie ctask->mtask = NULL; 6887996a778SMike Christie ctask->conn = conn; 6897996a778SMike Christie ctask->sc = sc; 6907996a778SMike Christie INIT_LIST_HEAD(&ctask->running); 6917996a778SMike Christie ctask->total_length = sc->request_bufflen; 6927996a778SMike Christie iscsi_prep_scsi_cmd_pdu(ctask); 6937996a778SMike Christie 6947996a778SMike Christie session->tt->init_cmd_task(ctask); 6957996a778SMike Christie 6967996a778SMike Christie __kfifo_put(conn->xmitqueue, (void*)&ctask, sizeof(void*)); 6977996a778SMike Christie debug_scsi( 6987996a778SMike Christie "ctask enq [%s cid %d sc %lx itt 0x%x len %d cmdsn %d win %d]\n", 6997996a778SMike Christie sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", 7007996a778SMike Christie conn->id, (long)sc, ctask->itt, sc->request_bufflen, 7017996a778SMike Christie session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); 7027996a778SMike Christie spin_unlock(&session->lock); 7037996a778SMike Christie 7047996a778SMike Christie scsi_queue_work(host, &conn->xmitwork); 7057996a778SMike Christie return 0; 7067996a778SMike Christie 7077996a778SMike Christie reject: 7087996a778SMike Christie spin_unlock(&session->lock); 7097996a778SMike Christie debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); 7107996a778SMike Christie return SCSI_MLQUEUE_HOST_BUSY; 7117996a778SMike Christie 7127996a778SMike Christie fault: 7137996a778SMike Christie spin_unlock(&session->lock); 714be2df72eSOr Gerlitz printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n", 7157996a778SMike Christie sc->cmnd[0], reason); 7167996a778SMike Christie sc->result = (DID_NO_CONNECT << 16); 7177996a778SMike Christie sc->resid = sc->request_bufflen; 7187996a778SMike Christie sc->scsi_done(sc); 7197996a778SMike Christie return 0; 7207996a778SMike Christie } 7217996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_queuecommand); 7227996a778SMike Christie 7237996a778SMike Christie int iscsi_change_queue_depth(struct scsi_device *sdev, int depth) 7247996a778SMike Christie { 7257996a778SMike Christie if (depth > ISCSI_MAX_CMD_PER_LUN) 7267996a778SMike Christie depth = ISCSI_MAX_CMD_PER_LUN; 7277996a778SMike Christie scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); 7287996a778SMike Christie return sdev->queue_depth; 7297996a778SMike Christie } 7307996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_change_queue_depth); 7317996a778SMike Christie 7327996a778SMike Christie static int 7337996a778SMike Christie iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 7347996a778SMike Christie char *data, uint32_t data_size) 7357996a778SMike Christie { 7367996a778SMike Christie struct iscsi_session *session = conn->session; 7377996a778SMike Christie struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; 7387996a778SMike Christie struct iscsi_mgmt_task *mtask; 7397996a778SMike Christie 7407996a778SMike Christie spin_lock_bh(&session->lock); 7417996a778SMike Christie if (session->state == ISCSI_STATE_TERMINATE) { 7427996a778SMike Christie spin_unlock_bh(&session->lock); 7437996a778SMike Christie return -EPERM; 7447996a778SMike Christie } 7457996a778SMike Christie if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || 7467996a778SMike Christie hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) 7477996a778SMike Christie /* 7487996a778SMike Christie * Login and Text are sent serially, in 7497996a778SMike Christie * request-followed-by-response sequence. 7507996a778SMike Christie * Same mtask can be used. Same ITT must be used. 7517996a778SMike Christie * Note that login_mtask is preallocated at conn_create(). 7527996a778SMike Christie */ 7537996a778SMike Christie mtask = conn->login_mtask; 7547996a778SMike Christie else { 7557996a778SMike Christie BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); 7567996a778SMike Christie BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); 7577996a778SMike Christie 7588d2860b3SMike Christie nop->exp_statsn = cpu_to_be32(conn->exp_statsn); 7597996a778SMike Christie if (!__kfifo_get(session->mgmtpool.queue, 7607996a778SMike Christie (void*)&mtask, sizeof(void*))) { 7617996a778SMike Christie spin_unlock_bh(&session->lock); 7627996a778SMike Christie return -ENOSPC; 7637996a778SMike Christie } 7647996a778SMike Christie } 7657996a778SMike Christie 7667996a778SMike Christie /* 7678d2860b3SMike Christie * pre-format CmdSN for outgoing PDU. 7687996a778SMike Christie */ 7697996a778SMike Christie if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) { 7707996a778SMike Christie hdr->itt = mtask->itt | (conn->id << ISCSI_CID_SHIFT) | 7717996a778SMike Christie (session->age << ISCSI_AGE_SHIFT); 7727996a778SMike Christie nop->cmdsn = cpu_to_be32(session->cmdsn); 7737996a778SMike Christie if (conn->c_stage == ISCSI_CONN_STARTED && 7747996a778SMike Christie !(hdr->opcode & ISCSI_OP_IMMEDIATE)) 7757996a778SMike Christie session->cmdsn++; 7767996a778SMike Christie } else 7777996a778SMike Christie /* do not advance CmdSN */ 7787996a778SMike Christie nop->cmdsn = cpu_to_be32(session->cmdsn); 7797996a778SMike Christie 7807996a778SMike Christie if (data_size) { 7817996a778SMike Christie memcpy(mtask->data, data, data_size); 7827996a778SMike Christie mtask->data_count = data_size; 7837996a778SMike Christie } else 7847996a778SMike Christie mtask->data_count = 0; 7857996a778SMike Christie 7867996a778SMike Christie INIT_LIST_HEAD(&mtask->running); 7877996a778SMike Christie memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr)); 7887996a778SMike Christie if (session->tt->init_mgmt_task) 7897996a778SMike Christie session->tt->init_mgmt_task(conn, mtask, data, data_size); 7907996a778SMike Christie spin_unlock_bh(&session->lock); 7917996a778SMike Christie 7927996a778SMike Christie debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n", 7937996a778SMike Christie hdr->opcode, hdr->itt, data_size); 7947996a778SMike Christie 7957996a778SMike Christie /* 7967996a778SMike Christie * since send_pdu() could be called at least from two contexts, 7977996a778SMike Christie * we need to serialize __kfifo_put, so we don't have to take 7987996a778SMike Christie * additional lock on fast data-path 7997996a778SMike Christie */ 8007996a778SMike Christie if (hdr->opcode & ISCSI_OP_IMMEDIATE) 8017996a778SMike Christie __kfifo_put(conn->immqueue, (void*)&mtask, sizeof(void*)); 8027996a778SMike Christie else 8037996a778SMike Christie __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*)); 8047996a778SMike Christie 8057996a778SMike Christie scsi_queue_work(session->host, &conn->xmitwork); 8067996a778SMike Christie return 0; 8077996a778SMike Christie } 8087996a778SMike Christie 8097996a778SMike Christie int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, 8107996a778SMike Christie char *data, uint32_t data_size) 8117996a778SMike Christie { 8127996a778SMike Christie struct iscsi_conn *conn = cls_conn->dd_data; 8137996a778SMike Christie int rc; 8147996a778SMike Christie 8157996a778SMike Christie mutex_lock(&conn->xmitmutex); 8167996a778SMike Christie rc = iscsi_conn_send_generic(conn, hdr, data, data_size); 8177996a778SMike Christie mutex_unlock(&conn->xmitmutex); 8187996a778SMike Christie 8197996a778SMike Christie return rc; 8207996a778SMike Christie } 8217996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); 8227996a778SMike Christie 8237996a778SMike Christie void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) 8247996a778SMike Christie { 8257996a778SMike Christie struct iscsi_session *session = class_to_transport_session(cls_session); 8267996a778SMike Christie struct iscsi_conn *conn = session->leadconn; 8277996a778SMike Christie 8287996a778SMike Christie spin_lock_bh(&session->lock); 8297996a778SMike Christie if (session->state != ISCSI_STATE_LOGGED_IN) { 830656cffc9SMike Christie session->state = ISCSI_STATE_RECOVERY_FAILED; 8317996a778SMike Christie if (conn) 8327996a778SMike Christie wake_up(&conn->ehwait); 8337996a778SMike Christie } 8347996a778SMike Christie spin_unlock_bh(&session->lock); 8357996a778SMike Christie } 8367996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); 8377996a778SMike Christie 8387996a778SMike Christie int iscsi_eh_host_reset(struct scsi_cmnd *sc) 8397996a778SMike Christie { 8407996a778SMike Christie struct Scsi_Host *host = sc->device->host; 8417996a778SMike Christie struct iscsi_session *session = iscsi_hostdata(host->hostdata); 8427996a778SMike Christie struct iscsi_conn *conn = session->leadconn; 8437996a778SMike Christie int fail_session = 0; 8447996a778SMike Christie 8457996a778SMike Christie spin_lock_bh(&session->lock); 8467996a778SMike Christie if (session->state == ISCSI_STATE_TERMINATE) { 8477996a778SMike Christie failed: 8487996a778SMike Christie debug_scsi("failing host reset: session terminated " 8497996a778SMike Christie "[CID %d age %d]", conn->id, session->age); 8507996a778SMike Christie spin_unlock_bh(&session->lock); 8517996a778SMike Christie return FAILED; 8527996a778SMike Christie } 8537996a778SMike Christie 8547996a778SMike Christie if (sc->SCp.phase == session->age) { 8557996a778SMike Christie debug_scsi("failing connection CID %d due to SCSI host reset", 8567996a778SMike Christie conn->id); 8577996a778SMike Christie fail_session = 1; 8587996a778SMike Christie } 8597996a778SMike Christie spin_unlock_bh(&session->lock); 8607996a778SMike Christie 8617996a778SMike Christie /* 8627996a778SMike Christie * we drop the lock here but the leadconn cannot be destoyed while 8637996a778SMike Christie * we are in the scsi eh 8647996a778SMike Christie */ 865656cffc9SMike Christie if (fail_session) 8667996a778SMike Christie iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); 8677996a778SMike Christie 8687996a778SMike Christie debug_scsi("iscsi_eh_host_reset wait for relogin\n"); 8697996a778SMike Christie wait_event_interruptible(conn->ehwait, 8707996a778SMike Christie session->state == ISCSI_STATE_TERMINATE || 8717996a778SMike Christie session->state == ISCSI_STATE_LOGGED_IN || 872656cffc9SMike Christie session->state == ISCSI_STATE_RECOVERY_FAILED); 8737996a778SMike Christie if (signal_pending(current)) 8747996a778SMike Christie flush_signals(current); 8757996a778SMike Christie 8767996a778SMike Christie spin_lock_bh(&session->lock); 8777996a778SMike Christie if (session->state == ISCSI_STATE_LOGGED_IN) 878be2df72eSOr Gerlitz printk(KERN_INFO "iscsi: host reset succeeded\n"); 8797996a778SMike Christie else 8807996a778SMike Christie goto failed; 8817996a778SMike Christie spin_unlock_bh(&session->lock); 8827996a778SMike Christie 8837996a778SMike Christie return SUCCESS; 8847996a778SMike Christie } 8857996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_eh_host_reset); 8867996a778SMike Christie 8877996a778SMike Christie static void iscsi_tmabort_timedout(unsigned long data) 8887996a778SMike Christie { 8897996a778SMike Christie struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)data; 8907996a778SMike Christie struct iscsi_conn *conn = ctask->conn; 8917996a778SMike Christie struct iscsi_session *session = conn->session; 8927996a778SMike Christie 8937996a778SMike Christie spin_lock(&session->lock); 8947996a778SMike Christie if (conn->tmabort_state == TMABORT_INITIAL) { 8957996a778SMike Christie conn->tmabort_state = TMABORT_TIMEDOUT; 8967996a778SMike Christie debug_scsi("tmabort timedout [sc %p itt 0x%x]\n", 8977996a778SMike Christie ctask->sc, ctask->itt); 8987996a778SMike Christie /* unblock eh_abort() */ 8997996a778SMike Christie wake_up(&conn->ehwait); 9007996a778SMike Christie } 9017996a778SMike Christie spin_unlock(&session->lock); 9027996a778SMike Christie } 9037996a778SMike Christie 9047996a778SMike Christie /* must be called with the mutex lock */ 9057996a778SMike Christie static int iscsi_exec_abort_task(struct scsi_cmnd *sc, 9067996a778SMike Christie struct iscsi_cmd_task *ctask) 9077996a778SMike Christie { 9087996a778SMike Christie struct iscsi_conn *conn = ctask->conn; 9097996a778SMike Christie struct iscsi_session *session = conn->session; 9107996a778SMike Christie struct iscsi_tm *hdr = &conn->tmhdr; 9117996a778SMike Christie int rc; 9127996a778SMike Christie 9137996a778SMike Christie /* 9147996a778SMike Christie * ctask timed out but session is OK requests must be serialized. 9157996a778SMike Christie */ 9167996a778SMike Christie memset(hdr, 0, sizeof(struct iscsi_tm)); 9177996a778SMike Christie hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; 9187996a778SMike Christie hdr->flags = ISCSI_TM_FUNC_ABORT_TASK; 9197996a778SMike Christie hdr->flags |= ISCSI_FLAG_CMD_FINAL; 9207996a778SMike Christie memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); 9217996a778SMike Christie hdr->rtt = ctask->hdr->itt; 9227996a778SMike Christie hdr->refcmdsn = ctask->hdr->cmdsn; 9237996a778SMike Christie 9247996a778SMike Christie rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr, 9257996a778SMike Christie NULL, 0); 9267996a778SMike Christie if (rc) { 9277996a778SMike Christie iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); 9287996a778SMike Christie debug_scsi("abort sent failure [itt 0x%x] %d", ctask->itt, rc); 9297996a778SMike Christie return rc; 9307996a778SMike Christie } 9317996a778SMike Christie 9327996a778SMike Christie debug_scsi("abort sent [itt 0x%x]\n", ctask->itt); 9337996a778SMike Christie 9347996a778SMike Christie spin_lock_bh(&session->lock); 9357996a778SMike Christie ctask->mtask = (struct iscsi_mgmt_task *) 9367996a778SMike Christie session->mgmt_cmds[(hdr->itt & ISCSI_ITT_MASK) - 9377996a778SMike Christie ISCSI_MGMT_ITT_OFFSET]; 9387996a778SMike Christie 9397996a778SMike Christie if (conn->tmabort_state == TMABORT_INITIAL) { 9407996a778SMike Christie conn->tmfcmd_pdus_cnt++; 9417996a778SMike Christie conn->tmabort_timer.expires = 10*HZ + jiffies; 9427996a778SMike Christie conn->tmabort_timer.function = iscsi_tmabort_timedout; 9437996a778SMike Christie conn->tmabort_timer.data = (unsigned long)ctask; 9447996a778SMike Christie add_timer(&conn->tmabort_timer); 9457996a778SMike Christie debug_scsi("abort set timeout [itt 0x%x]", ctask->itt); 9467996a778SMike Christie } 9477996a778SMike Christie spin_unlock_bh(&session->lock); 9487996a778SMike Christie mutex_unlock(&conn->xmitmutex); 9497996a778SMike Christie 9507996a778SMike Christie /* 9517996a778SMike Christie * block eh thread until: 9527996a778SMike Christie * 9537996a778SMike Christie * 1) abort response 9547996a778SMike Christie * 2) abort timeout 9557996a778SMike Christie * 3) session is terminated or restarted or userspace has 9567996a778SMike Christie * given up on recovery 9577996a778SMike Christie */ 9587996a778SMike Christie wait_event_interruptible(conn->ehwait, 9597996a778SMike Christie sc->SCp.phase != session->age || 9607996a778SMike Christie session->state != ISCSI_STATE_LOGGED_IN || 961656cffc9SMike Christie conn->tmabort_state != TMABORT_INITIAL); 9627996a778SMike Christie if (signal_pending(current)) 9637996a778SMike Christie flush_signals(current); 9647996a778SMike Christie del_timer_sync(&conn->tmabort_timer); 9657996a778SMike Christie 9667996a778SMike Christie mutex_lock(&conn->xmitmutex); 9677996a778SMike Christie return 0; 9687996a778SMike Christie } 9697996a778SMike Christie 9707996a778SMike Christie /* 9717996a778SMike Christie * xmit mutex and session lock must be held 9727996a778SMike Christie */ 9737996a778SMike Christie #define iscsi_remove_task(tasktype) \ 9747996a778SMike Christie static struct iscsi_##tasktype * \ 9757996a778SMike Christie iscsi_remove_##tasktype(struct kfifo *fifo, uint32_t itt) \ 9767996a778SMike Christie { \ 9777996a778SMike Christie int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*); \ 9787996a778SMike Christie struct iscsi_##tasktype *task; \ 9797996a778SMike Christie \ 9807996a778SMike Christie debug_scsi("searching %d tasks\n", nr_tasks); \ 9817996a778SMike Christie \ 9827996a778SMike Christie for (i = 0; i < nr_tasks; i++) { \ 9837996a778SMike Christie __kfifo_get(fifo, (void*)&task, sizeof(void*)); \ 9847996a778SMike Christie debug_scsi("check task %u\n", task->itt); \ 9857996a778SMike Christie \ 9867996a778SMike Christie if (task->itt == itt) { \ 9877996a778SMike Christie debug_scsi("matched task\n"); \ 9887996a778SMike Christie break; \ 9897996a778SMike Christie } \ 9907996a778SMike Christie \ 9917996a778SMike Christie __kfifo_put(fifo, (void*)&task, sizeof(void*)); \ 9927996a778SMike Christie } \ 9937996a778SMike Christie return NULL; \ 9947996a778SMike Christie } 9957996a778SMike Christie 9967996a778SMike Christie iscsi_remove_task(mgmt_task); 9977996a778SMike Christie iscsi_remove_task(cmd_task); 9987996a778SMike Christie 9997996a778SMike Christie static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask) 10007996a778SMike Christie { 10017996a778SMike Christie struct iscsi_conn *conn = ctask->conn; 10027996a778SMike Christie struct iscsi_session *session = conn->session; 10037996a778SMike Christie 10047996a778SMike Christie if (!ctask->mtask) 10057996a778SMike Christie return -EINVAL; 10067996a778SMike Christie 10077996a778SMike Christie if (!iscsi_remove_mgmt_task(conn->immqueue, ctask->mtask->itt)) 10087996a778SMike Christie list_del(&ctask->mtask->running); 10097996a778SMike Christie __kfifo_put(session->mgmtpool.queue, (void*)&ctask->mtask, 10107996a778SMike Christie sizeof(void*)); 10117996a778SMike Christie ctask->mtask = NULL; 10127996a778SMike Christie return 0; 10137996a778SMike Christie } 10147996a778SMike Christie 10157996a778SMike Christie /* 10167996a778SMike Christie * session lock and xmitmutex must be held 10177996a778SMike Christie */ 10187996a778SMike Christie static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, 10197996a778SMike Christie int err) 10207996a778SMike Christie { 10217996a778SMike Christie struct scsi_cmnd *sc; 10227996a778SMike Christie 10237996a778SMike Christie conn->session->tt->cleanup_cmd_task(conn, ctask); 10247996a778SMike Christie iscsi_ctask_mtask_cleanup(ctask); 10257996a778SMike Christie 10267996a778SMike Christie sc = ctask->sc; 10277996a778SMike Christie if (!sc) 10287996a778SMike Christie return; 10297996a778SMike Christie sc->result = err; 10307996a778SMike Christie sc->resid = sc->request_bufflen; 10317996a778SMike Christie iscsi_complete_command(conn->session, ctask); 10327996a778SMike Christie } 10337996a778SMike Christie 10347996a778SMike Christie int iscsi_eh_abort(struct scsi_cmnd *sc) 10357996a778SMike Christie { 10367996a778SMike Christie struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; 10377996a778SMike Christie struct iscsi_conn *conn = ctask->conn; 10387996a778SMike Christie struct iscsi_session *session = conn->session; 10397996a778SMike Christie struct iscsi_cmd_task *pending_ctask; 10407996a778SMike Christie int rc; 10417996a778SMike Christie 10427996a778SMike Christie conn->eh_abort_cnt++; 10437996a778SMike Christie debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt); 10447996a778SMike Christie 10457996a778SMike Christie mutex_lock(&conn->xmitmutex); 10467996a778SMike Christie spin_lock_bh(&session->lock); 10477996a778SMike Christie 10487996a778SMike Christie /* 10497996a778SMike Christie * If we are not logged in or we have started a new session 10507996a778SMike Christie * then let the host reset code handle this 10517996a778SMike Christie */ 10527996a778SMike Christie if (session->state != ISCSI_STATE_LOGGED_IN || 10537996a778SMike Christie sc->SCp.phase != session->age) 10547996a778SMike Christie goto failed; 10557996a778SMike Christie 10567996a778SMike Christie /* ctask completed before time out */ 10577996a778SMike Christie if (!ctask->sc) 10587996a778SMike Christie goto success; 10597996a778SMike Christie 10607996a778SMike Christie /* what should we do here ? */ 10617996a778SMike Christie if (conn->ctask == ctask) { 1062be2df72eSOr Gerlitz printk(KERN_INFO "iscsi: sc %p itt 0x%x partially sent. " 1063be2df72eSOr Gerlitz "Failing abort\n", sc, ctask->itt); 10647996a778SMike Christie goto failed; 10657996a778SMike Christie } 10667996a778SMike Christie 10677996a778SMike Christie /* check for the easy pending cmd abort */ 10687996a778SMike Christie pending_ctask = iscsi_remove_cmd_task(conn->xmitqueue, ctask->itt); 10697996a778SMike Christie if (pending_ctask) { 10707996a778SMike Christie /* iscsi_tcp queues write transfers on the xmitqueue */ 10717996a778SMike Christie if (list_empty(&pending_ctask->running)) { 10727996a778SMike Christie debug_scsi("found pending task\n"); 10737996a778SMike Christie goto success; 10747996a778SMike Christie } else 10757996a778SMike Christie __kfifo_put(conn->xmitqueue, (void*)&pending_ctask, 10767996a778SMike Christie sizeof(void*)); 10777996a778SMike Christie } 10787996a778SMike Christie 10797996a778SMike Christie conn->tmabort_state = TMABORT_INITIAL; 10807996a778SMike Christie 10817996a778SMike Christie spin_unlock_bh(&session->lock); 10827996a778SMike Christie rc = iscsi_exec_abort_task(sc, ctask); 10837996a778SMike Christie spin_lock_bh(&session->lock); 10847996a778SMike Christie 10857996a778SMike Christie iscsi_ctask_mtask_cleanup(ctask); 10867996a778SMike Christie if (rc || sc->SCp.phase != session->age || 10877996a778SMike Christie session->state != ISCSI_STATE_LOGGED_IN) 10887996a778SMike Christie goto failed; 10897996a778SMike Christie 10907996a778SMike Christie /* ctask completed before tmf abort response */ 10917996a778SMike Christie if (!ctask->sc) { 10927996a778SMike Christie debug_scsi("sc completed while abort in progress\n"); 10937996a778SMike Christie goto success; 10947996a778SMike Christie } 10957996a778SMike Christie 10967996a778SMike Christie if (conn->tmabort_state != TMABORT_SUCCESS) { 10977996a778SMike Christie spin_unlock_bh(&session->lock); 10987996a778SMike Christie iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); 10997996a778SMike Christie spin_lock_bh(&session->lock); 11007996a778SMike Christie goto failed; 11017996a778SMike Christie } 11027996a778SMike Christie 11037996a778SMike Christie success: 11047996a778SMike Christie debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); 11057996a778SMike Christie spin_unlock_bh(&session->lock); 11067996a778SMike Christie 11077996a778SMike Christie /* 11087996a778SMike Christie * clean up task if aborted. we have the xmitmutex so grab 11097996a778SMike Christie * the recv lock as a writer 11107996a778SMike Christie */ 11117996a778SMike Christie write_lock_bh(conn->recv_lock); 11127996a778SMike Christie spin_lock(&session->lock); 11137996a778SMike Christie fail_command(conn, ctask, DID_ABORT << 16); 11147996a778SMike Christie spin_unlock(&session->lock); 11157996a778SMike Christie write_unlock_bh(conn->recv_lock); 11167996a778SMike Christie 11177996a778SMike Christie mutex_unlock(&conn->xmitmutex); 11187996a778SMike Christie return SUCCESS; 11197996a778SMike Christie 11207996a778SMike Christie failed: 11217996a778SMike Christie spin_unlock_bh(&session->lock); 11227996a778SMike Christie mutex_unlock(&conn->xmitmutex); 11237996a778SMike Christie 11247996a778SMike Christie debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); 11257996a778SMike Christie return FAILED; 11267996a778SMike Christie } 11277996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_eh_abort); 11287996a778SMike Christie 11297996a778SMike Christie int 11307996a778SMike Christie iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size) 11317996a778SMike Christie { 11327996a778SMike Christie int i; 11337996a778SMike Christie 11347996a778SMike Christie *items = kmalloc(max * sizeof(void*), GFP_KERNEL); 11357996a778SMike Christie if (*items == NULL) 11367996a778SMike Christie return -ENOMEM; 11377996a778SMike Christie 11387996a778SMike Christie q->max = max; 11397996a778SMike Christie q->pool = kmalloc(max * sizeof(void*), GFP_KERNEL); 11407996a778SMike Christie if (q->pool == NULL) { 11417996a778SMike Christie kfree(*items); 11427996a778SMike Christie return -ENOMEM; 11437996a778SMike Christie } 11447996a778SMike Christie 11457996a778SMike Christie q->queue = kfifo_init((void*)q->pool, max * sizeof(void*), 11467996a778SMike Christie GFP_KERNEL, NULL); 11477996a778SMike Christie if (q->queue == ERR_PTR(-ENOMEM)) { 11487996a778SMike Christie kfree(q->pool); 11497996a778SMike Christie kfree(*items); 11507996a778SMike Christie return -ENOMEM; 11517996a778SMike Christie } 11527996a778SMike Christie 11537996a778SMike Christie for (i = 0; i < max; i++) { 11547996a778SMike Christie q->pool[i] = kmalloc(item_size, GFP_KERNEL); 11557996a778SMike Christie if (q->pool[i] == NULL) { 11567996a778SMike Christie int j; 11577996a778SMike Christie 11587996a778SMike Christie for (j = 0; j < i; j++) 11597996a778SMike Christie kfree(q->pool[j]); 11607996a778SMike Christie 11617996a778SMike Christie kfifo_free(q->queue); 11627996a778SMike Christie kfree(q->pool); 11637996a778SMike Christie kfree(*items); 11647996a778SMike Christie return -ENOMEM; 11657996a778SMike Christie } 11667996a778SMike Christie memset(q->pool[i], 0, item_size); 11677996a778SMike Christie (*items)[i] = q->pool[i]; 11687996a778SMike Christie __kfifo_put(q->queue, (void*)&q->pool[i], sizeof(void*)); 11697996a778SMike Christie } 11707996a778SMike Christie return 0; 11717996a778SMike Christie } 11727996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_pool_init); 11737996a778SMike Christie 11747996a778SMike Christie void iscsi_pool_free(struct iscsi_queue *q, void **items) 11757996a778SMike Christie { 11767996a778SMike Christie int i; 11777996a778SMike Christie 11787996a778SMike Christie for (i = 0; i < q->max; i++) 11797996a778SMike Christie kfree(items[i]); 11807996a778SMike Christie kfree(q->pool); 11817996a778SMike Christie kfree(items); 11827996a778SMike Christie } 11837996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_pool_free); 11847996a778SMike Christie 11857996a778SMike Christie /* 11867996a778SMike Christie * iSCSI Session's hostdata organization: 11877996a778SMike Christie * 11887996a778SMike Christie * *------------------* <== hostdata_session(host->hostdata) 11897996a778SMike Christie * | ptr to class sess| 11907996a778SMike Christie * |------------------| <== iscsi_hostdata(host->hostdata) 11917996a778SMike Christie * | iscsi_session | 11927996a778SMike Christie * *------------------* 11937996a778SMike Christie */ 11947996a778SMike Christie 11957996a778SMike Christie #define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \ 11967996a778SMike Christie _sz % sizeof(unsigned long)) 11977996a778SMike Christie 11987996a778SMike Christie #define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata)) 11997996a778SMike Christie 12007996a778SMike Christie /** 12017996a778SMike Christie * iscsi_session_setup - create iscsi cls session and host and session 12027996a778SMike Christie * @scsit: scsi transport template 12037996a778SMike Christie * @iscsit: iscsi transport template 12047996a778SMike Christie * @initial_cmdsn: initial CmdSN 12057996a778SMike Christie * @hostno: host no allocated 12067996a778SMike Christie * 12077996a778SMike Christie * This can be used by software iscsi_transports that allocate 12087996a778SMike Christie * a session per scsi host. 12097996a778SMike Christie **/ 12107996a778SMike Christie struct iscsi_cls_session * 12117996a778SMike Christie iscsi_session_setup(struct iscsi_transport *iscsit, 12127996a778SMike Christie struct scsi_transport_template *scsit, 12137996a778SMike Christie int cmd_task_size, int mgmt_task_size, 12147996a778SMike Christie uint32_t initial_cmdsn, uint32_t *hostno) 12157996a778SMike Christie { 12167996a778SMike Christie struct Scsi_Host *shost; 12177996a778SMike Christie struct iscsi_session *session; 12187996a778SMike Christie struct iscsi_cls_session *cls_session; 12197996a778SMike Christie int cmd_i; 12207996a778SMike Christie 12217996a778SMike Christie shost = scsi_host_alloc(iscsit->host_template, 12227996a778SMike Christie hostdata_privsize(sizeof(*session))); 12237996a778SMike Christie if (!shost) 12247996a778SMike Christie return NULL; 12257996a778SMike Christie 12267996a778SMike Christie shost->max_id = 1; 12277996a778SMike Christie shost->max_channel = 0; 12287996a778SMike Christie shost->max_lun = iscsit->max_lun; 12297996a778SMike Christie shost->max_cmd_len = iscsit->max_cmd_len; 12307996a778SMike Christie shost->transportt = scsit; 12317996a778SMike Christie shost->transportt->create_work_queue = 1; 12327996a778SMike Christie *hostno = shost->host_no; 12337996a778SMike Christie 12347996a778SMike Christie session = iscsi_hostdata(shost->hostdata); 12357996a778SMike Christie memset(session, 0, sizeof(struct iscsi_session)); 12367996a778SMike Christie session->host = shost; 12377996a778SMike Christie session->state = ISCSI_STATE_FREE; 12387996a778SMike Christie session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX; 12397996a778SMike Christie session->cmds_max = ISCSI_XMIT_CMDS_MAX; 12407996a778SMike Christie session->cmdsn = initial_cmdsn; 12417996a778SMike Christie session->exp_cmdsn = initial_cmdsn + 1; 12427996a778SMike Christie session->max_cmdsn = initial_cmdsn + 1; 12437996a778SMike Christie session->max_r2t = 1; 12447996a778SMike Christie session->tt = iscsit; 12457996a778SMike Christie 12467996a778SMike Christie /* initialize SCSI PDU commands pool */ 12477996a778SMike Christie if (iscsi_pool_init(&session->cmdpool, session->cmds_max, 12487996a778SMike Christie (void***)&session->cmds, 12497996a778SMike Christie cmd_task_size + sizeof(struct iscsi_cmd_task))) 12507996a778SMike Christie goto cmdpool_alloc_fail; 12517996a778SMike Christie 12527996a778SMike Christie /* pre-format cmds pool with ITT */ 12537996a778SMike Christie for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { 12547996a778SMike Christie struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; 12557996a778SMike Christie 12567996a778SMike Christie if (cmd_task_size) 12577996a778SMike Christie ctask->dd_data = &ctask[1]; 12587996a778SMike Christie ctask->itt = cmd_i; 12597996a778SMike Christie } 12607996a778SMike Christie 12617996a778SMike Christie spin_lock_init(&session->lock); 12627996a778SMike Christie INIT_LIST_HEAD(&session->connections); 12637996a778SMike Christie 12647996a778SMike Christie /* initialize immediate command pool */ 12657996a778SMike Christie if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max, 12667996a778SMike Christie (void***)&session->mgmt_cmds, 12677996a778SMike Christie mgmt_task_size + sizeof(struct iscsi_mgmt_task))) 12687996a778SMike Christie goto mgmtpool_alloc_fail; 12697996a778SMike Christie 12707996a778SMike Christie 12717996a778SMike Christie /* pre-format immediate cmds pool with ITT */ 12727996a778SMike Christie for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { 12737996a778SMike Christie struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i]; 12747996a778SMike Christie 12757996a778SMike Christie if (mgmt_task_size) 12767996a778SMike Christie mtask->dd_data = &mtask[1]; 12777996a778SMike Christie mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i; 12787996a778SMike Christie } 12797996a778SMike Christie 12807996a778SMike Christie if (scsi_add_host(shost, NULL)) 12817996a778SMike Christie goto add_host_fail; 12827996a778SMike Christie 12837996a778SMike Christie cls_session = iscsi_create_session(shost, iscsit, 0); 12847996a778SMike Christie if (!cls_session) 12857996a778SMike Christie goto cls_session_fail; 12867996a778SMike Christie *(unsigned long*)shost->hostdata = (unsigned long)cls_session; 12877996a778SMike Christie 12887996a778SMike Christie return cls_session; 12897996a778SMike Christie 12907996a778SMike Christie cls_session_fail: 12917996a778SMike Christie scsi_remove_host(shost); 12927996a778SMike Christie add_host_fail: 12937996a778SMike Christie iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); 12947996a778SMike Christie mgmtpool_alloc_fail: 12957996a778SMike Christie iscsi_pool_free(&session->cmdpool, (void**)session->cmds); 12967996a778SMike Christie cmdpool_alloc_fail: 12977996a778SMike Christie scsi_host_put(shost); 12987996a778SMike Christie return NULL; 12997996a778SMike Christie } 13007996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_session_setup); 13017996a778SMike Christie 13027996a778SMike Christie /** 13037996a778SMike Christie * iscsi_session_teardown - destroy session, host, and cls_session 13047996a778SMike Christie * shost: scsi host 13057996a778SMike Christie * 13067996a778SMike Christie * This can be used by software iscsi_transports that allocate 13077996a778SMike Christie * a session per scsi host. 13087996a778SMike Christie **/ 13097996a778SMike Christie void iscsi_session_teardown(struct iscsi_cls_session *cls_session) 13107996a778SMike Christie { 13117996a778SMike Christie struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); 13127996a778SMike Christie struct iscsi_session *session = iscsi_hostdata(shost->hostdata); 13137996a778SMike Christie 13147996a778SMike Christie scsi_remove_host(shost); 13157996a778SMike Christie 13167996a778SMike Christie iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); 13177996a778SMike Christie iscsi_pool_free(&session->cmdpool, (void**)session->cmds); 13187996a778SMike Christie 13197996a778SMike Christie iscsi_destroy_session(cls_session); 13207996a778SMike Christie scsi_host_put(shost); 13217996a778SMike Christie } 13227996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_session_teardown); 13237996a778SMike Christie 13247996a778SMike Christie /** 13257996a778SMike Christie * iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn 13267996a778SMike Christie * @cls_session: iscsi_cls_session 13277996a778SMike Christie * @conn_idx: cid 13287996a778SMike Christie **/ 13297996a778SMike Christie struct iscsi_cls_conn * 13307996a778SMike Christie iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) 13317996a778SMike Christie { 13327996a778SMike Christie struct iscsi_session *session = class_to_transport_session(cls_session); 13337996a778SMike Christie struct iscsi_conn *conn; 13347996a778SMike Christie struct iscsi_cls_conn *cls_conn; 1335d36ab6f3SMike Christie char *data; 13367996a778SMike Christie 13377996a778SMike Christie cls_conn = iscsi_create_conn(cls_session, conn_idx); 13387996a778SMike Christie if (!cls_conn) 13397996a778SMike Christie return NULL; 13407996a778SMike Christie conn = cls_conn->dd_data; 13417996a778SMike Christie memset(conn, 0, sizeof(*conn)); 13427996a778SMike Christie 13437996a778SMike Christie conn->session = session; 13447996a778SMike Christie conn->cls_conn = cls_conn; 13457996a778SMike Christie conn->c_stage = ISCSI_CONN_INITIAL_STAGE; 13467996a778SMike Christie conn->id = conn_idx; 13477996a778SMike Christie conn->exp_statsn = 0; 13487996a778SMike Christie conn->tmabort_state = TMABORT_INITIAL; 13497996a778SMike Christie INIT_LIST_HEAD(&conn->run_list); 13507996a778SMike Christie INIT_LIST_HEAD(&conn->mgmt_run_list); 13517996a778SMike Christie 13527996a778SMike Christie /* initialize general xmit PDU commands queue */ 13537996a778SMike Christie conn->xmitqueue = kfifo_alloc(session->cmds_max * sizeof(void*), 13547996a778SMike Christie GFP_KERNEL, NULL); 13557996a778SMike Christie if (conn->xmitqueue == ERR_PTR(-ENOMEM)) 13567996a778SMike Christie goto xmitqueue_alloc_fail; 13577996a778SMike Christie 13587996a778SMike Christie /* initialize general immediate & non-immediate PDU commands queue */ 13597996a778SMike Christie conn->immqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*), 13607996a778SMike Christie GFP_KERNEL, NULL); 13617996a778SMike Christie if (conn->immqueue == ERR_PTR(-ENOMEM)) 13627996a778SMike Christie goto immqueue_alloc_fail; 13637996a778SMike Christie 13647996a778SMike Christie conn->mgmtqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*), 13657996a778SMike Christie GFP_KERNEL, NULL); 13667996a778SMike Christie if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) 13677996a778SMike Christie goto mgmtqueue_alloc_fail; 13687996a778SMike Christie 13697996a778SMike Christie INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn); 13707996a778SMike Christie 13717996a778SMike Christie /* allocate login_mtask used for the login/text sequences */ 13727996a778SMike Christie spin_lock_bh(&session->lock); 13737996a778SMike Christie if (!__kfifo_get(session->mgmtpool.queue, 13747996a778SMike Christie (void*)&conn->login_mtask, 13757996a778SMike Christie sizeof(void*))) { 13767996a778SMike Christie spin_unlock_bh(&session->lock); 13777996a778SMike Christie goto login_mtask_alloc_fail; 13787996a778SMike Christie } 13797996a778SMike Christie spin_unlock_bh(&session->lock); 13807996a778SMike Christie 1381d36ab6f3SMike Christie data = kmalloc(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH, GFP_KERNEL); 1382d36ab6f3SMike Christie if (!data) 1383d36ab6f3SMike Christie goto login_mtask_data_alloc_fail; 1384d36ab6f3SMike Christie conn->login_mtask->data = data; 1385d36ab6f3SMike Christie 13867996a778SMike Christie init_timer(&conn->tmabort_timer); 13877996a778SMike Christie mutex_init(&conn->xmitmutex); 13887996a778SMike Christie init_waitqueue_head(&conn->ehwait); 13897996a778SMike Christie 13907996a778SMike Christie return cls_conn; 13917996a778SMike Christie 1392d36ab6f3SMike Christie login_mtask_data_alloc_fail: 1393d36ab6f3SMike Christie __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, 1394d36ab6f3SMike Christie sizeof(void*)); 13957996a778SMike Christie login_mtask_alloc_fail: 13967996a778SMike Christie kfifo_free(conn->mgmtqueue); 13977996a778SMike Christie mgmtqueue_alloc_fail: 13987996a778SMike Christie kfifo_free(conn->immqueue); 13997996a778SMike Christie immqueue_alloc_fail: 14007996a778SMike Christie kfifo_free(conn->xmitqueue); 14017996a778SMike Christie xmitqueue_alloc_fail: 14027996a778SMike Christie iscsi_destroy_conn(cls_conn); 14037996a778SMike Christie return NULL; 14047996a778SMike Christie } 14057996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_conn_setup); 14067996a778SMike Christie 14077996a778SMike Christie /** 14087996a778SMike Christie * iscsi_conn_teardown - teardown iscsi connection 14097996a778SMike Christie * cls_conn: iscsi class connection 14107996a778SMike Christie * 14117996a778SMike Christie * TODO: we may need to make this into a two step process 14127996a778SMike Christie * like scsi-mls remove + put host 14137996a778SMike Christie */ 14147996a778SMike Christie void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) 14157996a778SMike Christie { 14167996a778SMike Christie struct iscsi_conn *conn = cls_conn->dd_data; 14177996a778SMike Christie struct iscsi_session *session = conn->session; 14187996a778SMike Christie unsigned long flags; 14197996a778SMike Christie 14207996a778SMike Christie set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 142167a61114SMike Christie mutex_lock(&conn->xmitmutex); 14227996a778SMike Christie if (conn->c_stage == ISCSI_CONN_INITIAL_STAGE) { 14237996a778SMike Christie if (session->tt->suspend_conn_recv) 14247996a778SMike Christie session->tt->suspend_conn_recv(conn); 14257996a778SMike Christie 14267996a778SMike Christie session->tt->terminate_conn(conn); 14277996a778SMike Christie } 14287996a778SMike Christie 14297996a778SMike Christie spin_lock_bh(&session->lock); 14307996a778SMike Christie conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; 14317996a778SMike Christie if (session->leadconn == conn) { 14327996a778SMike Christie /* 14337996a778SMike Christie * leading connection? then give up on recovery. 14347996a778SMike Christie */ 14357996a778SMike Christie session->state = ISCSI_STATE_TERMINATE; 14367996a778SMike Christie wake_up(&conn->ehwait); 14377996a778SMike Christie } 14387996a778SMike Christie spin_unlock_bh(&session->lock); 14397996a778SMike Christie 14407996a778SMike Christie mutex_unlock(&conn->xmitmutex); 14417996a778SMike Christie 14427996a778SMike Christie /* 14437996a778SMike Christie * Block until all in-progress commands for this connection 14447996a778SMike Christie * time out or fail. 14457996a778SMike Christie */ 14467996a778SMike Christie for (;;) { 14477996a778SMike Christie spin_lock_irqsave(session->host->host_lock, flags); 14487996a778SMike Christie if (!session->host->host_busy) { /* OK for ERL == 0 */ 14497996a778SMike Christie spin_unlock_irqrestore(session->host->host_lock, flags); 14507996a778SMike Christie break; 14517996a778SMike Christie } 14527996a778SMike Christie spin_unlock_irqrestore(session->host->host_lock, flags); 14537996a778SMike Christie msleep_interruptible(500); 1454be2df72eSOr Gerlitz printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d " 1455be2df72eSOr Gerlitz "host_failed %d\n", session->host->host_busy, 1456be2df72eSOr Gerlitz session->host->host_failed); 14577996a778SMike Christie /* 14587996a778SMike Christie * force eh_abort() to unblock 14597996a778SMike Christie */ 14607996a778SMike Christie wake_up(&conn->ehwait); 14617996a778SMike Christie } 14627996a778SMike Christie 14637996a778SMike Christie spin_lock_bh(&session->lock); 1464d36ab6f3SMike Christie kfree(conn->login_mtask->data); 14657996a778SMike Christie __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, 14667996a778SMike Christie sizeof(void*)); 14677996a778SMike Christie list_del(&conn->item); 14687996a778SMike Christie if (list_empty(&session->connections)) 14697996a778SMike Christie session->leadconn = NULL; 14707996a778SMike Christie if (session->leadconn && session->leadconn == conn) 14717996a778SMike Christie session->leadconn = container_of(session->connections.next, 14727996a778SMike Christie struct iscsi_conn, item); 14737996a778SMike Christie 14747996a778SMike Christie if (session->leadconn == NULL) 14757996a778SMike Christie /* no connections exits.. reset sequencing */ 14767996a778SMike Christie session->cmdsn = session->max_cmdsn = session->exp_cmdsn = 1; 14777996a778SMike Christie spin_unlock_bh(&session->lock); 14787996a778SMike Christie 14797996a778SMike Christie kfifo_free(conn->xmitqueue); 14807996a778SMike Christie kfifo_free(conn->immqueue); 14817996a778SMike Christie kfifo_free(conn->mgmtqueue); 14827996a778SMike Christie 14837996a778SMike Christie iscsi_destroy_conn(cls_conn); 14847996a778SMike Christie } 14857996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_conn_teardown); 14867996a778SMike Christie 14877996a778SMike Christie int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) 14887996a778SMike Christie { 14897996a778SMike Christie struct iscsi_conn *conn = cls_conn->dd_data; 14907996a778SMike Christie struct iscsi_session *session = conn->session; 14917996a778SMike Christie 14927996a778SMike Christie if (session == NULL) { 14937996a778SMike Christie printk(KERN_ERR "iscsi: can't start unbound connection\n"); 14947996a778SMike Christie return -EPERM; 14957996a778SMike Christie } 14967996a778SMike Christie 14977996a778SMike Christie spin_lock_bh(&session->lock); 14987996a778SMike Christie conn->c_stage = ISCSI_CONN_STARTED; 14997996a778SMike Christie session->state = ISCSI_STATE_LOGGED_IN; 15007996a778SMike Christie 15017996a778SMike Christie switch(conn->stop_stage) { 15027996a778SMike Christie case STOP_CONN_RECOVER: 15037996a778SMike Christie /* 15047996a778SMike Christie * unblock eh_abort() if it is blocked. re-try all 15057996a778SMike Christie * commands after successful recovery 15067996a778SMike Christie */ 15077996a778SMike Christie conn->stop_stage = 0; 15087996a778SMike Christie conn->tmabort_state = TMABORT_INITIAL; 15097996a778SMike Christie session->age++; 15107996a778SMike Christie spin_unlock_bh(&session->lock); 15117996a778SMike Christie 15127996a778SMike Christie iscsi_unblock_session(session_to_cls(session)); 15137996a778SMike Christie wake_up(&conn->ehwait); 15147996a778SMike Christie return 0; 15157996a778SMike Christie case STOP_CONN_TERM: 15167996a778SMike Christie conn->stop_stage = 0; 15177996a778SMike Christie break; 15187996a778SMike Christie default: 15197996a778SMike Christie break; 15207996a778SMike Christie } 15217996a778SMike Christie spin_unlock_bh(&session->lock); 15227996a778SMike Christie 15237996a778SMike Christie return 0; 15247996a778SMike Christie } 15257996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_conn_start); 15267996a778SMike Christie 15277996a778SMike Christie static void 15287996a778SMike Christie flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn) 15297996a778SMike Christie { 15307996a778SMike Christie struct iscsi_mgmt_task *mtask, *tmp; 15317996a778SMike Christie 15327996a778SMike Christie /* handle pending */ 15337996a778SMike Christie while (__kfifo_get(conn->immqueue, (void*)&mtask, sizeof(void*)) || 15347996a778SMike Christie __kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) { 15357996a778SMike Christie if (mtask == conn->login_mtask) 15367996a778SMike Christie continue; 15377996a778SMike Christie debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt); 15387996a778SMike Christie __kfifo_put(session->mgmtpool.queue, (void*)&mtask, 15397996a778SMike Christie sizeof(void*)); 15407996a778SMike Christie } 15417996a778SMike Christie 15427996a778SMike Christie /* handle running */ 15437996a778SMike Christie list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) { 15447996a778SMike Christie debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt); 1545ed2abc7fSMike Christie list_del(&mtask->running); 1546ed2abc7fSMike Christie 15477996a778SMike Christie if (mtask == conn->login_mtask) 15487996a778SMike Christie continue; 1549ed2abc7fSMike Christie __kfifo_put(session->mgmtpool.queue, (void*)&mtask, 15507996a778SMike Christie sizeof(void*)); 15517996a778SMike Christie } 15527996a778SMike Christie 15537996a778SMike Christie conn->mtask = NULL; 15547996a778SMike Christie } 15557996a778SMike Christie 15567996a778SMike Christie /* Fail commands. Mutex and session lock held and recv side suspended */ 15577996a778SMike Christie static void fail_all_commands(struct iscsi_conn *conn) 15587996a778SMike Christie { 15597996a778SMike Christie struct iscsi_cmd_task *ctask, *tmp; 15607996a778SMike Christie 15617996a778SMike Christie /* flush pending */ 15627996a778SMike Christie while (__kfifo_get(conn->xmitqueue, (void*)&ctask, sizeof(void*))) { 15637996a778SMike Christie debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc, 15647996a778SMike Christie ctask->itt); 15657996a778SMike Christie fail_command(conn, ctask, DID_BUS_BUSY << 16); 15667996a778SMike Christie } 15677996a778SMike Christie 15687996a778SMike Christie /* fail all other running */ 15697996a778SMike Christie list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) { 15707996a778SMike Christie debug_scsi("failing in progress sc %p itt 0x%x\n", 15717996a778SMike Christie ctask->sc, ctask->itt); 15727996a778SMike Christie fail_command(conn, ctask, DID_BUS_BUSY << 16); 15737996a778SMike Christie } 15747996a778SMike Christie 15757996a778SMike Christie conn->ctask = NULL; 15767996a778SMike Christie } 15777996a778SMike Christie 1578656cffc9SMike Christie static void iscsi_start_session_recovery(struct iscsi_session *session, 15797996a778SMike Christie struct iscsi_conn *conn, int flag) 15807996a778SMike Christie { 1581ed2abc7fSMike Christie int old_stop_stage; 1582ed2abc7fSMike Christie 15837996a778SMike Christie spin_lock_bh(&session->lock); 1584ed2abc7fSMike Christie if (conn->stop_stage == STOP_CONN_TERM) { 15857996a778SMike Christie spin_unlock_bh(&session->lock); 15867996a778SMike Christie return; 15877996a778SMike Christie } 1588ed2abc7fSMike Christie 1589ed2abc7fSMike Christie /* 1590ed2abc7fSMike Christie * When this is called for the in_login state, we only want to clean 159167a61114SMike Christie * up the login task and connection. We do not need to block and set 159267a61114SMike Christie * the recovery state again 1593ed2abc7fSMike Christie */ 159467a61114SMike Christie if (flag == STOP_CONN_TERM) 159567a61114SMike Christie session->state = ISCSI_STATE_TERMINATE; 159667a61114SMike Christie else if (conn->stop_stage != STOP_CONN_RECOVER) 159767a61114SMike Christie session->state = ISCSI_STATE_IN_RECOVERY; 1598ed2abc7fSMike Christie 1599ed2abc7fSMike Christie old_stop_stage = conn->stop_stage; 16007996a778SMike Christie conn->stop_stage = flag; 160167a61114SMike Christie conn->c_stage = ISCSI_CONN_STOPPED; 160267a61114SMike Christie set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 16037996a778SMike Christie spin_unlock_bh(&session->lock); 16047996a778SMike Christie 16057996a778SMike Christie if (session->tt->suspend_conn_recv) 16067996a778SMike Christie session->tt->suspend_conn_recv(conn); 16077996a778SMike Christie 16087996a778SMike Christie mutex_lock(&conn->xmitmutex); 16097996a778SMike Christie /* 16107996a778SMike Christie * for connection level recovery we should not calculate 16117996a778SMike Christie * header digest. conn->hdr_size used for optimization 16127996a778SMike Christie * in hdr_extract() and will be re-negotiated at 16137996a778SMike Christie * set_param() time. 16147996a778SMike Christie */ 16157996a778SMike Christie if (flag == STOP_CONN_RECOVER) { 16167996a778SMike Christie conn->hdrdgst_en = 0; 16177996a778SMike Christie conn->datadgst_en = 0; 1618656cffc9SMike Christie if (session->state == ISCSI_STATE_IN_RECOVERY && 161967a61114SMike Christie old_stop_stage != STOP_CONN_RECOVER) { 162067a61114SMike Christie debug_scsi("blocking session\n"); 16217996a778SMike Christie iscsi_block_session(session_to_cls(session)); 16227996a778SMike Christie } 162367a61114SMike Christie } 1624656cffc9SMike Christie 1625656cffc9SMike Christie session->tt->terminate_conn(conn); 1626656cffc9SMike Christie /* 1627656cffc9SMike Christie * flush queues. 1628656cffc9SMike Christie */ 1629656cffc9SMike Christie spin_lock_bh(&session->lock); 1630656cffc9SMike Christie fail_all_commands(conn); 1631656cffc9SMike Christie flush_control_queues(session, conn); 1632656cffc9SMike Christie spin_unlock_bh(&session->lock); 1633656cffc9SMike Christie 16347996a778SMike Christie mutex_unlock(&conn->xmitmutex); 16357996a778SMike Christie } 16367996a778SMike Christie 16377996a778SMike Christie void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) 16387996a778SMike Christie { 16397996a778SMike Christie struct iscsi_conn *conn = cls_conn->dd_data; 16407996a778SMike Christie struct iscsi_session *session = conn->session; 16417996a778SMike Christie 16427996a778SMike Christie switch (flag) { 16437996a778SMike Christie case STOP_CONN_RECOVER: 16447996a778SMike Christie case STOP_CONN_TERM: 16457996a778SMike Christie iscsi_start_session_recovery(session, conn, flag); 16468d2860b3SMike Christie break; 16477996a778SMike Christie default: 1648be2df72eSOr Gerlitz printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag); 16497996a778SMike Christie } 16507996a778SMike Christie } 16517996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_conn_stop); 16527996a778SMike Christie 16537996a778SMike Christie int iscsi_conn_bind(struct iscsi_cls_session *cls_session, 16547996a778SMike Christie struct iscsi_cls_conn *cls_conn, int is_leading) 16557996a778SMike Christie { 16567996a778SMike Christie struct iscsi_session *session = class_to_transport_session(cls_session); 16577996a778SMike Christie struct iscsi_conn *tmp = ERR_PTR(-EEXIST), *conn = cls_conn->dd_data; 16587996a778SMike Christie 16597996a778SMike Christie /* lookup for existing connection */ 16607996a778SMike Christie spin_lock_bh(&session->lock); 16617996a778SMike Christie list_for_each_entry(tmp, &session->connections, item) { 16627996a778SMike Christie if (tmp == conn) { 16637996a778SMike Christie if (conn->c_stage != ISCSI_CONN_STOPPED || 16647996a778SMike Christie conn->stop_stage == STOP_CONN_TERM) { 1665be2df72eSOr Gerlitz printk(KERN_ERR "iscsi: can't bind " 16667996a778SMike Christie "non-stopped connection (%d:%d)\n", 16677996a778SMike Christie conn->c_stage, conn->stop_stage); 16687996a778SMike Christie spin_unlock_bh(&session->lock); 16697996a778SMike Christie return -EIO; 16707996a778SMike Christie } 16717996a778SMike Christie break; 16727996a778SMike Christie } 16737996a778SMike Christie } 16747996a778SMike Christie if (tmp != conn) { 16757996a778SMike Christie /* bind new iSCSI connection to session */ 16767996a778SMike Christie conn->session = session; 16777996a778SMike Christie list_add(&conn->item, &session->connections); 16787996a778SMike Christie } 16797996a778SMike Christie spin_unlock_bh(&session->lock); 16807996a778SMike Christie 16817996a778SMike Christie if (is_leading) 16827996a778SMike Christie session->leadconn = conn; 16837996a778SMike Christie 16847996a778SMike Christie /* 16857996a778SMike Christie * Unblock xmitworker(), Login Phase will pass through. 16867996a778SMike Christie */ 16877996a778SMike Christie clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); 16887996a778SMike Christie clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 16897996a778SMike Christie return 0; 16907996a778SMike Christie } 16917996a778SMike Christie EXPORT_SYMBOL_GPL(iscsi_conn_bind); 16927996a778SMike Christie 16937996a778SMike Christie MODULE_AUTHOR("Mike Christie"); 16947996a778SMike Christie MODULE_DESCRIPTION("iSCSI library functions"); 16957996a778SMike Christie MODULE_LICENSE("GPL"); 1696