iscsi-initiator.c
来自「iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备」· C语言 代码 · 共 589 行
C
589 行
/* * iSCSI driver for Linux * Copyright (C) 2001 Cisco Systems, Inc. * Copyright (C) 2004 Mike Christie * Copyright (C) 2004 IBM Corporation * maintained by linux-iscsi-devel@lists.sourceforge.net * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * See the file COPYING included with this distribution for more details. * * $Id: iscsi-initiator.c,v 1.89 2005/01/19 22:58:35 mikenc Exp $ * * This file contains interfaces required by SCSI mid layer, module * initialization and shutdown routines. */#include <linux/version.h>#include <linux/interrupt.h>#include <linux/moduleparam.h>#include <linux/notifier.h>#include <linux/reboot.h>#include <linux/in.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_transport.h>#include "scsi_transport_iscsi.h"#include "iscsi-sfnet.h"#include "iscsi-session.h"#include "iscsi-protocol.h"#include "iscsi-task.h"#include "iscsi-version.h"/* * IMPORTANT NOTE: to prevent deadlock, when holding multiple locks, * the following locking order must be followed at all times: * * session->portal_lock - access to a session's portal info * session->task_lock - access to a session's collections of tasks * host_lock - mid-layer acquires before calling queuecommand, * and eh_*. * * Note for grabbing task_lock: queuecommand and eh_timed_out are invoked in * soft_irq context. The former can be invoked in process context as well. * Every other function where we grab task_lock, we have process context. * Hence we use spin_lock in eh_timed_out and spin_lock_bh every where else to * grab the task lock. */MODULE_AUTHOR("Cisco Systems, Inc.");MODULE_DESCRIPTION("iSCSI initiator");MODULE_LICENSE("GPL");MODULE_VERSION(ISCSI_DRIVER_VERSION);kmem_cache_t *iscsi_task_cache;static struct scsi_transport_template *iscsi_transportt;/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */#define SNA32_CHECK 2147483648ULintiscsi_sna_lt(u32 n1, u32 n2){ return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) || (n1 > n2 && (n2 - n1 < SNA32_CHECK)));}/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */intiscsi_sna_lte(u32 n1, u32 n2){ return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) || (n1 > n2 && (n2 - n1 < SNA32_CHECK)));}/* mark a scsi_cmnd as having a LUN communication failure */static inline voidset_lun_comm_failure(struct scsi_cmnd *sc){ sc->sense_buffer[0] = 0x70; sc->sense_buffer[2] = NOT_READY; sc->sense_buffer[7] = 0x6; sc->sense_buffer[12] = 0x08; sc->sense_buffer[13] = 0x00;}u32iscsi_command_attr(struct scsi_cmnd *cmd){ unsigned int attr = ISCSI_ATTR_UNTAGGED; char msg[2]; if (scsi_populate_tag_msg(cmd, msg) == 2) { switch (msg[0]) { case MSG_SIMPLE_TAG: attr = ISCSI_ATTR_SIMPLE; break; case MSG_HEAD_TAG: attr = ISCSI_ATTR_HEAD_OF_QUEUE; break; case MSG_ORDERED_TAG: attr = ISCSI_ATTR_ORDERED; break; }; } return attr;}static intiscsi_slave_configure(struct scsi_device *sdev){ int depth = 1, tag = 0; /* * TODO (one day) - when tcq is not supported we should * internally queue a command to have one ready to go right * away when the outstanding one completes. */ if (sdev->tagged_supported) { scsi_activate_tcq(sdev, ISCSI_CMDS_PER_LUN); depth = ISCSI_CMDS_PER_LUN; tag = MSG_ORDERED_TAG; } scsi_adjust_queue_depth(sdev, tag, depth); return 0;}/** * iscsi_eh_timed_out - handle scsi command timeouts * sc: scsi command * * Note: * In the normal case where the session is ok or the command was queued * when the session was down (and it never came up but did not yet timeout * - TODO use device block/unblock), we will fail the command here if * we can. For all other race cases where for example the session changed * state at the same time the command timed out but we had the task lock * we just ask for more time since those events will clean themselves up. **/static enum scsi_eh_timer_returniscsi_eh_timed_out(struct scsi_cmnd *sc){ struct Scsi_Host *shost = sc->device->host; struct iscsi_session *session = (struct iscsi_session *)shost->hostdata; struct iscsi_task *task; enum scsi_eh_timer_return ret = EH_RESET_TIMER; int result = DID_BUS_BUSY << 16; spin_lock(&session->task_lock); task = (struct iscsi_task *)sc->SCp.ptr; if (!task) { /* * completed while timer was firing */ ret = EH_HANDLED; goto done; } if (test_bit(SESSION_ESTABLISHED, &session->control_bits)) ret = EH_NOT_HANDLED; else if (test_bit(SESSION_REPLACEMENT_TIMEDOUT, &session->control_bits)) result = DID_NO_CONNECT << 16; if (task->itt == ISCSI_RSVD_TASK_TAG) { sc->result = result; __iscsi_complete_task(task); ret = EH_HANDLED; } done: spin_unlock(&session->task_lock); return ret;}static intiscsi_eh_abort(struct scsi_cmnd *sc){ struct Scsi_Host *shost = sc->device->host; struct iscsi_session *session = (struct iscsi_session *)shost->hostdata; struct iscsi_task *task, *tmf_task; int ret = FAILED; spin_unlock_irq(shost->host_lock); spin_lock_bh(&session->task_lock); /* * TODO must fix these type of tests */ if (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) goto done; task = (struct iscsi_task *)sc->SCp.ptr; if (!task) { iscsi_host_err(session, "eh_abort cmnd already done\n"); ret = SUCCESS; goto done; } if (task->itt == ISCSI_RSVD_TASK_TAG) { __iscsi_complete_task(task); ret = SUCCESS; goto done; } /* * TODO need a iscsi_dev_info */ iscsi_host_info(session, "Sending ABORT TASK for task itt %u\n", task->itt); tmf_task = session->mgmt_task; memset(tmf_task, 0, sizeof(*tmf_task)); iscsi_init_task(tmf_task); tmf_task->session = session; tmf_task->lun = task->lun; /* * this will become the refcmdsn */ tmf_task->cmdsn = task->cmdsn; tmf_task->rtt = task->itt; set_bit(ISCSI_TASK_ABORT, &tmf_task->flags); if (!iscsi_exec_task_mgmt(tmf_task, session->abort_timeout)) { ret = SUCCESS; goto done; } /* * TMF may have failed if the task completed first (check here) */ if (!sc->SCp.ptr) ret = SUCCESS; done: spin_unlock_bh(&session->task_lock); spin_lock_irq(shost->host_lock); return ret;}static intiscsi_eh_device_reset(struct scsi_cmnd *sc){ struct Scsi_Host *shost = sc->device->host; struct iscsi_session *session = (struct iscsi_session *)shost->hostdata; struct iscsi_task *task; int ret = FAILED; spin_unlock_irq(shost->host_lock); spin_lock_bh(&session->task_lock); if (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) goto done; task = session->mgmt_task; memset(task, 0, sizeof(*task)); iscsi_init_task(task); task->session = session; task->lun = sc->device->lun; __set_bit(ISCSI_TASK_ABORT_TASK_SET, &task->flags); /* * need a iscsi_dev_info */ iscsi_host_info(session, "Sending ABORT TASK SET\n"); if (!iscsi_exec_task_mgmt(task, session->abort_timeout)) { ret = SUCCESS; goto done; } iscsi_init_task(task); __set_bit(ISCSI_TASK_LU_RESET, &task->flags); iscsi_host_info(session, "Sending LU RESET\n"); if (!iscsi_exec_task_mgmt(task, session->reset_timeout)) ret = SUCCESS; done: spin_unlock_bh(&session->task_lock); spin_lock_irq(shost->host_lock); return ret;}static intiscsi_eh_host_reset(struct scsi_cmnd *sc){ struct Scsi_Host *shost = sc->device->host; struct iscsi_session *session = (struct iscsi_session *)shost->hostdata; struct iscsi_task *task; int ret = FAILED; spin_unlock_irq(shost->host_lock); spin_lock_bh(&session->task_lock); if (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) goto done; task = session->mgmt_task; memset(task, 0, sizeof(*task)); iscsi_init_task(task); task->session = session; __set_bit(ISCSI_TASK_TGT_WARM_RESET, &task->flags); iscsi_host_info(session, "Sending TARGET WARM RESET\n"); if (iscsi_exec_task_mgmt(task, session->reset_timeout)) /* * no other options */ iscsi_drop_session(session); done: /* * if we failed, scsi-ml will put us offline * and if we were successful it will redrive the * commands, so we clean everything up from our side * so scsi-ml can retake ownership of the commands. * (At this point the tx and rx threads will not be * touching the commands since either the session * was dropped or we just did a target reset) */ iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_BUS_BUSY); spin_unlock_bh(&session->task_lock); if (iscsi_wait_for_session(session, 0)) ret = SUCCESS; spin_lock_irq(shost->host_lock); return ret;}voidiscsi_complete_command(struct scsi_cmnd *sc){ sc->SCp.ptr = NULL; sc->scsi_done(sc);}/** * iscsi_queuecommand - queuecommand interface for the iSCSI driver. * @sc: scsi command from the midlayer * @done: Call back function to be called once the command is executed. **/static intiscsi_queuecommand(struct scsi_cmnd *sc, void (*done) (struct scsi_cmnd *)){ struct Scsi_Host *host = sc->device->host; struct iscsi_session *session = (struct iscsi_session *)host->hostdata; struct iscsi_task *task; int ret = 0; spin_unlock_irq(host->host_lock); /* * record whether I/O commands have been ever been sent on this * session, to help us decide when we need the session and should * retry logins regardless of the login status. Ignore all the * commands sent by default as part of the LUN being scanned or a * device being opened, so that sessions that have always been idle * can be dropped. Of course, this is always true for disks, since * Linux will do reads looking for a partition table. */ switch (sc->cmnd[0]) { case INQUIRY: case REPORT_LUNS: case TEST_UNIT_READY: case READ_CAPACITY: case START_STOP: case MODE_SENSE: break; default: session->commands_queued = 1; break; } spin_lock_bh(&session->task_lock); if (test_bit(SESSION_REPLACEMENT_TIMEDOUT, &session->control_bits)) { spin_unlock_bh(&session->task_lock); if (printk_ratelimit()) iscsi_host_warn(session, "lun%u: Session terminating, " "failing to queue cdb 0x%x and any " "following commands\n", sc->device->lun, sc->cmnd[0]); goto fail; } /* make sure we can complete it properly later */ sc->scsi_done = done; sc->result = 0; memset(&sc->SCp, 0, sizeof(sc->SCp)); /* * alloc a task and add it to the pending queue so * the tx-thread will run it */ task = iscsi_alloc_task(session); if (!task) { ret = SCSI_MLQUEUE_HOST_BUSY; goto done; } task->lun = sc->device->lun; task->scsi_cmnd = sc; sc->SCp.ptr = (char *)task; list_add_tail(&task->queue, &session->pending_queue); iscsi_wake_tx_thread(TX_SCSI_COMMAND, session); done: spin_unlock_bh(&session->task_lock); spin_lock_irq(host->host_lock); return ret; fail: spin_lock_irq(host->host_lock); sc->result = DID_NO_CONNECT << 16; sc->resid = sc->request_bufflen; set_lun_comm_failure(sc); done(sc); return 0;}intiscsi_destroy_host(struct Scsi_Host *shost){ struct iscsi_session *session = (struct iscsi_session *)shost->hostdata; if (!test_bit(SESSION_CREATED, &session->control_bits)) return -EINVAL; if (test_and_set_bit(SESSION_RELEASING, &session->control_bits)) return -EINVAL; scsi_remove_host(shost); iscsi_destroy_session(session); scsi_host_put(shost); return 0;}static struct scsi_host_template iscsi_driver_template = { .name = "SFNet iSCSI driver", .proc_name = ISCSI_PROC_NAME, .module = THIS_MODULE, .queuecommand = iscsi_queuecommand, .eh_timed_out = iscsi_eh_timed_out, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler = iscsi_eh_device_reset, .eh_host_reset_handler = iscsi_eh_host_reset, .skip_settle_delay = 1, .slave_configure = iscsi_slave_configure, .this_id = -1, .can_queue = ISCSI_CANQUEUE, .sg_tablesize = ISCSI_MAX_SG, .cmd_per_lun = ISCSI_CMDS_PER_LUN, .use_clustering = ENABLE_CLUSTERING, .max_sectors = 256, .emulated = 1, .shost_attrs = iscsi_host_attrs,};intiscsi_create_host(struct iscsi_session_ioctl *ioctld){ struct Scsi_Host *shost; struct iscsi_session *session; int rc; shost = scsi_host_alloc(&iscsi_driver_template, sizeof(*session)); if (!shost) return -ENOMEM; shost->max_id = ISCSI_MAX_TARGETS; shost->max_lun = ISCSI_MAX_LUNS; shost->max_channel = ISCSI_MAX_CHANNELS; shost->max_cmd_len = ISCSI_MAX_CMD_LEN; shost->transportt = iscsi_transportt; session = (struct iscsi_session *)shost->hostdata; memset(session, 0, sizeof(*session)); session->shost = shost; rc = iscsi_create_session(session, ioctld); if (rc) { scsi_host_put(shost); return rc; } rc = scsi_add_host(shost, NULL); if (rc) { iscsi_destroy_session(session); scsi_host_put(shost); return rc; } scsi_scan_host(shost); set_bit(SESSION_CREATED, &session->control_bits); return 0;}/* * This function must only be called when the sysfs and * ioctl interfaces are inaccessible. For example when * the module_exit function is executed the driver's sysfs * and ioctl entry points will return "no device". */static voidiscsi_destroy_all_hosts(void){ struct iscsi_session *session, *tmp; list_for_each_entry_safe(session, tmp, &iscsi_sessions, list) iscsi_destroy_host(session->shost);}static intiscsi_reboot_notifier_function(struct notifier_block *this, unsigned long code, void *unused){ iscsi_destroy_all_hosts(); iscsi_notice("Driver shutdown completed\n"); return NOTIFY_DONE;}/* XXX move this to driver model shutdown */static struct notifier_block iscsi_reboot_notifier = { .notifier_call = iscsi_reboot_notifier_function, .next = NULL, .priority = 255, /* priority, might need to have a * relook at the value */};static int__init iscsi_init(void){ iscsi_notice("%s (%s) built for Linux %s\n", ISCSI_DRIVER_VERSION, ISCSI_DATE, UTS_RELEASE); /* pool of iscsi tasks */ iscsi_task_cache = kmem_cache_create("iscsi_task_cache", sizeof(struct iscsi_task), 0, SLAB_NO_REAP, NULL, NULL); if (!iscsi_task_cache) { iscsi_err("kmem_cache_create failed\n"); return -ENOMEM; } iscsi_transportt = iscsi_attach_transport(&iscsi_fnt); if (!iscsi_transportt) goto free_cache; if (iscsi_register_interface()) goto release_transport; register_reboot_notifier(&iscsi_reboot_notifier); return 0; release_transport: iscsi_release_transport(iscsi_transportt); free_cache: kmem_cache_destroy(iscsi_task_cache); iscsi_err("Failed to init driver\n"); return -ENODEV;}static void__exit iscsi_cleanup(void){ unregister_reboot_notifier(&iscsi_reboot_notifier); iscsi_unregister_interface(); iscsi_destroy_all_hosts(); iscsi_release_transport(iscsi_transportt); kmem_cache_destroy(iscsi_task_cache);}module_init(iscsi_init);module_exit(iscsi_cleanup);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?