From 9c935fdd212ce1263ca10d1f5822bf2f529f3d03 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Thu, 9 Sep 2010 14:52:42 +0000 Subject: [PATCH] More work on scst_local: - Add sessions (SCSI hosts) creation/delete commands as well as fixes and cleanups - Docs updated git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2073 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst_local/README | 105 +++++++++++++++--- scst_local/scst_local.c | 233 +++++++++++++++++++++++++++++++--------- 2 files changed, 270 insertions(+), 68 deletions(-) diff --git a/scst_local/README b/scst_local/README index 8493b5d62..2ecee1c4f 100644 --- a/scst_local/README +++ b/scst_local/README @@ -14,12 +14,10 @@ except the following: you can't mount file systems or put swap on them. This is a limitation of Linux memory/cache manager. See SCST README file for details. -To build, simply issue 'make' in the scst-local directory. +To build, simply issue 'make' in the scst_local directory. Try 'modinfo scst_local' for a listing of module parameters so far. -NOTE! This is now part of scst and supports being built in the scst tree. - Here is how I have used it so far: 1. Load up scst: @@ -30,16 +28,12 @@ Here is how I have used it so far: 2. Create a virtual disk (or your own device handler): dd if=/dev/zero of=/some/path/vdisk1.img bs=16384 count=1000000 -# dd if=/dev/zero of=/some/path/vdisk2.img bs=16384 count=1000000 - echo "open vm_disk1 /some/path/vdisk1.img" > /proc/scsi_tgt/vdisk/vdisk -# echo "open vm_disk2 /some/path/vdisk2.img" > /proc/scsi_tgt/vdisk/vdisk - echo "add vm_disk1 0" > /proc/scsi_tgt/groups/Default/devices -# echo "add vm_disk2 1" > /proc/scsi_tgt/groups/Default/devices + echo "add_device vm_disk1 filename=/some/path/vdisk1.img" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt 3. Load the scst_local driver: - insmod scst_local -# insmod scst_local max_luns=8 + insmod scst_local add_default_tgt=1 + echo "add vm_disk1 0" >/sys/kernel/scst_tgt/targets/scst_local/scst_local_tgt/luns/mgmt 4. Check what you have @@ -52,9 +46,41 @@ Here is how I have used it so far: Vendor: TSSTcorp Model: CD/DVDW TS-L632D Rev: TO04 Type: CD-ROM ANSI SCSI revision: 05 Host: scsi7 Channel: 00 Id: 00 Lun: 00 - Vendor: SCST_FIO Model: vm_disk1 Rev: 101 + Vendor: SCST_FIO Model: vm_disk1 Rev: 200 Type: Direct-Access ANSI SCSI revision: 04 +Or for (3) you can: + + insmod scst_local + echo "add_target scst_local_tgt session_name=scst_local_host" >/sys/kernel/scst_tgt/targets/scst_local//mgmt + echo "add vm_disk1 0" >/sys/kernel/scst_tgt/targets/scst_local/scst_local_tgt/luns/mgmt + +Or instead of manually "add_device" in (2) and (3) write and scstadmin config +(doesn't create the session at the moment due to bug in scstadmin): + +HANDLER vdisk_fileio { + DEVICE vm_disk1 { + filename /some/path/vdisk1.img + } +} + +TARGET_DRIVER scst_local { + TARGET scst_local_tgt { + session_name=scst_local_host + + LUN 0 vm_disk1 + } +} + +then: + + insmod scst_local + scstadmin -config conf_file.cfg + +There can be any number of targets and sessions created. Each SCST +session corresponds to SCSI host. You can change which LUNs assigned to +each session by using SCST access control. + 5. Have fun. Some of this was coded while in Santa Clara, some in Bangalore, and some in @@ -67,13 +93,57 @@ The code still has bugs, so if you encounter any, email me the fixes at: I am thinking of renaming this to something more interesting. -Limitations -=========== +Sysfs interface +=============== -Swapping on scst_local's devices and using a writable mmap over a file -on them are not supported due to possible OOM deadlock in the Linux -memory/cache manager. See SCST README for more details. +See SCST's README for a common SCST sysfs description. +Root of this driver is /sys/kernel/scst_tgt/targets/scst_local. It has +the following additional entry: + + - stats - read-only attribute with some statistical information. + +Each target subdirectory contains the following additional entries: + + - phys_transport_version - contains and allows to change physical + transport version descriptor. It determines by which phisical + interface this target will look like. See SPC for more details. By + default, it is not defined (0). + + - scsi_transport_version - contains and allows to change SCSI + transport version descriptor. It determines by which SCSI + transport this target will look like. See SPC for more details. By + default, it is SAS. + +Each session subdirectory contains the following additional entries: + + - transport_id - contains this host's TransportID. This TransportID + used to identify initiator in Persisten Reservation commands. If you + change scsi_transport_version for a target, make sure you set for all + its sessions correct TransportID. See SPC for more details. + + - host - links to the corresponding SCSI host. Using it you can find + local sg/bsg/sd/etc. devices of this session. For instance, this + links points out to host12, so you can find your sg devices by: + +$ lsscsi -g|grep "\[12" +[12:0:0:0] disk SCST_FIO rd1 200 /dev/sdc /dev/sg2 +[12:0:0:1] disk SCST_FIO nullio 200 /dev/sdd /dev/sg3 + +They are /dev/sg2 and /dev/sg3. + +The following management commands available via /sys/kernel/scst_tgt/targets/scst_local/mgmt: + + - add_target target_name [session_name=sess_name; [session_name=sess_name1;] [...]] - + creates a target with optionally one or more sessions. + + - del_target target_name - deletes a target. + + - add_session target_name session_name - adds to target target_name + session (SCSI host) with name session_name. + + - del_session target_name session_name - deletes session session_name + from target target_name. Note on performance =================== @@ -122,3 +192,6 @@ V0.7 11-Oct-2008 (Santa Clara) Moved into the scst tree. Cleaned up some V0.9 30-Nov-2008 (Mtn View) Cleaned up an additional problem with symbols not being defined in older version of the kernel. Also fixed some English and cleaned up this doc. + +V1.0 10-Sep-2010 (Moscow) Sysfs management added. Reviewed and cleaned up. + diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 64be5dd98..da5d52089 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -90,16 +90,11 @@ static atomic_t num_aborts = ATOMIC_INIT(0); static atomic_t num_dev_resets = ATOMIC_INIT(0); static atomic_t num_target_resets = ATOMIC_INIT(0); -bool scst_local_add_default_tgt = true; +bool scst_local_add_default_tgt = false; module_param_named(add_default_tgt, scst_local_add_default_tgt, bool, S_IRUGO); -MODULE_PARM_DESC(add_default_host, "add (default) or not default target with " - "default session"); +MODULE_PARM_DESC(add_default_host, "add or not (default) on start default " + "target scst_local_tgt with default session scst_local_host"); -/* - * We put AEN items on a list on the scst_local_host_info structure so they - * can be processed via a work function. We do not actually need the LUN, since - * the whole target must be rescanned anyway. - */ struct scst_aen_work_item { struct list_head work_list_entry; struct scst_aen *aen; @@ -116,12 +111,14 @@ struct scst_local_tgt { }; struct scst_local_sess { + struct scst_session *scst_sess; + + unsigned int unregistering:1; + struct device dev; struct Scsi_Host *shost; struct scst_local_tgt *tgt; - struct scst_session *scst_sess; - int number; struct mutex tr_id_mutex; @@ -133,13 +130,14 @@ struct scst_local_sess { struct list_head aen_work_list; /* protected by aen_lock */ struct list_head sessions_list_entry; - - char *initiator_name; }; #define to_scst_lcl_sess(d) \ container_of(d, struct scst_local_sess, dev) +static int __scst_local_add_adapter(struct scst_local_tgt *tgt, + const char *initiator_name, struct scst_local_sess **out_sess, + bool locked); static int scst_local_add_adapter(struct scst_local_tgt *tgt, const char *initiator_name, struct scst_local_sess **out_sess); static void scst_local_remove_adapter(struct scst_local_sess *sess); @@ -684,6 +682,80 @@ out_up: return res; } +static ssize_t scst_local_sysfs_mgmt_cmd(char *buf) +{ + ssize_t res; + char *command, *target_name, *session_name; + struct scst_local_tgt *t, *tgt; + + TRACE_ENTRY(); + + if (down_read_trylock(&scst_local_exit_rwsem) == 0) + return -ENOENT; + + command = scst_get_next_lexem(&buf); + + target_name = scst_get_next_lexem(&buf); + if (*target_name == '\0') { + PRINT_ERROR("%s", "Target name required"); + res = -EINVAL; + goto out_up; + } + + mutex_lock(&scst_local_mutex); + + tgt = NULL; + list_for_each_entry(t, &scst_local_tgts_list, tgts_list_entry) { + if (strcmp(t->scst_tgt->tgt_name, target_name) == 0) { + tgt = t; + break; + } + } + if (tgt == NULL) { + PRINT_ERROR("Target %s not found", target_name); + res = -EINVAL; + goto out_unlock; + } + + session_name = scst_get_next_lexem(&buf); + if (*session_name == '\0') { + PRINT_ERROR("%s", "Session name required"); + res = -EINVAL; + goto out_unlock; + } + + if (strcasecmp("add_session", command) == 0) { + res = __scst_local_add_adapter(tgt, session_name, NULL, true); + } else if (strcasecmp("del_session", command) == 0) { + struct scst_local_sess *s, *sess = NULL; + list_for_each_entry(s, &tgt->sessions_list, + sessions_list_entry) { + if (strcmp(s->scst_sess->initiator_name, session_name) == 0) { + sess = s; + break; + } + } + if (sess == NULL) { + PRINT_ERROR("Session %s not found (target %s)", + session_name, target_name); + res = -EINVAL; + goto out_unlock; + } + scst_local_remove_adapter(sess); + } + + res = 0; + +out_unlock: + mutex_unlock(&scst_local_mutex); + +out_up: + up_read(&scst_local_exit_rwsem); + + TRACE_EXIT_RES(res); + return res; +} + #endif /* CONFIG_SCST_PROC */ static int scst_local_abort(struct scsi_cmnd *SCpnt) @@ -847,6 +919,27 @@ static int scst_local_queuecommand(struct scsi_cmnd *SCpnt, scsi_set_resid(SCpnt, 0); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) + /* + * Allocate a tgt_specific_structure. We need this in case we need + * to construct a single element SGL. + */ + tgt_specific = kmem_cache_alloc(tgt_specific_pool, GFP_ATOMIC); + if (!tgt_specific) { + PRINT_ERROR("Unable to create tgt_specific (size %d)", + sizeof(*tgt_specific)); + return -ENOMEM; + } + tgt_specific->cmnd = SCpnt; + tgt_specific->done = done; +#else + /* + * We save a pointer to the done routine in SCpnt->scsi_done and + * we save that as tgt specific stuff below. + */ + SCpnt->scsi_done = done; +#endif + /* * Tell the target that we have a command ... but first we need * to get the LUN into a format that SCST understand @@ -877,27 +970,6 @@ static int scst_local_queuecommand(struct scsi_cmnd *SCpnt, break; } -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) - /* - * Allocate a tgt_specific_structure. We need this in case we need - * to construct a single element SGL. - */ - tgt_specific = kmem_cache_alloc(tgt_specific_pool, GFP_ATOMIC); - if (!tgt_specific) { - PRINT_ERROR("Unable to create tgt_specific (size %d)", - sizeof(*tgt_specific)); - return -ENOMEM; - } - tgt_specific->cmnd = SCpnt; - tgt_specific->done = done; -#else - /* - * We save a pointer to the done routine in SCpnt->scsi_done and - * we save that as tgt specific stuff below. - */ - SCpnt->scsi_done = done; -#endif - #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) /* * If the command has a request, not a scatterlist, then convert it @@ -1003,17 +1075,16 @@ static int scst_local_targ_pre_exec(struct scst_cmd *scst_cmd) return res; } -static void scst_aen_work_fn(struct work_struct *work) +/* Must be called under sess->aen_lock. Drops then reacquires it inside. */ +static void scst_process_aens(struct scst_local_sess *sess, + bool cleanup_only) { - struct scst_local_sess *sess = - container_of(work, struct scst_local_sess, aen_work); struct scst_aen_work_item *work_item = NULL; TRACE_ENTRY(); - TRACE_MGMT_DBG("Target work (sess %p)", sess); + TRACE_DBG("Target work sess %p", sess); - spin_lock(&sess->aen_lock); while (!list_empty(&sess->aen_work_list)) { work_item = list_entry(sess->aen_work_list.next, struct scst_aen_work_item, work_list_entry); @@ -1021,14 +1092,37 @@ static void scst_aen_work_fn(struct work_struct *work) spin_unlock(&sess->aen_lock); + if (cleanup_only) + goto done; + + sBUG_ON(work_item->aen->event_fn != SCST_AEN_SCSI); + + /* Let's always rescan */ scsi_scan_target(&sess->shost->shost_gendev, 0, 0, SCAN_WILD_CARD, 1); +done: scst_aen_done(work_item->aen); kfree(work_item); spin_lock(&sess->aen_lock); } + + TRACE_EXIT(); + return; +} + +static void scst_aen_work_fn(struct work_struct *work) +{ + struct scst_local_sess *sess = + container_of(work, struct scst_local_sess, aen_work); + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Target work %p)", work); + + spin_lock(&sess->aen_lock); + scst_process_aens(sess, false); spin_unlock(&sess->aen_lock); TRACE_EXIT(); @@ -1059,8 +1153,17 @@ static int scst_local_report_aen(struct scst_aen *aen) } spin_lock(&sess->aen_lock); + + if (unlikely(sess->unregistering)) { + spin_unlock(&sess->aen_lock); + kfree(work_item); + res = SCST_AEN_RES_NOT_SUPPORTED; + goto out; + } + list_add_tail(&work_item->work_list_entry, &sess->aen_work_list); work_item->aen = aen; + spin_unlock(&sess->aen_lock); schedule_work(&sess->aen_work); @@ -1072,6 +1175,7 @@ static int scst_local_report_aen(struct scst_aen *aen) break; } +out: TRACE_EXIT_RES(res); return res; } @@ -1209,12 +1313,15 @@ static struct scst_tgt_template scst_local_targ_tmpl = { .xmit_response_atomic = 1, #ifndef CONFIG_SCST_PROC .enabled_attr_not_needed = 1, - .tgtt_attrs = scst_local_tgtt_attrs, - .tgt_attrs = scst_local_tgt_attrs, - .sess_attrs = scst_local_sess_attrs, - .add_target = scst_local_sysfs_add_target, - .del_target = scst_local_sysfs_del_target, - .add_target_parameters = "session_name", + .tgtt_attrs = scst_local_tgtt_attrs, + .tgt_attrs = scst_local_tgt_attrs, + .sess_attrs = scst_local_sess_attrs, + .add_target = scst_local_sysfs_add_target, + .del_target = scst_local_sysfs_del_target, + .mgmt_cmd = scst_local_sysfs_mgmt_cmd, + .add_target_parameters = "session_name", + .mgmt_cmd_help = " echo \"add_session target_name session_name\" >mgmt\n" + " echo \"del_session target_name session_name\" >mgmt\n", #endif .detect = scst_local_targ_detect, .release = scst_local_targ_release, @@ -1383,6 +1490,11 @@ static void scst_local_release_adapter(struct device *dev) if (sess == NULL) goto out; + spin_lock(&sess->aen_lock); + sess->unregistering = 1; + scst_process_aens(sess, true); + spin_unlock(&sess->aen_lock); + cancel_work_sync(&sess->aen_work); scst_unregister_session(sess->scst_sess, TRUE, NULL); @@ -1394,8 +1506,9 @@ out: return; } -static int scst_local_add_adapter(struct scst_local_tgt *tgt, - const char *initiator_name, struct scst_local_sess **out_sess) +static int __scst_local_add_adapter(struct scst_local_tgt *tgt, + const char *initiator_name, struct scst_local_sess **out_sess, + bool locked) { int res; struct scst_local_sess *sess; @@ -1426,7 +1539,7 @@ static int scst_local_add_adapter(struct scst_local_tgt *tgt, if (sess->scst_sess == NULL) { PRINT_ERROR("%s", "scst_register_session failed"); kfree(sess); - res = -ENOMEM; + res = -EFAULT; goto out_free; } @@ -1436,7 +1549,11 @@ static int scst_local_add_adapter(struct scst_local_tgt *tgt, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) snprintf(sess->dev.bus_id, sizeof(sess->dev.bus_id), initiator_name); #else +# ifdef CONFIG_SCST_PROC sess->dev.init_name = sess->scst_sess->initiator_name; +# else + sess->dev.init_name = kobject_name(&sess->scst_sess->sess_kobj); +#endif #endif res = device_register(&sess->dev); @@ -1453,9 +1570,13 @@ static int scst_local_add_adapter(struct scst_local_tgt *tgt, } #endif - mutex_lock(&scst_local_mutex); + if (!locked) + mutex_lock(&scst_local_mutex); list_add_tail(&sess->sessions_list_entry, &tgt->sessions_list); - mutex_unlock(&scst_local_mutex); + if (!locked) + mutex_unlock(&scst_local_mutex); + + scsi_scan_target(&sess->shost->shost_gendev, 0, 0, SCAN_WILD_CARD, 1); out: TRACE_EXIT_RES(res); @@ -1474,6 +1595,12 @@ out_free: goto out; } +static int scst_local_add_adapter(struct scst_local_tgt *tgt, + const char *initiator_name, struct scst_local_sess **out_sess) +{ + return __scst_local_add_adapter(tgt, initiator_name, out_sess, false); +} + /* Must be called under scst_local_mutex */ static void scst_local_remove_adapter(struct scst_local_sess *sess) { @@ -1507,7 +1634,7 @@ static int scst_local_add_target(const char *target_name, tgt->scst_tgt = scst_register_target(&scst_local_targ_tmpl, target_name); if (tgt->scst_tgt == NULL) { PRINT_ERROR("%s", "scst_register_target() failed:"); - res = -ENOMEM; + res = -EFAULT; goto out_free; } @@ -1547,6 +1674,8 @@ static void __scst_local_remove_target(struct scst_local_tgt *tgt) scst_unregister_target(tgt->scst_tgt); + kfree(tgt); + TRACE_EXIT(); return; } @@ -1631,11 +1760,11 @@ static int __init scst_local_init(void) if (!scst_local_add_default_tgt) goto out; - ret = scst_local_add_target("default_tgt", &tgt); + ret = scst_local_add_target("scst_local_tgt", &tgt); if (ret != 0) goto tgt_templ_unreg; - ret = scst_local_add_adapter(tgt, "default_scst_local_sess", NULL); + ret = scst_local_add_adapter(tgt, "scst_local_host", NULL); if (ret != 0) goto tgt_unreg;