The dummy RAID controller serves 2 purposes: (1) commands that are addressed to an inexistent LUN are redirected to it (2) it provides a LUN 0 by default which is required by the SCSI spec . (1) is obviously wrong because instead of "wrong lun" "invalid cdb" is returned to the initiator. A "shadow LUN" of type NO_LUN is now used for this purpose. This LU uses bs_null as backingstore, so there are no idle threads spawned for it (in contrast to the previous dummy raid controller at LUN 0). (2) confuses some OSes / users (Windows prompts for drivers, Solaris repeatedly tries to online the LU, but does not succeed). It's now the user's responsibility to attach a LU to LUN 0 to adhere to the SCSI spec (Solaris / WinXP don't insist, Linux does!). Signed-off-by: Arne Redlich <arne.redlich@xxxxxxxxxxxxxx> --- usr/Makefile | 2 +- usr/list.h | 3 ++ usr/nolun.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ usr/spc.c | 10 ++++++ usr/spc.h | 1 + usr/target.c | 24 +++++++++----- usr/target.h | 2 + usr/tgtadm.c | 5 +-- 8 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 usr/nolun.c diff --git a/usr/Makefile b/usr/Makefile index 6019a2e..17ce671 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -62,7 +62,7 @@ LIBS += -lpthread PROGRAMS += tgtd tgtadm tgtimg SCRIPTS += ../scripts/tgt-setup-lun ../scripts/tgt-admin TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \ - parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o \ + parser.o spc.o sbc.o mmc.o nolun.o osd.o scc.o smc.o \ ssc.o bs_ssc.o libssc.o \ bs_null.o bs_sg.o bs.o libcrc32c.o MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-admin.8 \ diff --git a/usr/list.h b/usr/list.h index 4d76057..2df7c7c 100644 --- a/usr/list.h +++ b/usr/list.h @@ -32,6 +32,9 @@ static inline void INIT_LIST_HEAD(struct list_head *list) #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + static inline int list_empty(const struct list_head *head) { return head->next == head; diff --git a/usr/nolun.c b/usr/nolun.c new file mode 100644 index 0000000..0f516a0 --- /dev/null +++ b/usr/nolun.c @@ -0,0 +1,95 @@ +/* + * Processing of SCSI commands addressed to inexistent LUNs. + * + * Based on scc.c: + * + * Copyright (C) 2007 FUJITA Tomonori <tomof@xxxxxxx> + * Copyright (C) 2007 Mike Christie <michaelc@xxxxxxxxxxx> + * + * 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, version 2 of the + * License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <linux/fs.h> + +#include "list.h" +#include "util.h" +#include "tgtd.h" +#include "target.h" +#include "driver.h" +#include "scsi.h" +#include "tgtadm_error.h" +#include "spc.h" + +static int nolun_lu_init(struct scsi_lu *lu) +{ + memset(&lu->attrs, 0x0, sizeof(lu->attrs)); + /* + * TODO: use PQ 11b (per. dev. supp but not connected) and dev type 0x0 + * (SBC-2) instead? + */ + lu->attrs.device_type = 0x1f; /* unknown / no device type */ + lu->attrs.qualifier = 0x3; /* peripheral dev. not supp. on this LU */ + + return 0; +} + +static int nolun_lu_config(struct scsi_lu *lu __attribute__((unused)), + char *params __attribute__((unused))) +{ + return TGTADM_NO_LUN; +} + +static int nolun_lu_online(struct scsi_lu *lu __attribute__((unused))) +{ + /* TODO: correct return value? most consumers don't check at all ... */ + return -ENOENT; +} + +static int nolun_lu_offline(struct scsi_lu *lu __attribute__((unused))) +{ + return 0; +} + +static void nolun_lu_exit(struct scsi_lu *lu __attribute__((unused))) +{ + return; +} + +static struct device_type_template nolun_template = { + .type = TYPE_NO_LUN, + .lu_init = nolun_lu_init, + .lu_config = nolun_lu_config, + .lu_online = nolun_lu_online, + .lu_offline = nolun_lu_offline, + .lu_exit = nolun_lu_exit, + .ops = { + [0x0 ... 0x11] = {spc_invalid_lun,}, + {spc_inquiry,}, /* 0x12 */ + [ 0x13 ... 0x9f] = {spc_invalid_lun}, + {spc_report_luns,}, /* 0xA0 */ + [0xa1 ... 0xff] = {spc_invalid_lun}, + } +}; + +__attribute__((constructor)) static void nolun_init(void) +{ + device_type_register(&nolun_template); +} diff --git a/usr/spc.c b/usr/spc.c index 1a75766..5e3f896 100644 --- a/usr/spc.c +++ b/usr/spc.c @@ -271,6 +271,8 @@ int spc_report_luns(int host_no, struct scsi_cmd *cmd) nr_luns = 0; list_for_each_entry(lu, dev_list, device_siblings) { + if (lu->lun == TGT_SHADOW_LUN) + continue; nr_luns++; if (!alen) @@ -901,6 +903,14 @@ int spc_illegal_op(int host_no, struct scsi_cmd *cmd) return SAM_STAT_CHECK_CONDITION; } +int spc_invalid_lun(int host_no, struct scsi_cmd *cmd) +{ + dump_cdb(cmd); + scsi_set_in_resid_by_actual(cmd, 0); + sense_data_build(cmd, ILLEGAL_REQUEST, ASC_LUN_NOT_SUPPORTED); + return SAM_STAT_CHECK_CONDITION; +} + enum { Opt_scsi_id, Opt_scsi_sn, Opt_vendor_id, Opt_product_id, diff --git a/usr/spc.h b/usr/spc.h index cfc9cf3..22be22e 100644 --- a/usr/spc.h +++ b/usr/spc.h @@ -10,6 +10,7 @@ extern int spc_test_unit(int host_no, struct scsi_cmd *cmd); extern int spc_request_sense(int host_no, struct scsi_cmd *cmd); extern int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd); extern int spc_illegal_op(int host_no, struct scsi_cmd *cmd); +extern int spc_invalid_lun(int host_no, struct scsi_cmd *cmd); extern int spc_lu_init(struct scsi_lu *lu); typedef int (match_fn_t)(struct scsi_lu *lu, char *params); diff --git a/usr/target.c b/usr/target.c index b7ea99d..13177ab 100644 --- a/usr/target.c +++ b/usr/target.c @@ -460,7 +460,10 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params, goto out; } - bst = target->bst; + bst = (lun == TGT_SHADOW_LUN) ? + get_backingstore_template("null") : + target->bst; + if (backing) { if (bstype) { bst = get_backingstore_template(bstype); @@ -826,11 +829,11 @@ int target_cmd_queue(int tid, struct scsi_cmd *cmd) cmd->dev_id = dev_id; dprintf("%p %x %" PRIx64 "\n", cmd, cmd->scb[0], dev_id); cmd->dev = device_lookup(target, dev_id); - /* use LUN0 */ + /* use TGT_SHADOW_LUN */ if (!cmd->dev) - cmd->dev = list_first_entry(&target->device_list, - struct scsi_lu, - device_siblings); + cmd->dev = list_last_entry(&target->device_list, + struct scsi_lu, + device_siblings); cmd->itn_lu_info = it_nexus_lu_info_lookup(itn, cmd->dev->lun); @@ -1587,7 +1590,9 @@ int tgt_target_show_all(char *buf, int rest) } shprintf(total, buf, rest, _TAB1 "LUN information:\n"); - list_for_each_entry(lu, &target->device_list, device_siblings) + list_for_each_entry(lu, &target->device_list, device_siblings) { + if (lu->lun == TGT_SHADOW_LUN) + continue; shprintf(total, buf, rest, _TAB2 "LUN: %" PRIu64 "\n" _TAB3 "Type: %s\n" @@ -1605,6 +1610,7 @@ int tgt_target_show_all(char *buf, int rest) lu->attrs.online ? "Yes" : "No", lu->attrs.removable ? "Yes" : "No", lu->path ? : "No backing store"); + } if (!strcmp(tgt_drivers[target->lid]->name, "iscsi")) { int i, aid; @@ -1721,7 +1727,7 @@ int tgt_target_create(int lld, int tid, char *args) INIT_LIST_HEAD(&target->acl_list); INIT_LIST_HEAD(&target->it_nexus_list); - tgt_device_create(tid, TYPE_RAID, 0, NULL, 0); + tgt_device_create(tid, TYPE_NO_LUN, TGT_SHADOW_LUN, NULL, 0); if (tgt_drivers[lld]->target_create) tgt_drivers[lld]->target_create(target); @@ -1748,8 +1754,8 @@ int tgt_target_destroy(int lld_no, int tid) } while (!list_empty(&target->device_list)) { - /* we remove lun0 last */ - lu = list_entry(target->device_list.prev, struct scsi_lu, + /* we remove TGT_SHADOW_LUN last */ + lu = list_entry(target->device_list.next, struct scsi_lu, device_siblings); ret = tgt_device_destroy(tid, lu->lun, 1); if (ret) diff --git a/usr/target.h b/usr/target.h index 8fe30aa..26e2377 100644 --- a/usr/target.h +++ b/usr/target.h @@ -8,6 +8,8 @@ #define HASH_ORDER 4 #define hashfn(val) hash_long((unsigned long) (val), HASH_ORDER) +#define TGT_SHADOW_LUN 0xffffffffffffffffULL + struct acl_entry { char *address; struct list_head aclent_list; diff --git a/usr/tgtadm.c b/usr/tgtadm.c index 8d9f7e2..cc4e2fd 100644 --- a/usr/tgtadm.c +++ b/usr/tgtadm.c @@ -673,10 +673,7 @@ int main(int argc, char **argv) eprintf("'tid' option is necessary\n"); exit(EINVAL); } - if (!lun) { - eprintf("'lun' option is necessary\n"); - exit(EINVAL); - } + switch (op) { case OP_NEW: rc = verify_mode_params(argc, argv, "LmotlbEY"); -- 1.6.0.4 -- To unsubscribe from this list: send the line "unsubscribe stgt" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html