📄 iscsi_initiator.c
字号:
/* initiator/iscsi_initiator.c * * vi: set autoindent tabstop=8 shiftwidth=8 : * * This file contains the core functions for iscsi initiator code that are * responsible for connecting and maintaining sessions between initiator * and target. The object file generated is the loadable kernel module. * * Copyright (C) 2001-2004 InterOperability Lab (IOL) * University of New Hampshire (UNH) * Durham, NH 03824 * * 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, 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * The name of IOL and/or UNH may not be used to endorse or promote * products derived from this software without specific prior * written permission.*/#include "iscsi_initiator.h"#include "initiator_utilities.h"#include "initiator_error_rec.h"#include "initiator_proc_iface.h"#include "../common/lun_packing.h"#include <linux/reboot.h>/* max number of redirects to follow during login on any connection */#define MAX_REDIRECTION_RETRIES 2/* Holds pointer to private data part of Scsi_Host structure. * This is set up once, by iscsi_detect() * which is called when the initiator module is loaded */struct iscsi_hostdata *global_hostdata = NULL;/* Holds pointer to the registered Scsi_Host */struct Scsi_Host *global_host = NULL;/* Used for all access to the shared structures, and during the operation * of any single thread to prevent things from disappearing from under * the thread.*//* dummy that always returns false */inttarget_in_use(__u32 id){return 0;}UNH_LOCK_TYPE host_data_lock = UNH_LOCK_UNLOCKED;/* Mike Christie, mikenc@us.ibm.com */static int iscsi_initiator_halt(struct notifier_block *nb, ulong event, void *buf);/* Mike Christie, mikenc@us.ibm.com *//* reboot notifier */static struct notifier_block iscsi_initiator_notifier = { iscsi_initiator_halt, NULL, 0};/* * either the session->sess_lock MUST be held by the calling process/thread * or the session structure must not be active yet. * increase maximum number command structures session may use to: * (MAX_COMMANDS_PER_LUN + 1) * n_luns_in_session * (one extra in case of task-management and/or nopout commands) * Returns 1 on success, 0 on failure. */static int __attribute__ ((no_instrument_function))preallocate_commands(struct session *sess){ __u32 limit; struct command *new_command; limit = (MAX_COMMANDS_PER_LUN + 1) * sess->n_luns_in_session; while (sess->n_commands_alloced < limit) { new_command = (struct command *)my_kmalloc( sizeof(struct command), "command"); if (unlikely(new_command == NULL)) { return 0; } /* each new command goes at end of session's free list */ endup_command(new_command, sess); sess->n_commands_alloced++; } TRACE(TRACE_DEBUG,"%u commands now allocated to session %p target %u\n", sess->n_commands_alloced, sess, sess->scsi_target_id); return 1;}/* * the session->sess_lock MUST be held by the calling process/thread. * reduce maximum number command structures session may use to: * (MAX_COMMANDS_PER_LUN + 1) * n_luns_in_session */static void __attribute__ ((no_instrument_function))unallocate_commands(struct session *sess){ __u32 limit; struct command *old_command; struct list_head *lptr; limit = (MAX_COMMANDS_PER_LUN + 1) * sess->n_luns_in_session; while (sess->n_commands_alloced > limit && !list_empty(&sess->free_commands)) { lptr = sess->free_commands.next; list_del(lptr); old_command = list_entry(lptr, struct command, link); my_kfree((void **)&old_command, "command"); sess->n_commands_alloced--; } TRACE(TRACE_DEBUG,"%u commands now allocated to session %p target %u\n", sess->n_commands_alloced, sess, sess->scsi_target_id);}/* initialize all fields in struct iscsi_targetdata for one target */voidinit_targetdata(struct iscsi_targetdata *targ_data){ /* Initialize all fields to 0 or NULL */ memset(targ_data, 0, sizeof(struct iscsi_targetdata)); /* now go back and initialize those that are not 0 or NULL by default */ targ_data->isid_type = DEFAULT_ISID_TYPE; targ_data->isid_number = DEFAULT_ISID_NUMBER; targ_data->isid_qualifier = DEFAULT_ISID_QUALIFIER; targ_data->auth_parameter.chap_local_ctx = CHAP_InitializeContext(); targ_data->auth_parameter.chap_peer_ctx = CHAP_InitializeContext(); targ_data->auth_parameter.srp_ctx = SRP_InitializeContext(); /* Initialize parameter list to a copy of default set of parameters */ param_tbl_init(targ_data->param_tbl);}/* uninitialize fields in struct iscsi_targetdata that need freeing */voiduninit_targetdata(struct iscsi_targetdata *targ_data){ param_tbl_uncpy(targ_data->param_tbl); SRP_FinalizeContext(targ_data->auth_parameter.srp_ctx); CHAP_FinalizeContext(targ_data->auth_parameter.chap_peer_ctx); CHAP_FinalizeContext(targ_data->auth_parameter.chap_local_ctx);}/**************************************************************************** Called from iscsi_initiator_detect() and unh_iscsi_probe().* Initializes the hostdata structure.* Creates the parameter table for this hostdata.* Returns non-NULL pointer to parameter table if ok, NULL if error.***************************************************************************/static void __attribute__ ((no_instrument_function))init_initiator(struct iscsi_hostdata *hostdata){ int i; struct iscsi_targetdata *targ_data; TRACE(TRACE_ENTER_LEAVE, "Enter init_initiator, hostdata %p\n", hostdata); /* Initialize all fields to 0 or NULL */ memset(hostdata, 0, sizeof(struct iscsi_hostdata)); /* now go back and initialize those that are not 0 or NULL by default */ INIT_LIST_HEAD(&hostdata->session_list); for (i = 0, targ_data = hostdata->target_data; i < MAX_TARGET_IDS; targ_data++, i++) { init_targetdata(targ_data); } TRACE(TRACE_ENTER_LEAVE, "Leave init_initiator\n");}/**************************************************************************** Called by the scsi subsystem when the iscsi_initiator module is loaded.* (via the detect field in the Scsi_Host_Template driver_template)* This allows the iscsi_initiator driver code to register itself with the* SCSI Mid-level as being the driver for this type of host adapter.* It registers space for iscsi_hostdata struct with the SCSI Mid-level.* When called, io_request_lock is held by the midlevel caller.* It returns:* n > 0 if ok, where n is number of host adapters of this type* on this system (all will be driven by this code);* n < 0 on error.***************************************************************************/static intiscsi_initiator_detect(Scsi_Host_Template * tmpt){ int retval = 1; struct Scsi_Host *host = NULL; TRACE(TRACE_ENTER_LEAVE, "Enter iscsi_initiator_detect, tmpt %p\n", tmpt); /* scsi_register() is defined in linux/drivers/scsi/hosts.c */ host = scsi_register(tmpt, sizeof(struct iscsi_hostdata)); TRACE(TRACE_DEBUG, "Back from scsi_register, host %p\n", host); if (unlikely(host == NULL)) { retval = -1; goto out; } TRACE(TRACE_ISCSI, "registered UNH iSCSI host number %d\n", host->host_no); /* setup the security key hash table */ setup_security_hash_table(); /* three standard default values which can be overridden: * host->max_channel = 0; * host->max_id = 8; * host->max_lun = 8; */ /* set the number of allowed "targets" this adaptor "card" can handle. * the "target" numbers will be [0..max_id-1]. * each "target" will have LUN numbers in [0..max_lun-1]. */ host->max_id = MAX_TARGET_IDS; host->max_lun = MAX_LUNS; /* set the maximum number of bytes we will accept in a scsi cdb */ host->max_cmd_len = MAX_CDB_LEN; /* scsi_register() allocated space for our private structure at the end * of the host structure. This global variable is cheating! * Potentially each (physical) adaptor card can have its own hostdata. * We get away with having a single global variable here because * we know we will never have more than 1 adaptor of this type ever. */ global_hostdata = (struct iscsi_hostdata *) host->hostdata; global_host = host; /* Initialize the adapter's private data structure */ init_initiator(global_hostdata); (void) initiator_register_device(); /* Mike Christie, mikenc@us.ibm.com */ register_reboot_notifier(&iscsi_initiator_notifier); TRACE(TRACE_DEBUG, "Register iSCSI host adapter \"%s\"\n", tmpt->proc_name);out: TRACE(TRACE_ENTER_LEAVE, "Leave iscsi_initiator_detect, retval %d\n", retval); return retval;}/* called only when session->sched_scheme is CONN_SCHED_LUN. * the session->sess_lock MUST be held by the calling process/thread. * the host_data_lock MAY be held by the calling process/thread. * find the connection with the fewest number of LUNs assigned to it * and then to assign this lun to it */static void __attribute__ ((no_instrument_function))locked_add_lun_to_sess(__u32 lun, struct session *session){ __u32 min_count; struct connection *conn, *sofar; /* find the connection with the fewest luns assigned to it */ min_count = MAX_LUNS; sofar = NULL; for (conn = session->connection_head; conn != NULL; conn = conn->next) { if (conn->connection_state == CONNECTION_FULL_FEATURE_PHASE && min_count > conn->assigned_lun_count) { min_count = conn->assigned_lun_count; sofar = conn; } } if (sofar) { session->lun_assignments[lun] = sofar; sofar->assigned_lun_count++; printk("Assign lun %u to connection %u, %u luns assigned\n", lun, sofar->connection_id, sofar->assigned_lun_count); }}/* * when called, NO LOCKS SHOULD BE LOCKED! * find the connection with the fewest number of LUNs assigned to it * and then to assign this lun to it */static void __attribute__ ((no_instrument_function))add_lun_to_sess( __u32 lun, struct session *session ){ unsigned long lock_flags; UNH_LOCK(&session->sess_lock, lock_flags); /* find the connection with the fewest luns assigned to it */ locked_add_lun_to_sess(lun, session); UNH_UNLOCK(&session->sess_lock, lock_flags);}/* * host_data_lock and sess->sess_lock MUST be held by the calling process/thread * called only when this lun is assigned to a specific connection * remove this lun from this session's table of assigned connections.*/static void __attribute__ ((no_instrument_function))locked_drop_lun_from_sess(__u32 lun, struct session *sess, int free_inquiry){ struct connection *conn; /* this lun was assigned to this connection, unassign it now */ conn = sess->lun_assignments[lun]; sess->lun_assignments[lun] = NULL; if (free_inquiry) free_inquiry_stuff(sess, lun); conn->assigned_lun_count--; printk("Unassign lun %u from connection %u, %u luns assigned\n", lun, conn->connection_id, conn->assigned_lun_count);}/* * the host_data_lock MUST be held by the calling process/thread. * remove this lun from this session's table of assigned connections. */static void __attribute__ ((no_instrument_function))drop_lun_from_sess(__u32 lun, struct session *session){ unsigned long lock_flags; if (session->lun_assignments[lun]) { UNH_LOCK(&session->sess_lock, lock_flags); /* this lun was assigned to this connection, unassign it now */ locked_drop_lun_from_sess(lun, session, 1); UNH_UNLOCK(&session->sess_lock, lock_flags); }}/* * when called, NO LOCKS SHOULD BE LOCKED! * find a lun that is already in use in this session. * returns -1 on error, else lun number in range [0..31] */intfind_used_lun(struct session *sess){ int lun; if (unlikely(sess->lun_bits == 0)) { TRACE_ERROR ("No LUNs currently in use by session %p target %u\n", sess, sess->scsi_target_id); lun = -1; } else { /* at least 1 bit set in lun_bits, find the smallest * numbered 1 bit */ for (lun = 0; !(sess->lun_bits & (1 << lun)); lun++) { } } return lun;}/* * when called, NO LOCKS SHOULD BE LOCKED! * Create a new recovery connection to keep the iSCSI session active. * Remember, iscsi requires at least one connection in a session to be active * for the session to be alive - SAI * Returns 1 on success, 0 on failure. */intcreate_rec_conn(struct connection *conn){ __u32 connid; int lun, retval; struct connection *this_conn; struct session *sess = conn->my_session; struct iscsi_targetdata *targ_data; struct sockaddr *new_ip_address; int new_ip_length; unsigned long flags, lock_flags; TRACE(TRACE_ENTER_LEAVE, "Entering create_rec_conn\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -