iscsi-recv-pdu.c
来自「iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备」· C语言 代码 · 共 1,019 行 · 第 1/2 页
C
1,019 行
/* FIXME: we could discard the data or drop the session */ return rc;}/* * Only call this from recvs where the rx_buffer is not in * use. We don't bother checking the CRC, since we couldn't * retry the command anyway */static voiddrop_data(struct iscsi_session *session, struct iscsi_hdr *sth){ int pad, length, num_bytes; struct kvec iov; length = ntoh24(sth->dlength); pad = length % PAD_WORD_LEN; if (pad) pad = PAD_WORD_LEN - pad; length += pad; if (session->data_digest == ISCSI_DIGEST_CRC32C) { iscsi_host_info(session, "recv_data discarding %d data PDU " "bytes, %d pad bytes, %Zu digest bytes\n", ntoh24(sth->dlength), pad, sizeof(u32)); length += sizeof(u32); } else iscsi_host_info(session, "recv_data discarding %d data PDU " "bytes, %d pad bytes\n", ntoh24(sth->dlength), pad); while (!signal_pending(current) && length > 0) { num_bytes = min_t(int, length, sizeof(session->rx_buffer)); iov.iov_base = session->rx_buffer; iov.iov_len = sizeof(session->rx_buffer); /* should iov_len match num_bytes ? */ if (iscsi_recvmsg(session, &iov, 1, num_bytes) != ISCSI_IO_SUCCESS) { iscsi_drop_session(session); break; } /* assume a PDU round-trip, connection is ok */ session->last_rx = jiffies; length -= num_bytes; }}static voidhandle_scsi_data(struct iscsi_session *session, struct iscsi_hdr *sth){ struct iscsi_data_rsp_hdr *stdrh = (struct iscsi_data_rsp_hdr *)sth; struct iscsi_task *task; struct scsi_cmnd *sc; struct scatterlist sg; int dlength, offset, rc; u32 itt = ntohl(stdrh->itt); if (stdrh->flags & ISCSI_FLAG_DATA_STATUS) /* FIXME: check StatSN */ session->exp_stat_sn = ntohl(stdrh->statsn) + 1; update_sn(session, ntohl(stdrh->expcmdsn), ntohl(stdrh->maxcmdsn)); dlength = ntoh24(stdrh->dlength); offset = ntohl(stdrh->offset); spin_lock_bh(&session->task_lock); task = iscsi_find_session_task(session, itt); if (!task) { iscsi_host_warn(session, "recv_data, no task for itt %u next " "itt %u, discarding received data, offset %u " "len %u\n", ntohl(stdrh->itt), session->next_itt, offset, dlength); spin_unlock_bh(&session->task_lock); drop_data(session, sth); return; } sc = task->scsi_cmnd; /* sanity check the PDU against the command */ if (!test_bit(ISCSI_TASK_READ, &task->flags)) { iscsi_host_err(session, "lun%u: recv_data itt %u, command " "cdb 0x%02x, dropping session due to " "unexpected Data-in from\n", task->lun, itt, sc->cmnd[0]); iscsi_drop_session(session); goto done; } else if ((offset + dlength) > sc->request_bufflen) { /* buffer overflow, often because of a corrupt PDU header */ iscsi_host_err(session, "recv_data for itt %u, cmnd 0x%x, " "bufflen %u, Data PDU with offset %u len %u " "overflows command buffer, dropping session\n", itt, sc->cmnd[0], sc->request_bufflen, offset, dlength); iscsi_drop_session(session); goto done; } else if (task->rxdata != offset) { /* * if the data arrives out-of-order, it becomes much harder * for us to correctly calculate the residual if we don't get * enough data and also don't get an underflow from the * target. This can happen if we discard Data PDUs due to * bogus offsets/lengths. Since we always negotiate for * Data PDUs in-order, this should never happen, but check * for it anyway. */ iscsi_host_err(session, "recv_data for itt %u, cmnd 0x%x, " "bufflen %u, offset %u does not match expected " "offset %u, dropping session\n", itt, sc->cmnd[0], sc->request_bufflen, offset, task->rxdata); iscsi_drop_session(session); goto done; } /* * either we'll read it all, or we'll drop the session and requeue * the command, so it's safe to increment early */ task->rxdata += dlength; spin_unlock_bh(&session->task_lock); if (sc->use_sg) rc = iscsi_recv_sg_data(session, dlength, sc->request_buffer, sc->use_sg, offset, session->data_digest); else { sg_init_one(&sg, sc->request_buffer, dlength); rc = iscsi_recv_sg_data(session, dlength, &sg, 1, offset, session->data_digest); } spin_lock_bh(&session->task_lock); switch (rc) { case ISCSI_IO_ERR: iscsi_drop_session(session); break; case ISCSI_IO_CRC32C_ERR: __set_bit(ISCSI_TASK_CRC_ERROR, &task->flags); /* fall through */ case ISCSI_IO_SUCCESS: if (stdrh->flags & ISCSI_FLAG_DATA_STATUS) { iscsi_process_task_status(task, sth); iscsi_complete_task(task); } } done: __iscsi_put_task(task); spin_unlock_bh(&session->task_lock);}/** * handle_task_mgmt_rsp - Process the task management response. * @session: to retrieve the task * @ststmrh: task management response header * * Description: * Retrieve the task for which task mgmt response is received and take * appropriate action based on the type of task management request. **/static voidhandle_task_mgmt_rsp(struct iscsi_session *session, struct iscsi_hdr *sth){ struct iscsi_scsi_task_mgmt_rsp_hdr *ststmrh; struct iscsi_task *task; u32 mgmt_itt; ststmrh = (struct iscsi_scsi_task_mgmt_rsp_hdr *)sth; mgmt_itt = ntohl(ststmrh->itt); /* FIXME: check StatSN */ session->exp_stat_sn = ntohl(ststmrh->statsn) + 1; update_sn(session, ntohl(ststmrh->expcmdsn), ntohl(ststmrh->maxcmdsn)); spin_lock_bh(&session->task_lock); /* * This can fail if they timedout and we escalated the recovery * to a new function */ task = iscsi_find_session_task(session, mgmt_itt); if (!task) { iscsi_host_warn(session, "mgmt response 0x%x for unknown itt " "%u, rtt %u\n", ststmrh->response, ntohl(ststmrh->itt), ntohl(ststmrh->rtt)); goto done; } if (ststmrh->response == 0) { iscsi_host_info(task->session, "task mgmt itt %u " "successful\n", mgmt_itt); iscsi_complete_tmf_task(task, ISCSI_TASK_TMF_SUCCESS); } else { iscsi_host_err(task->session, "task mgmt itt %u rejected" " (0x%x)\n", mgmt_itt, ststmrh->response); iscsi_complete_tmf_task(task, ISCSI_TASK_TMF_FAILED); } __iscsi_put_task(task); done: /* * we got the expected response, allow the eh thread to send * another task mgmt PDU whenever it wants to */ if (session->last_mgmt_itt == mgmt_itt) session->last_mgmt_itt = ISCSI_RSVD_TASK_TAG; spin_unlock_bh(&session->task_lock);}static voidprocess_immed_cmd_reject(struct iscsi_session *session, unsigned char *xbuf, int dlength){ u32 itt; struct iscsi_task *task; struct iscsi_hdr pdu; if (dlength < sizeof(pdu)) { iscsi_host_warn(session, "Immediate command rejected, dlength " "%u\n", dlength); return; } /* look at the rejected PDU */ memcpy(&pdu, xbuf, sizeof(pdu)); itt = ntohl(pdu.itt); /* * try to find the task corresponding to this itt, * and wake up any process waiting on it */ spin_lock_bh(&session->task_lock); if (session->last_mgmt_itt == itt) session->last_mgmt_itt = ISCSI_RSVD_TASK_TAG; task = iscsi_find_session_task(session, itt); if (task) { iscsi_host_notice(session, "task mgmt PDU rejected, mgmt %u, " "itt %u\n", itt, task->itt); iscsi_complete_tmf_task(task, ISCSI_TASK_IMM_REJECT); __iscsi_put_task(task); } else if ((pdu.opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT_CMD) /* * our Logout was rejected. just let the * logout response timer drop the session */ iscsi_host_warn(session, "Logout PDU rejected, itt %u\n", itt); else iscsi_host_warn(session, "itt %u immediate command rejected\n", itt); spin_unlock_bh(&session->task_lock);}static voidhandle_reject(struct iscsi_session *session, struct iscsi_hdr *sth, unsigned char *xbuf){ struct iscsi_reject_hdr *reject; struct iscsi_hdr pdu; int dlength; u32 itt; reject = (struct iscsi_reject_hdr *)sth; dlength = ntoh24(reject->dlength); /* FIXME: check StatSN */ session->exp_stat_sn = ntohl(reject->statsn) + 1; update_sn(session, ntohl(reject->expcmdsn), ntohl(reject->maxcmdsn)); if (reject->reason == ISCSI_REJECT_DATA_DIGEST_ERROR) { /* * we don't need to do anything about these, * timers or other PDUs will handle the problem. */ if (dlength >= sizeof(pdu)) { memcpy(&pdu, xbuf, sizeof(pdu)); itt = ntohl(pdu.itt); iscsi_host_warn(session, "itt %u (opcode 0x%x) rejected" " because of a DataDigest error\n", itt, pdu.opcode); } else iscsi_host_warn(session, "Target rejected a PDU because" " of a DataDigest error\n"); } else if (reject->reason == ISCSI_REJECT_IMM_CMD_REJECT) process_immed_cmd_reject(session, xbuf, dlength); else { if (dlength >= sizeof(pdu)) { /* look at the rejected PDU */ memcpy(&pdu, xbuf, sizeof(pdu)); itt = ntohl(pdu.itt); iscsi_host_err(session, "Dropping session because " "target rejected a PDU, reason 0x%x, " "dlength %d, rejected itt %u, opcode " "0x%x\n", reject->reason, dlength, itt, pdu.opcode); } else iscsi_host_err(session, "Dropping session because " "target rejected a PDU, reason 0x%x, " "dlength %u\n", reject->reason, dlength); iscsi_drop_session(session); }}static voidhandle_async_msg(struct iscsi_session *session, struct iscsi_hdr *sth, unsigned char *xbuf){ struct iscsi_async_msg_hdr *staeh = (struct iscsi_async_msg_hdr *)sth; unsigned int senselen; /* FIXME: check StatSN */ session->exp_stat_sn = ntohl(staeh->statsn) + 1; update_sn(session, ntohl(staeh->expcmdsn), ntohl(staeh->maxcmdsn)); switch (staeh->async_event) { case ISCSI_ASYNC_MSG_SCSI_EVENT: senselen = (xbuf[0] << 8) | xbuf[1]; xbuf += 2; iscsi_host_info(session, "Received async SCSI event. Printing " "sense\n");/* remove for 2.6.11 __scsi_print_sense(ISCSI_PROC_NAME, xbuf, senselen);*/ break; case ISCSI_ASYNC_MSG_REQUEST_LOGOUT: /* * FIXME: this is really a request to drop a connection, * not the whole session, but we currently only have one * connection per session, so there's no difference * at the moment. */ iscsi_host_warn(session, "Target requests logout within %u " "seconds for session\n", ntohs(staeh->param3)); /* * we need to get the task lock to make sure the TX thread * isn't in the middle of adding another task to the session. */ spin_lock_bh(&session->task_lock); iscsi_request_logout(session, ntohs(staeh->param3) - (HZ / 10), session->active_timeout); spin_unlock_bh(&session->task_lock); break; case ISCSI_ASYNC_MSG_DROPPING_CONNECTION: iscsi_host_warn(session, "Target dropping connection %u, " "reconnect min %u max %u\n", ntohs(staeh->param1), ntohs(staeh->param2), ntohs(staeh->param3)); session->time2wait = (long) ntohs(staeh->param2) & 0x0000FFFFFL; break; case ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS: iscsi_host_warn(session, "Target dropping all connections, " "reconnect min %u max %u\n", ntohs(staeh->param2), ntohs(staeh->param3)); session->time2wait = (long) ntohs(staeh->param2) & 0x0000FFFFFL; break; case ISCSI_ASYNC_MSG_VENDOR_SPECIFIC: iscsi_host_warn(session, "Ignoring vendor-specific async event," " vcode 0x%x\n", staeh->async_vcode); break; case ISCSI_ASYNC_MSG_PARAM_NEGOTIATION: iscsi_host_warn(session, "Received async event param " "negotiation, dropping session\n"); iscsi_drop_session(session); break; default: iscsi_host_err(session, "Received unknown async event 0x%x\n", staeh->async_event); break; } if (staeh->async_event == ISCSI_ASYNC_MSG_DROPPING_CONNECTION || staeh->async_event == ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS || staeh->async_event == ISCSI_ASYNC_MSG_REQUEST_LOGOUT) { spin_lock(&session->portal_lock); memcpy(&session->addr, &session->portal.addr, sizeof(struct sockaddr)); spin_unlock(&session->portal_lock); }}/** * iscsi_recv_pdu - Read in a iSCSI PDU * @session: iscsi session structure * @hdr: a iSCSI PDU header * @hdr_digest: digest type for header * @data: buffer for data * @max_data_len: buffer size * @data_digest: digest type for data * @timeout: come crap timeout used for login PDUs * * Description: * Reads a iSCSI PDU into memory. Excpet for login PDUs, this function * will also process the PDU. **/intiscsi_recv_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr, int hdr_digest, char *data, int max_data_len, int data_digest, int timeout){ int rc; int data_len; struct scatterlist sg; /* * TODO setting the login timer does nothing for FFP so * it is safe to set, however this will be fixed properly soon */ if (timeout) session->login_phase_timer = jiffies + (timeout * HZ); else session->login_phase_timer = 0; if (iscsi_recv_header(session, hdr, hdr_digest) != ISCSI_IO_SUCCESS) goto fail; data_len = ntoh24(hdr->dlength); /* * scsi data is read in and processed by its handler for now */ if (data_len && hdr->opcode != ISCSI_OP_SCSI_DATA_RSP) { if (data_len > max_data_len) { iscsi_host_err(session, "iscsi_recv_pdu() cannot read " "%d bytes of PDU data, only %d bytes " "of buffer available\n", data_len, max_data_len); goto fail; } /* * must clear this, beucase the login api uses the same * buffer for recv and send */ memset(data, 0, max_data_len); sg_init_one(&sg, data, data_len); rc = iscsi_recv_sg_data(session, data_len, &sg, 1, 0, data_digest); if (rc == ISCSI_IO_CRC32C_ERR) { switch (hdr->opcode) { case ISCSI_OP_ASYNC_MSG: case ISCSI_OP_REJECT: /* unsolicited so ignore */ goto done; default: goto fail; }; } else if (rc != ISCSI_IO_SUCCESS) goto fail; } switch (hdr->opcode) { case ISCSI_OP_NOOP_IN: handle_nop_in(session, hdr); break; case ISCSI_OP_SCSI_RSP: handle_scsi_rsp(session, hdr, data); break; case ISCSI_OP_SCSI_TASK_MGT_RSP: handle_task_mgmt_rsp(session, hdr); break; case ISCSI_OP_R2T: handle_r2t(session, hdr); break; case ISCSI_OP_SCSI_DATA_RSP: handle_scsi_data(session, hdr); break; case ISCSI_OP_ASYNC_MSG: handle_async_msg(session, hdr, data); break; case ISCSI_OP_REJECT: handle_reject(session, hdr, data); break; case ISCSI_OP_LOGOUT_RSP: handle_logout(session, hdr); break; case ISCSI_OP_LOGIN_RSP: /* * The login api needs the buffer to be cleared when no * data has been read */ if (!data_len) memset(data, 0, max_data_len); /* * login api will process further */ break; default: iscsi_host_err(session, "Dropping session after receiving " "unexpected opcode 0x%x\n", hdr->opcode); session->time2wait = 2; goto fail; } done: /* TODO rm phase_timer hack */ session->login_phase_timer = 0; return 1; fail: session->login_phase_timer = 0; iscsi_drop_session(session); return 0;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?