diff options
Diffstat (limited to 'drivers/target/target_core_pscsi.c')
| -rw-r--r-- | drivers/target/target_core_pscsi.c | 609 |
1 files changed, 243 insertions, 366 deletions
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index e992b27aa090..db4e09042469 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -1,26 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * Filename: target_core_pscsi.c * * This file contains the generic target mode <-> Linux SCSI subsystem plugin. * - * (c) Copyright 2003-2012 RisingTide Systems LLC. + * (c) Copyright 2003-2013 Datera, Inc. * * Nicholas A. Bellinger <nab@kernel.org> * - * 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. - * - * 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. - * ******************************************************************************/ #include <linux/string.h> @@ -30,15 +17,12 @@ #include <linux/blk_types.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/genhd.h> #include <linux/cdrom.h> #include <linux/ratelimit.h> #include <linux/module.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> -#include <scsi/scsi.h> #include <scsi/scsi_device.h> -#include <scsi/scsi_cmnd.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> @@ -46,19 +30,16 @@ #include <target/target_core_backend.h> #include "target_core_alua.h" +#include "target_core_internal.h" #include "target_core_pscsi.h" -#define ISPRINT(a) ((a >= ' ') && (a <= '~')) - static inline struct pscsi_dev_virt *PSCSI_DEV(struct se_device *dev) { return container_of(dev, struct pscsi_dev_virt, dev); } -static struct se_subsystem_api pscsi_template; - static sense_reason_t pscsi_execute_cmd(struct se_cmd *cmd); -static void pscsi_req_done(struct request *, int); +static enum rq_end_io_ret pscsi_req_done(struct request *, blk_status_t); /* pscsi_attach_hba(): * @@ -81,7 +62,7 @@ static int pscsi_attach_hba(struct se_hba *hba, u32 host_id) pr_debug("CORE_HBA[%d] - TCM SCSI HBA Driver %s on" " Generic Target Core Stack %s\n", hba->hba_id, - PSCSI_VERSION, TARGET_CORE_MOD_VERSION); + PSCSI_VERSION, TARGET_CORE_VERSION); pr_debug("CORE_HBA[%d] - Attached SCSI HBA to Generic\n", hba->hba_id); @@ -134,10 +115,10 @@ static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag) * pSCSI Host ID and enable for phba mode */ sh = scsi_host_lookup(phv->phv_host_id); - if (IS_ERR(sh)) { + if (!sh) { pr_err("pSCSI: Unable to locate SCSI Host for" " phv_host_id: %d\n", phv->phv_host_id); - return PTR_ERR(sh); + return -EINVAL; } phv->phv_lld_host = sh; @@ -157,44 +138,44 @@ static void pscsi_tape_read_blocksize(struct se_device *dev, buf = kzalloc(12, GFP_KERNEL); if (!buf) - return; + goto out_free; memset(cdb, 0, MAX_COMMAND_SIZE); cdb[0] = MODE_SENSE; cdb[4] = 0x0c; /* 12 bytes */ - ret = scsi_execute_req(sdev, cdb, DMA_FROM_DEVICE, buf, 12, NULL, - HZ, 1, NULL); + ret = scsi_execute_cmd(sdev, cdb, REQ_OP_DRV_IN, buf, 12, HZ, 1, NULL); if (ret) goto out_free; /* * If MODE_SENSE still returns zero, set the default value to 1024. */ - sdev->sector_size = (buf[9] << 16) | (buf[10] << 8) | (buf[11]); + sdev->sector_size = get_unaligned_be24(&buf[9]); +out_free: if (!sdev->sector_size) sdev->sector_size = 1024; -out_free: + kfree(buf); } static void pscsi_set_inquiry_info(struct scsi_device *sdev, struct t10_wwn *wwn) { - unsigned char *buf; - if (sdev->inquiry_len < INQUIRY_LEN) return; - - buf = sdev->inquiry; - if (!buf) - return; /* - * Use sdev->inquiry from drivers/scsi/scsi_scan.c:scsi_alloc_sdev() + * Use sdev->inquiry data from drivers/scsi/scsi_scan.c:scsi_add_lun() */ - memcpy(&wwn->vendor[0], &buf[8], sizeof(wwn->vendor)); - memcpy(&wwn->model[0], &buf[16], sizeof(wwn->model)); - memcpy(&wwn->revision[0], &buf[32], sizeof(wwn->revision)); + BUILD_BUG_ON(sizeof(wwn->vendor) != INQUIRY_VENDOR_LEN + 1); + snprintf(wwn->vendor, sizeof(wwn->vendor), + "%." __stringify(INQUIRY_VENDOR_LEN) "s", sdev->vendor); + BUILD_BUG_ON(sizeof(wwn->model) != INQUIRY_MODEL_LEN + 1); + snprintf(wwn->model, sizeof(wwn->model), + "%." __stringify(INQUIRY_MODEL_LEN) "s", sdev->model); + BUILD_BUG_ON(sizeof(wwn->revision) != INQUIRY_REVISION_LEN + 1); + snprintf(wwn->revision, sizeof(wwn->revision), + "%." __stringify(INQUIRY_REVISION_LEN) "s", sdev->rev); } static int @@ -211,11 +192,10 @@ pscsi_get_inquiry_vpd_serial(struct scsi_device *sdev, struct t10_wwn *wwn) cdb[0] = INQUIRY; cdb[1] = 0x01; /* Query VPD */ cdb[2] = 0x80; /* Unit Serial Number */ - cdb[3] = (INQUIRY_VPD_SERIAL_LEN >> 8) & 0xff; - cdb[4] = (INQUIRY_VPD_SERIAL_LEN & 0xff); + put_unaligned_be16(INQUIRY_VPD_SERIAL_LEN, &cdb[3]); - ret = scsi_execute_req(sdev, cdb, DMA_FROM_DEVICE, buf, - INQUIRY_VPD_SERIAL_LEN, NULL, HZ, 1, NULL); + ret = scsi_execute_cmd(sdev, cdb, REQ_OP_DRV_IN, buf, + INQUIRY_VPD_SERIAL_LEN, HZ, 1, NULL); if (ret) goto out_free; @@ -247,16 +227,14 @@ pscsi_get_inquiry_vpd_device_ident(struct scsi_device *sdev, cdb[0] = INQUIRY; cdb[1] = 0x01; /* Query VPD */ cdb[2] = 0x83; /* Device Identifier */ - cdb[3] = (INQUIRY_VPD_DEVICE_IDENTIFIER_LEN >> 8) & 0xff; - cdb[4] = (INQUIRY_VPD_DEVICE_IDENTIFIER_LEN & 0xff); + put_unaligned_be16(INQUIRY_VPD_DEVICE_IDENTIFIER_LEN, &cdb[3]); - ret = scsi_execute_req(sdev, cdb, DMA_FROM_DEVICE, buf, - INQUIRY_VPD_DEVICE_IDENTIFIER_LEN, - NULL, HZ, 1, NULL); + ret = scsi_execute_cmd(sdev, cdb, REQ_OP_DRV_IN, buf, + INQUIRY_VPD_DEVICE_IDENTIFIER_LEN, HZ, 1, NULL); if (ret) goto out; - page_len = (buf[2] << 8) | buf[3]; + page_len = get_unaligned_be16(&buf[2]); while (page_len > 0) { /* Grab a pointer to the Identification descriptor */ page_83 = &buf[off]; @@ -312,14 +290,15 @@ static int pscsi_add_device_to_list(struct se_device *dev, if (!sd->queue_depth) { sd->queue_depth = PSCSI_DEFAULT_QUEUEDEPTH; - pr_err("Set broken SCSI Device %d:%d:%d" + pr_err("Set broken SCSI Device %d:%d:%llu" " queue_depth to %d\n", sd->channel, sd->id, sd->lun, sd->queue_depth); } - dev->dev_attrib.hw_block_size = sd->sector_size; + dev->dev_attrib.hw_block_size = + min_not_zero((int)sd->sector_size, 512); dev->dev_attrib.hw_max_sectors = - min_t(int, sd->host->max_sectors, queue_max_hw_sectors(q)); + min_not_zero(sd->host->max_sectors, queue_max_hw_sectors(q)); dev->dev_attrib.hw_queue_depth = sd->queue_depth; /* @@ -342,8 +321,10 @@ static int pscsi_add_device_to_list(struct se_device *dev, /* * For TYPE_TAPE, attempt to determine blocksize with MODE_SENSE. */ - if (sd->type == TYPE_TAPE) + if (sd->type == TYPE_TAPE) { pscsi_tape_read_blocksize(dev, sd); + dev->dev_attrib.hw_block_size = sd->sector_size; + } return 0; } @@ -371,11 +352,11 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd) struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; struct pscsi_dev_virt *pdv = PSCSI_DEV(dev); struct Scsi_Host *sh = sd->host; - struct block_device *bd; + struct file *bdev_file; int ret; if (scsi_device_get(sd)) { - pr_err("scsi_device_get() failed for %d:%d:%d:%d\n", + pr_err("scsi_device_get() failed for %d:%d:%d:%llu\n", sh->host_no, sd->channel, sd->id, sd->lun); spin_unlock_irq(sh->host_lock); return -EIO; @@ -383,33 +364,34 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd) spin_unlock_irq(sh->host_lock); /* * Claim exclusive struct block_device access to struct scsi_device - * for TYPE_DISK using supplied udev_path + * for TYPE_DISK and TYPE_ZBC using supplied udev_path */ - bd = blkdev_get_by_path(dev->udev_path, - FMODE_WRITE|FMODE_READ|FMODE_EXCL, pdv); - if (IS_ERR(bd)) { - pr_err("pSCSI: blkdev_get_by_path() failed\n"); + bdev_file = bdev_file_open_by_path(dev->udev_path, + BLK_OPEN_WRITE | BLK_OPEN_READ, pdv, NULL); + if (IS_ERR(bdev_file)) { + pr_err("pSCSI: bdev_file_open_by_path() failed\n"); scsi_device_put(sd); - return PTR_ERR(bd); + return PTR_ERR(bdev_file); } - pdv->pdv_bd = bd; + pdv->pdv_bdev_file = bdev_file; ret = pscsi_add_device_to_list(dev, sd); if (ret) { - blkdev_put(pdv->pdv_bd, FMODE_WRITE|FMODE_READ|FMODE_EXCL); + fput(bdev_file); scsi_device_put(sd); return ret; } - pr_debug("CORE_PSCSI[%d] - Added TYPE_DISK for %d:%d:%d:%d\n", - phv->phv_host_id, sh->host_no, sd->channel, sd->id, sd->lun); + pr_debug("CORE_PSCSI[%d] - Added TYPE_%s for %d:%d:%d:%llu\n", + phv->phv_host_id, sd->type == TYPE_DISK ? "DISK" : "ZBC", + sh->host_no, sd->channel, sd->id, sd->lun); return 0; } /* * Called with struct Scsi_Host->host_lock called. */ -static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) +static int pscsi_create_type_nondisk(struct se_device *dev, struct scsi_device *sd) __releases(sh->host_lock) { struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; @@ -417,7 +399,7 @@ static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) int ret; if (scsi_device_get(sd)) { - pr_err("scsi_device_get() failed for %d:%d:%d:%d\n", + pr_err("scsi_device_get() failed for %d:%d:%d:%llu\n", sh->host_no, sd->channel, sd->id, sd->lun); spin_unlock_irq(sh->host_lock); return -EIO; @@ -429,35 +411,13 @@ static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) scsi_device_put(sd); return ret; } - pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n", + pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%llu\n", phv->phv_host_id, scsi_device_type(sd->type), sh->host_no, sd->channel, sd->id, sd->lun); return 0; } -/* - * Called with struct Scsi_Host->host_lock called. - */ -static int pscsi_create_type_other(struct se_device *dev, - struct scsi_device *sd) - __releases(sh->host_lock) -{ - struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; - struct Scsi_Host *sh = sd->host; - int ret; - - spin_unlock_irq(sh->host_lock); - ret = pscsi_add_device_to_list(dev, sd); - if (ret) - return ret; - - pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n", - phv->phv_host_id, scsi_device_type(sd->type), sh->host_no, - sd->channel, sd->id, sd->lun); - return 0; -} - static int pscsi_configure_device(struct se_device *dev) { struct se_hba *hba = dev->se_hba; @@ -515,11 +475,12 @@ static int pscsi_configure_device(struct se_device *dev) sh = phv->phv_lld_host; } else { sh = scsi_host_lookup(pdv->pdv_host_id); - if (IS_ERR(sh)) { + if (!sh) { pr_err("pSCSI: Unable to locate" " pdv_host_id: %d\n", pdv->pdv_host_id); - return PTR_ERR(sh); + return -EINVAL; } + pdv->pdv_lld_host = sh; } } else { if (phv->phv_mode == PHV_VIRTUAL_HOST_ID) { @@ -537,18 +498,16 @@ static int pscsi_configure_device(struct se_device *dev) continue; /* * Functions will release the held struct scsi_host->host_lock - * before calling calling pscsi_add_device_to_list() to register + * before calling pscsi_add_device_to_list() to register * struct scsi_device with target_core_mod. */ switch (sd->type) { case TYPE_DISK: + case TYPE_ZBC: ret = pscsi_create_type_disk(dev, sd); break; - case TYPE_ROM: - ret = pscsi_create_type_rom(dev, sd); - break; default: - ret = pscsi_create_type_other(dev, sd); + ret = pscsi_create_type_nondisk(dev, sd); break; } @@ -579,8 +538,21 @@ static int pscsi_configure_device(struct se_device *dev) return -ENODEV; } +static void pscsi_dev_call_rcu(struct rcu_head *p) +{ + struct se_device *dev = container_of(p, struct se_device, rcu_head); + struct pscsi_dev_virt *pdv = PSCSI_DEV(dev); + + kfree(pdv); +} + static void pscsi_free_device(struct se_device *dev) { + call_rcu(&dev->rcu_head, pscsi_dev_call_rcu); +} + +static void pscsi_destroy_device(struct se_device *dev) +{ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev); struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; struct scsi_device *sd = pdv->pdv_sd; @@ -588,12 +560,13 @@ static void pscsi_free_device(struct se_device *dev) if (sd) { /* * Release exclusive pSCSI internal struct block_device claim for - * struct scsi_device with TYPE_DISK from pscsi_create_type_disk() + * struct scsi_device with TYPE_DISK or TYPE_ZBC + * from pscsi_create_type_disk() */ - if ((sd->type == TYPE_DISK) && pdv->pdv_bd) { - blkdev_put(pdv->pdv_bd, - FMODE_WRITE|FMODE_READ|FMODE_EXCL); - pdv->pdv_bd = NULL; + if ((sd->type == TYPE_DISK || sd->type == TYPE_ZBC) && + pdv->pdv_bdev_file) { + fput(pdv->pdv_bdev_file); + pdv->pdv_bdev_file = NULL; } /* * For HBA mode PHV_LLD_SCSI_HOST_NO, release the reference @@ -602,58 +575,56 @@ static void pscsi_free_device(struct se_device *dev) if ((phv->phv_mode == PHV_LLD_SCSI_HOST_NO) && (phv->phv_lld_host != NULL)) scsi_host_put(phv->phv_lld_host); + else if (pdv->pdv_lld_host) + scsi_host_put(pdv->pdv_lld_host); - if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM)) - scsi_device_put(sd); + scsi_device_put(sd); pdv->pdv_sd = NULL; } - - kfree(pdv); } -static void pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg, - unsigned char *sense_buffer) +static void pscsi_complete_cmd(struct se_cmd *cmd, u8 scsi_status, + unsigned char *req_sense, int valid_data) { struct pscsi_dev_virt *pdv = PSCSI_DEV(cmd->se_dev); struct scsi_device *sd = pdv->pdv_sd; - int result; - struct pscsi_plugin_task *pt = cmd->priv; - unsigned char *cdb; + unsigned char *cdb = cmd->priv; + /* - * Special case for REPORT_LUNs handling where pscsi_plugin_task has - * not been allocated because TCM is handling the emulation directly. + * Special case for REPORT_LUNs which is emulated and not passed on. */ - if (!pt) + if (!cdb) return; - cdb = &pt->pscsi_cdb[0]; - result = pt->pscsi_result; /* * Hack to make sure that Write-Protect modepage is set if R/O mode is * forced. */ - if (!cmd->se_deve || !cmd->data_length) + if (!cmd->data_length) goto after_mode_sense; if (((cdb[0] == MODE_SENSE) || (cdb[0] == MODE_SENSE_10)) && - (status_byte(result) << 1) == SAM_STAT_GOOD) { - if (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) { + scsi_status == SAM_STAT_GOOD) { + bool read_only = target_lun_is_rdonly(cmd); + + if (read_only) { unsigned char *buf; buf = transport_kmap_data_sg(cmd); - if (!buf) + if (!buf) { ; /* XXX: TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE */ - - if (cdb[0] == MODE_SENSE_10) { - if (!(buf[3] & 0x80)) - buf[3] |= 0x80; } else { - if (!(buf[2] & 0x80)) - buf[2] |= 0x80; + if (cdb[0] == MODE_SENSE_10) { + if (!(buf[3] & 0x80)) + buf[3] |= 0x80; + } else { + if (!(buf[2] & 0x80)) + buf[2] |= 0x80; + } + + transport_kunmap_data_sg(cmd); } - - transport_kunmap_data_sg(cmd); } } after_mode_sense: @@ -670,39 +641,56 @@ after_mode_sense: * storage engine. */ if (((cdb[0] == MODE_SELECT) || (cdb[0] == MODE_SELECT_10)) && - (status_byte(result) << 1) == SAM_STAT_GOOD) { + scsi_status == SAM_STAT_GOOD) { unsigned char *buf; u16 bdl; u32 blocksize; - buf = sg_virt(&sg[0]); + buf = sg_virt(&cmd->t_data_sg[0]); if (!buf) { pr_err("Unable to get buf for scatterlist\n"); goto after_mode_select; } if (cdb[0] == MODE_SELECT) - bdl = (buf[3]); + bdl = buf[3]; else - bdl = (buf[6] << 8) | (buf[7]); + bdl = get_unaligned_be16(&buf[6]); if (!bdl) goto after_mode_select; if (cdb[0] == MODE_SELECT) - blocksize = (buf[9] << 16) | (buf[10] << 8) | - (buf[11]); + blocksize = get_unaligned_be24(&buf[9]); else - blocksize = (buf[13] << 16) | (buf[14] << 8) | - (buf[15]); + blocksize = get_unaligned_be24(&buf[13]); sd->sector_size = blocksize; } after_mode_select: - if (sense_buffer && (status_byte(result) & CHECK_CONDITION)) { - memcpy(sense_buffer, pt->pscsi_sense, TRANSPORT_SENSE_BUFFER); - cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; + if (scsi_status == SAM_STAT_CHECK_CONDITION) { + transport_copy_sense_to_cmd(cmd, req_sense); + + /* + * check for TAPE device reads with + * FM/EOM/ILI set, so that we can get data + * back despite framework assumption that a + * check condition means there is no data + */ + if (sd->type == TYPE_TAPE && valid_data && + cmd->data_direction == DMA_FROM_DEVICE) { + /* + * is sense data valid, fixed format, + * and have FM, EOM, or ILI set? + */ + if (req_sense[0] == 0xf0 && /* valid, fixed format */ + req_sense[2] & 0xe0 && /* FM, EOM, or ILI */ + (req_sense[2] & 0xf) == 0) { /* key==NO_SENSE */ + pr_debug("Tape FM/EOM/ILI status detected. Treat as normal read.\n"); + cmd->se_cmd_flags |= SCF_TREAT_READ_AS_NORMAL; + } + } } } @@ -749,14 +737,18 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, ret = -EINVAL; goto out; } - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_host_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI Host ID:" " %d\n", phv->phv_host_id, pdv->pdv_host_id); pdv->pdv_flags |= PDF_HAS_VIRT_HOST_ID; break; case Opt_scsi_channel_id: - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_channel_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI Channel" " ID: %d\n", phv->phv_host_id, @@ -764,7 +756,9 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, pdv->pdv_flags |= PDF_HAS_CHANNEL_ID; break; case Opt_scsi_target_id: - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_target_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI Target" " ID: %d\n", phv->phv_host_id, @@ -772,7 +766,9 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, pdv->pdv_flags |= PDF_HAS_TARGET_ID; break; case Opt_scsi_lun_id: - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_lun_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI LUN ID:" " %d\n", phv->phv_host_id, pdv->pdv_lun_id); @@ -795,7 +791,6 @@ static ssize_t pscsi_show_configfs_dev_params(struct se_device *dev, char *b) struct scsi_device *sd = pdv->pdv_sd; unsigned char host_id[16]; ssize_t bl; - int i; if (phv->phv_mode == PHV_VIRTUAL_HOST_ID) snprintf(host_id, 16, "%d", pdv->pdv_host_id); @@ -808,70 +803,36 @@ static ssize_t pscsi_show_configfs_dev_params(struct se_device *dev, char *b) host_id); if (sd) { - bl += sprintf(b + bl, " "); - bl += sprintf(b + bl, "Vendor: "); - for (i = 0; i < 8; i++) { - if (ISPRINT(sd->vendor[i])) /* printable character? */ - bl += sprintf(b + bl, "%c", sd->vendor[i]); - else - bl += sprintf(b + bl, " "); - } - bl += sprintf(b + bl, " Model: "); - for (i = 0; i < 16; i++) { - if (ISPRINT(sd->model[i])) /* printable character ? */ - bl += sprintf(b + bl, "%c", sd->model[i]); - else - bl += sprintf(b + bl, " "); - } - bl += sprintf(b + bl, " Rev: "); - for (i = 0; i < 4; i++) { - if (ISPRINT(sd->rev[i])) /* printable character ? */ - bl += sprintf(b + bl, "%c", sd->rev[i]); - else - bl += sprintf(b + bl, " "); - } - bl += sprintf(b + bl, "\n"); + bl += sprintf(b + bl, " Vendor: %." + __stringify(INQUIRY_VENDOR_LEN) "s", sd->vendor); + bl += sprintf(b + bl, " Model: %." + __stringify(INQUIRY_MODEL_LEN) "s", sd->model); + bl += sprintf(b + bl, " Rev: %." + __stringify(INQUIRY_REVISION_LEN) "s\n", sd->rev); } return bl; } -static void pscsi_bi_endio(struct bio *bio, int error) +static void pscsi_bi_endio(struct bio *bio) { - bio_put(bio); -} - -static inline struct bio *pscsi_get_bio(int nr_vecs) -{ - struct bio *bio; - /* - * Use bio_malloc() following the comment in for bio -> struct request - * in block/blk-core.c:blk_make_request() - */ - bio = bio_kmalloc(GFP_KERNEL, nr_vecs); - if (!bio) { - pr_err("PSCSI: bio_kmalloc() failed\n"); - return NULL; - } - bio->bi_end_io = pscsi_bi_endio; - - return bio; + bio_uninit(bio); + kfree(bio); } static sense_reason_t pscsi_map_sg(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, - enum dma_data_direction data_direction, struct bio **hbio) + struct request *req) { - struct pscsi_dev_virt *pdv = PSCSI_DEV(cmd->se_dev); - struct bio *bio = NULL, *tbio = NULL; + struct bio *bio = NULL; struct page *page; struct scatterlist *sg; u32 data_len = cmd->data_length, i, len, bytes, off; int nr_pages = (cmd->data_length + sgl[0].offset + PAGE_SIZE - 1) >> PAGE_SHIFT; int nr_vecs = 0, rc; - int rw = (data_direction == DMA_TO_DEVICE); + int rw = (cmd->data_direction == DMA_TO_DEVICE); - *hbio = NULL; + BUG_ON(!cmd->data_length); pr_debug("PSCSI: nr_pages: %d\n", nr_pages); @@ -895,130 +856,76 @@ pscsi_map_sg(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, bytes = min(bytes, data_len); if (!bio) { - nr_vecs = min_t(int, BIO_MAX_PAGES, nr_pages); - nr_pages -= nr_vecs; - /* - * Calls bio_kmalloc() and sets bio->bi_end_io() - */ - bio = pscsi_get_bio(nr_vecs); +new_bio: + nr_vecs = bio_max_segs(nr_pages); + bio = bio_kmalloc(nr_vecs, GFP_KERNEL); if (!bio) goto fail; - - if (rw) - bio->bi_rw |= REQ_WRITE; + bio_init_inline(bio, NULL, nr_vecs, + rw ? REQ_OP_WRITE : REQ_OP_READ); + bio->bi_end_io = pscsi_bi_endio; pr_debug("PSCSI: Allocated bio: %p," " dir: %s nr_vecs: %d\n", bio, (rw) ? "rw" : "r", nr_vecs); - /* - * Set *hbio pointer to handle the case: - * nr_pages > BIO_MAX_PAGES, where additional - * bios need to be added to complete a given - * command. - */ - if (!*hbio) - *hbio = tbio = bio; - else - tbio = tbio->bi_next = bio; } - pr_debug("PSCSI: Calling bio_add_pc_page() i: %d" + pr_debug("PSCSI: Calling bio_add_page() i: %d" " bio: %p page: %p len: %d off: %d\n", i, bio, page, len, off); - rc = bio_add_pc_page(pdv->pdv_sd->request_queue, - bio, page, bytes, off); - if (rc != bytes) - goto fail; - + rc = bio_add_page(bio, page, bytes, off); pr_debug("PSCSI: bio->bi_vcnt: %d nr_vecs: %d\n", - bio->bi_vcnt, nr_vecs); - - if (bio->bi_vcnt > nr_vecs) { + bio_segments(bio), nr_vecs); + if (rc != bytes) { pr_debug("PSCSI: Reached bio->bi_vcnt max:" " %d i: %d bio: %p, allocating another" " bio\n", bio->bi_vcnt, i, bio); - /* - * Clear the pointer so that another bio will - * be allocated with pscsi_get_bio() above, the - * current bio has already been set *tbio and - * bio->bi_next. - */ - bio = NULL; + + rc = blk_rq_append_bio(req, bio); + if (rc) { + pr_err("pSCSI: failed to append bio\n"); + goto fail; + } + + goto new_bio; } data_len -= bytes; } } + if (bio) { + rc = blk_rq_append_bio(req, bio); + if (rc) { + pr_err("pSCSI: failed to append bio\n"); + goto fail; + } + } + return 0; fail: - while (*hbio) { - bio = *hbio; - *hbio = (*hbio)->bi_next; - bio_endio(bio, 0); /* XXX: should be error */ + if (bio) { + bio_uninit(bio); + kfree(bio); } - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; -} - -/* - * Clear a lun set in the cdb if the initiator talking to use spoke - * and old standards version, as we can't assume the underlying device - * won't choke up on it. - */ -static inline void pscsi_clear_cdb_lun(unsigned char *cdb) -{ - switch (cdb[0]) { - case READ_10: /* SBC - RDProtect */ - case READ_12: /* SBC - RDProtect */ - case READ_16: /* SBC - RDProtect */ - case SEND_DIAGNOSTIC: /* SPC - SELF-TEST Code */ - case VERIFY: /* SBC - VRProtect */ - case VERIFY_16: /* SBC - VRProtect */ - case WRITE_VERIFY: /* SBC - VRProtect */ - case WRITE_VERIFY_12: /* SBC - VRProtect */ - case MAINTENANCE_IN: /* SPC - Parameter Data Format for SA RTPG */ - break; - default: - cdb[1] &= 0x1f; /* clear logical unit number */ - break; + while (req->bio) { + bio = req->bio; + req->bio = bio->bi_next; + bio_uninit(bio); + kfree(bio); } + req->biotail = NULL; + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } static sense_reason_t pscsi_parse_cdb(struct se_cmd *cmd) { - unsigned char *cdb = cmd->t_task_cdb; - if (cmd->se_cmd_flags & SCF_BIDI) return TCM_UNSUPPORTED_SCSI_OPCODE; - pscsi_clear_cdb_lun(cdb); - - /* - * For REPORT LUNS we always need to emulate the response, for everything - * else the default for pSCSI is to pass the command to the underlying - * LLD / physical hardware. - */ - switch (cdb[0]) { - case REPORT_LUNS: - cmd->execute_cmd = spc_emulate_report_luns; - return 0; - case READ_6: - case READ_10: - case READ_12: - case READ_16: - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_16: - case WRITE_VERIFY: - cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; - /* FALLTHROUGH*/ - default: - cmd->execute_cmd = pscsi_execute_cmd; - return 0; - } + return passthrough_parse_cdb(cmd, pscsi_execute_cmd); } static sense_reason_t @@ -1026,80 +933,49 @@ pscsi_execute_cmd(struct se_cmd *cmd) { struct scatterlist *sgl = cmd->t_data_sg; u32 sgl_nents = cmd->t_data_nents; - enum dma_data_direction data_direction = cmd->data_direction; struct pscsi_dev_virt *pdv = PSCSI_DEV(cmd->se_dev); - struct pscsi_plugin_task *pt; + struct scsi_cmnd *scmd; struct request *req; - struct bio *hbio; sense_reason_t ret; - /* - * Dynamically alloc cdb space, since it may be larger than - * TCM_MAX_COMMAND_SIZE - */ - pt = kzalloc(sizeof(*pt) + scsi_command_size(cmd->t_task_cdb), GFP_KERNEL); - if (!pt) { + req = scsi_alloc_request(pdv->pdv_sd->request_queue, + cmd->data_direction == DMA_TO_DEVICE ? + REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + if (IS_ERR(req)) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } - cmd->priv = pt; - - memcpy(pt->pscsi_cdb, cmd->t_task_cdb, - scsi_command_size(cmd->t_task_cdb)); - - if (!sgl) { - req = blk_get_request(pdv->pdv_sd->request_queue, - (data_direction == DMA_TO_DEVICE), - GFP_KERNEL); - if (!req || IS_ERR(req)) { - pr_err("PSCSI: blk_get_request() failed: %ld\n", - req ? IS_ERR(req) : -ENOMEM); - ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - goto fail; - } - } else { - BUG_ON(!cmd->data_length); - ret = pscsi_map_sg(cmd, sgl, sgl_nents, data_direction, &hbio); + if (sgl) { + ret = pscsi_map_sg(cmd, sgl, sgl_nents, req); if (ret) - goto fail; - - req = blk_make_request(pdv->pdv_sd->request_queue, hbio, - GFP_KERNEL); - if (IS_ERR(req)) { - pr_err("pSCSI: blk_make_request() failed\n"); - ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - goto fail_free_bio; - } + goto fail_put_request; } - req->cmd_type = REQ_TYPE_BLOCK_PC; req->end_io = pscsi_req_done; req->end_io_data = cmd; - req->cmd_len = scsi_command_size(pt->pscsi_cdb); - req->cmd = &pt->pscsi_cdb[0]; - req->sense = &pt->pscsi_sense[0]; - req->sense_len = 0; - if (pdv->pdv_sd->type == TYPE_DISK) + + scmd = blk_mq_rq_to_pdu(req); + scmd->cmd_len = scsi_command_size(cmd->t_task_cdb); + if (scmd->cmd_len > sizeof(scmd->cmnd)) { + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + goto fail_put_request; + } + memcpy(scmd->cmnd, cmd->t_task_cdb, scmd->cmd_len); + + if (pdv->pdv_sd->type == TYPE_DISK || + pdv->pdv_sd->type == TYPE_ZBC) req->timeout = PS_TIMEOUT_DISK; else req->timeout = PS_TIMEOUT_OTHER; - req->retries = PS_RETRY; + scmd->allowed = PS_RETRY; + + cmd->priv = scmd->cmnd; - blk_execute_rq_nowait(pdv->pdv_sd->request_queue, NULL, req, - (cmd->sam_task_attr == MSG_HEAD_TAG), - pscsi_req_done); + blk_execute_rq_nowait(req, cmd->sam_task_attr == TCM_HEAD_TAG); return 0; -fail_free_bio: - while (hbio) { - struct bio *bio = hbio; - hbio = hbio->bi_next; - bio_endio(bio, 0); /* XXX: should be error */ - } - ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; -fail: - kfree(pt); +fail_put_request: + blk_mq_free_request(req); return ret; } @@ -1112,77 +988,78 @@ static u32 pscsi_get_device_type(struct se_device *dev) struct pscsi_dev_virt *pdv = PSCSI_DEV(dev); struct scsi_device *sd = pdv->pdv_sd; - return sd->type; + return (sd) ? sd->type : TYPE_NO_LUN; } static sector_t pscsi_get_blocks(struct se_device *dev) { struct pscsi_dev_virt *pdv = PSCSI_DEV(dev); - if (pdv->pdv_bd && pdv->pdv_bd->bd_part) - return pdv->pdv_bd->bd_part->nr_sects; - - dump_stack(); + if (pdv->pdv_bdev_file) + return bdev_nr_sectors(file_bdev(pdv->pdv_bdev_file)); return 0; } -static void pscsi_req_done(struct request *req, int uptodate) +static enum rq_end_io_ret pscsi_req_done(struct request *req, + blk_status_t status) { struct se_cmd *cmd = req->end_io_data; - struct pscsi_plugin_task *pt = cmd->priv; - - pt->pscsi_result = req->errors; - pt->pscsi_resid = req->resid_len; + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req); + enum sam_status scsi_status = scmd->result & 0xff; + int valid_data = cmd->data_length - scmd->resid_len; + u8 *cdb = cmd->priv; - cmd->scsi_status = status_byte(pt->pscsi_result) << 1; - if (cmd->scsi_status) { + if (scsi_status != SAM_STAT_GOOD) { pr_debug("PSCSI Status Byte exception at cmd: %p CDB:" - " 0x%02x Result: 0x%08x\n", cmd, pt->pscsi_cdb[0], - pt->pscsi_result); + " 0x%02x Result: 0x%08x\n", cmd, cdb[0], scmd->result); } - switch (host_byte(pt->pscsi_result)) { + pscsi_complete_cmd(cmd, scsi_status, scmd->sense_buffer, valid_data); + + switch (host_byte(scmd->result)) { case DID_OK: - target_complete_cmd(cmd, cmd->scsi_status); + target_complete_cmd_with_length(cmd, scsi_status, valid_data); break; default: pr_debug("PSCSI Host Byte exception at cmd: %p CDB:" - " 0x%02x Result: 0x%08x\n", cmd, pt->pscsi_cdb[0], - pt->pscsi_result); + " 0x%02x Result: 0x%08x\n", cmd, cdb[0], scmd->result); target_complete_cmd(cmd, SAM_STAT_CHECK_CONDITION); break; } - __blk_put_request(req->q, req); - kfree(pt); + blk_mq_free_request(req); + return RQ_END_IO_NONE; } -static struct se_subsystem_api pscsi_template = { +static const struct target_backend_ops pscsi_ops = { .name = "pscsi", .owner = THIS_MODULE, - .transport_type = TRANSPORT_PLUGIN_PHBA_PDEV, + .transport_flags_default = TRANSPORT_FLAG_PASSTHROUGH | + TRANSPORT_FLAG_PASSTHROUGH_ALUA | + TRANSPORT_FLAG_PASSTHROUGH_PGR, .attach_hba = pscsi_attach_hba, .detach_hba = pscsi_detach_hba, .pmode_enable_hba = pscsi_pmode_enable_hba, .alloc_device = pscsi_alloc_device, .configure_device = pscsi_configure_device, + .destroy_device = pscsi_destroy_device, .free_device = pscsi_free_device, - .transport_complete = pscsi_transport_complete, .parse_cdb = pscsi_parse_cdb, .set_configfs_dev_params = pscsi_set_configfs_dev_params, .show_configfs_dev_params = pscsi_show_configfs_dev_params, .get_device_type = pscsi_get_device_type, .get_blocks = pscsi_get_blocks, + .tb_dev_attrib_attrs = passthrough_attrib_attrs, }; static int __init pscsi_module_init(void) { - return transport_subsystem_register(&pscsi_template); + return transport_backend_register(&pscsi_ops); } static void __exit pscsi_module_exit(void) { - transport_subsystem_release(&pscsi_template); + target_backend_unregister(&pscsi_ops); } MODULE_DESCRIPTION("TCM PSCSI subsystem plugin"); |
