From f4619f054e719755facb1a0ed3bcece741e01e24 Mon Sep 17 00:00:00 2001 From: Brian Meagher Date: Thu, 22 Feb 2024 16:46:30 -0800 Subject: [PATCH] scst_lib: Allow REPORT LUNS to operate on an unsupported LUN Initiators will attempt to send LUN 0 INQUIRY and REPORT LUNS commands when connecting to a target. Add scst_set_lun_not_supported_report_luns to handle the case when REPORT LUNs is sent to an unsupported LUN. Also, in scst_set_lun_not_supported_inquiry report a valid vendor identification string to promote better initiator behavior. --- scst/src/scst_lib.c | 143 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 349565fa4..7e0a6988a 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -57,6 +57,9 @@ #include "scst_mem.h" #include "scst_pres.h" +/* 8 byte ASCII Vendor */ +#define SCST_NOLUN_VENDOR "SCST " + /* * List and IRQ lock to globally serialize all STPG commands. Needed to * prevent deadlock, if (1) a device group contains multiple devices and @@ -1919,6 +1922,10 @@ static int scst_set_lun_not_supported_inquiry(struct scst_cmd *cmd) buf[2] = 6; /* Device complies to SPC-4 */ buf[4] = len - 4; + /* Set T10 Vendor identification */ + if (len >= 16) + memcpy(&(buf[8]), SCST_NOLUN_VENDOR, 8); + TRACE_BUFFER("INQUIRY for not supported LUN set", buf, len); cmd->data_direction = SCST_DATA_READ; @@ -1933,6 +1940,140 @@ static int scst_set_lun_not_supported_inquiry(struct scst_cmd *cmd) return res; } +static int scst_set_lun_not_supported_report_luns(struct scst_cmd *cmd) +{ + uint8_t *buf; + int dev_cnt = 0; + struct scatterlist *sg; + struct scst_tgt_dev *tgt_dev = NULL; + int i, len; + int res = 0; + int offs, overflow = 0; + + TRACE_ENTRY(); + + if (cmd->status != 0) { + TRACE_MGMT_DBG("cmd %p already has status %x set", + cmd, cmd->status); + res = -EEXIST; + goto out; + } + + if ((cmd->cdb[2] != 0) && (cmd->cdb[2] != 2)) { + TRACE(TRACE_MINOR, + "Unsupported SELECT REPORT value %#x in REPORT LUNS command", + cmd->cdb[2]); + scst_set_invalid_field_in_cdb(cmd, 2, 0); + goto out; + } + + if (cmd->sg == NULL) { + if (cmd->bufflen == 0) { + int bufflen = get_unaligned_be32(&cmd->cdb[6]); + + cmd->bufflen = bufflen ? max_t(int, 8, bufflen) : 8; + } + + /* + * If target driver preparing data buffer using tgt_alloc_data_buf() + * callback, it is responsible to copy the sense to its buffer + * in xmit_response(). + */ + if (cmd->tgt_i_data_buf_alloced && (cmd->tgt_i_sg != NULL)) { + cmd->sg = cmd->tgt_i_sg; + cmd->sg_cnt = cmd->tgt_i_sg_cnt; + TRACE_MEM("Tgt used for REPORTS LUNS (not supported LUN): cmd %p", + cmd); + goto go; + } + + cmd->sg = scst_alloc_sg(cmd->bufflen, GFP_ATOMIC, &cmd->sg_cnt); + if (cmd->sg == NULL) { + PRINT_ERROR("Unable to alloc sg for REPORTS LUNS (not supported LUN)"); + res = 1; + goto out; + } + + TRACE_MEM( + "sg %p (cnt %d, len %d) allocated for REPORTS LUNS (not supported LUN): cmd %p", + cmd->sg, cmd->sg_cnt, cmd->bufflen, cmd); + } + +go: + sg = cmd->sg; + len = sg->length; + + TRACE_MEM("sg %p (len %d) for REPORTS LUNS for cmd %p", sg, len, cmd); + + buf = sg_virt(sg); + len = max_t(int, 8, len); + + memset(buf, 0, len); + offs = 8; + + rcu_read_lock(); + for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { + struct list_head *head = &cmd->sess->sess_tgt_dev_list[i]; + + list_for_each_entry_rcu(tgt_dev, head, + sess_tgt_dev_list_entry) { + struct scst_tgt_dev_UA *ua; + + if (!overflow) { + if ((len - offs) < 8) { + overflow = 1; + goto inc_dev_cnt; + } + *(__force __be64 *)&buf[offs] + = scst_pack_lun(tgt_dev->lun, + cmd->sess->acg->addr_method); + offs += 8; + } +inc_dev_cnt: + dev_cnt++; + + /* Clear sense_reported_luns_data_changed UA. */ + spin_lock_bh(&tgt_dev->tgt_dev_lock); + list_for_each_entry(ua, &tgt_dev->UA_list, + UA_list_entry) { + if (scst_analyze_sense(ua->UA_sense_buffer, + ua->UA_valid_sense_len, + SCST_SENSE_ALL_VALID, + SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed))) { + TRACE_DBG( + "Freeing not needed REPORTED LUNS DATA CHANGED UA %p", + ua); + scst_tgt_dev_del_free_UA(tgt_dev, ua); + break; + } + } + spin_unlock_bh(&tgt_dev->tgt_dev_lock); + } + } + rcu_read_unlock(); + + /* Set the response header */ + dev_cnt *= 8; + put_unaligned_be32(dev_cnt, buf); + + dev_cnt += 8; + if (dev_cnt < len) + len = dev_cnt; + + TRACE_BUFFER("REPORTS LUNS for not supported LUN set", buf, len); + + cmd->data_direction = SCST_DATA_READ; + scst_set_resp_data_len(cmd, len); + + cmd->completed = 1; + cmd->resid_possible = 1; + +out: + TRACE_EXIT_RES(res); + + return res; +} + /* * scst_set_cmd_error() - set error in the command and fill the sense buffer. * @@ -1955,6 +2096,8 @@ int scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq) key, asc, ascq); else if (cmd->cdb[0] == INQUIRY) res = scst_set_lun_not_supported_inquiry(cmd); + else if (cmd->cdb[0] == REPORT_LUNS) + res = scst_set_lun_not_supported_report_luns(cmd); else goto do_sense;