diff --git a/MAINTAINERS b/MAINTAINERS index 4978dc19a4d277..7bddfee55154ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5360,6 +5360,14 @@ S: Supported F: drivers/scsi/ibmvscsi/ibmvscsi* F: drivers/scsi/ibmvscsi/viosrp.h +IBM Power Virtual SCSI Device Target Driver +M: Bryant G. Ly +L: linux-scsi@vger.kernel.org +L: target-devel@vger.kernel.org +S: Supported +F: drivers/scsi/ibmvscsi/ibmvscsis.* +F: drivers/scsi/libsrp.* + IBM Power Virtual FC Device Drivers M: Tyrel Datwyler L: linux-scsi@vger.kernel.org diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 0f0fb2ec7d93b6..0a1366cba81b41 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -40,13 +40,6 @@ config SCSI_DMA bool default n -config SCSI_TGT - tristate "SCSI target support" - depends on SCSI - ---help--- - If you want to use SCSI target mode drivers enable this option. - If you choose M, the module will be called scsi_tgt. - config SCSI_NETLINK bool default n @@ -275,13 +268,6 @@ config SCSI_FC_ATTRS each attached FiberChannel device to sysfs, say Y. Otherwise, say N. -config SCSI_FC_TGT_ATTRS - bool "SCSI target support for FiberChannel Transport Attributes" - depends on SCSI_FC_ATTRS - depends on SCSI_TGT = y || SCSI_TGT = SCSI_FC_ATTRS - help - If you want to use SCSI target mode drivers enable this option. - config SCSI_ISCSI_ATTRS tristate "iSCSI Transport Attributes" depends on SCSI && NET @@ -308,12 +294,6 @@ config SCSI_SRP_ATTRS If you wish to export transport-specific information about each attached SRP device to sysfs, say Y. -config SCSI_SRP_TGT_ATTRS - bool "SCSI target support for SRP Transport Attributes" - depends on SCSI_SRP_ATTRS - depends on SCSI_TGT = y || SCSI_TGT = SCSI_SRP_ATTRS - help - If you want to use SCSI target mode drivers enable this option. endmenu menuconfig SCSI_LOWLEVEL @@ -868,18 +848,18 @@ config SCSI_IBMVSCSI module will be called ibmvscsi. config SCSI_IBMVSCSIS - tristate "IBM Virtual SCSI Server support" - depends on PPC_PSERIES && SCSI_SRP && SCSI_SRP_TGT_ATTRS && TARGET_CORE - help - This is the SRP target driver for IBM pSeries virtual environments. + tristate "IBM Virtual SCSI Server support" + depends on PPC_PSERIES && SCSI_SRP && TARGET_CORE + help + This is the SRP target driver for IBM pSeries virtual environments. - The userspace component needed to initialize the driver and - documentation can be found: + The userspace component needed to initialize the driver and + documentation can be found: - http://stgt.berlios.de/ + http://stgt.berlios.de/ - To compile this driver as a module, choose M here: the - module will be called ibmvstgt. + To compile this driver as a module, choose M here: the + module will be called ibmvstgt. config SCSI_IBMVFC tristate "IBM Virtual FC support" @@ -1763,14 +1743,13 @@ config SCSI_PM8001 based host adapters. config SCSI_SRP - tristate "SCSI RDMA Protocol helper library" - depends on SCSI && PCI - select SCSI_TGT - help - If you wish to use SRP target drivers, say Y. + tristate "SCSI RDMA Protocol helper library" + depends on SCSI && PCI + help + If you wish to use SRP target drivers, say Y. - To compile this driver as a module, choose M here: the - module will be called libsrp. + To compile this driver as a module, choose M here: the + module will be called libsrp. config SCSI_BFA_FC tristate "Brocade BFA Fibre Channel Support" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index ed4a1434e5d2bf..4b5ec85ed9f740 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -20,7 +20,6 @@ CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_SCSI) += scsi_mod.o -obj-$(CONFIG_SCSI_TGT) += scsi_tgt.o obj-$(CONFIG_RAID_ATTRS) += raid_class.o @@ -149,6 +148,8 @@ obj-$(CONFIG_XEN_SCSI_FRONTEND) += xen-scsifront.o obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o obj-$(CONFIG_SCSI_WD719X) += wd719x.o +CFLAGS_libsrp.o := -DDEBUG + obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_CHR_DEV_ST) += st.o @@ -179,8 +180,6 @@ scsi_mod-$(CONFIG_SCSI_DH) += scsi_dh.o hv_storvsc-y := storvsc_drv.o -scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o - sd_mod-objs := sd.o sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o diff --git a/drivers/scsi/ibmvscsi/ibmvscsis.c b/drivers/scsi/ibmvscsi/ibmvscsis.c index 341fad6817492b..5dc84429344c89 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsis.c +++ b/drivers/scsi/ibmvscsi/ibmvscsis.c @@ -54,6 +54,10 @@ #include "ibmvscsi.h" #include "viosrp.h" +#define GETTARGET(x) ((int)((((u64)(x)) >> 56) & 0x003f)) +#define GETBUS(x) ((int)((((u64)(x)) >> 53) & 0x0007)) +#define GETLUN(x) ((int)((((u64)(x)) >> 48) & 0x001f)) + #define IBMVSCSIS_VERSION "v0.1" #define IBMVSCSIS_NAMELEN 32 @@ -62,23 +66,12 @@ #define MAX_H_COPY_RDMA (128*1024) -/* - * Hypervisor calls. - */ -#define h_reg_crq(ua, tok, sz)\ - plpar_hcall_norets(H_REG_CRQ, ua, tok, sz); - -#define GETTARGET(x) ((int)((((u64)(x)) >> 56) & 0x003f)) -#define GETBUS(x) ((int)((((u64)(x)) >> 53) & 0x0007)) -#define GETLUN(x) ((int)((((u64)(x)) >> 48) & 0x001f)) +#define SRP_RSP_SENSE_DATA_LEN 18 +#define NO_SUCH_LUN ((uint64_t)-1LL) +static struct workqueue_struct *vtgtd; static unsigned max_vdma_size = MAX_H_COPY_RDMA; -static const char ibmvscsis_driver_name[] = "ibmvscsis"; -static char system_id[64] = ""; -static char partition_name[97] = "UNKNOWN"; -static unsigned int partition_number = -1; - /* Adapter list and lock to control it */ static DEFINE_SPINLOCK(ibmvscsis_dev_lock); static LIST_HEAD(ibmvscsis_dev_list); @@ -130,10 +123,11 @@ struct ibmvscsis_adapter { struct crq_queue crq_queue; struct work_struct crq_work; + atomic_t req_lim_delta; u32 liobn; u32 riobn; - struct srp_target target; + struct srp_target *target; struct list_head list; struct ibmvscsis_tport tport; @@ -144,6 +138,110 @@ struct ibmvscsis_nacl { struct se_node_acl se_node_acl; }; +struct inquiry_data { + u8 qual_type; + u8 rmb_reserve; + u8 version; + u8 aerc_naca_hisup_format; + u8 addl_len; + u8 sccs_reserved; + u8 bque_encserv_vs_multip_mchngr_reserved; + u8 reladr_reserved_linked_cmdqueue_vs; + char vendor[8]; + char product[16]; + char revision[4]; + char vendor_specific[20]; + char reserved1[2]; + char version_descriptor[16]; + char reserved2[22]; + char unique[158]; +}; + +enum scsi_lun_addr_method { + SCSI_LUN_ADDR_METHOD_PERIPHERAL = 0, + SCSI_LUN_ADDR_METHOD_FLAT = 1, + SCSI_LUN_ADDR_METHOD_LUN = 2, + SCSI_LUN_ADDR_METHOD_EXTENDED_LUN = 3, +}; + +static int ibmvscsis_probe(struct vio_dev *vdev, + const struct vio_device_id *id); +static void ibmvscsis_dev_release(struct device *dev); +static int read_dma_window(struct vio_dev *vdev, + struct ibmvscsis_adapter *adapter); +static char *ibmvscsis_get_fabric_name(void); +static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg); +static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg); +static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg); +static int ibmvscsis_check_true(struct se_portal_group *se_tpg); +static int ibmvscsis_check_false(struct se_portal_group *se_tpg); +static u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg); +static int ibmvscsis_check_stop_free(struct se_cmd *se_cmd); +static void ibmvscsis_release_cmd(struct se_cmd *se_cmd); +static int ibmvscsis_shutdown_session(struct se_session *se_sess); +static void ibmvscsis_close_session(struct se_session *se_sess); +static u32 ibmvscsis_sess_get_index(struct se_session *se_sess); +static int ibmvscsis_write_pending(struct se_cmd *se_cmd); +static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd); +static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl); +static int ibmvscsis_get_cmd_state(struct se_cmd *se_cmd); +static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd); +static int ibmvscsis_queue_status(struct se_cmd *se_cmd); +static void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd); +static void ibmvscsis_aborted_task(struct se_cmd *se_cmd); +static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf, + struct config_group *group, + const char *name); +static void ibmvscsis_drop_tport(struct se_wwn *wwn); +static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn, + struct config_group *group, + const char *name); +static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg); +static int ibmvscsis_remove(struct vio_dev *vdev); +static ssize_t system_id_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t partition_number_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t unit_address_show(struct device *dev, + struct device_attribute *attr, char *buf); +static int get_system_info(void); +static irqreturn_t ibmvscsis_interrupt(int dummy, void *data); +static int process_srp_iu(struct iu_entry *iue); +static void process_iu(struct viosrp_crq *crq, + struct ibmvscsis_adapter *adapter); +static void process_crq(struct viosrp_crq *crq, + struct ibmvscsis_adapter *adapter); +static void handle_crq(struct work_struct *work); +static int ibmvscsis_reset_crq_queue(struct ibmvscsis_adapter *adapter); +static void crq_queue_destroy(struct ibmvscsis_adapter *adapter); +static inline struct viosrp_crq *next_crq(struct crq_queue *queue); +static int send_iu(struct iu_entry *iue, u64 length, u8 format); +static int send_rsp(struct iu_entry *iue, struct scsi_cmnd *sc, + unsigned char status, unsigned char asc); +static int send_adapter_info(struct iu_entry *iue, + dma_addr_t remote_buffer, u16 length); +static int process_mad_iu(struct iu_entry *iue); +static void process_login(struct iu_entry *iue); +static void process_tsk_mgmt(struct iu_entry *iue); +static int ibmvscsis_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, + int nsg, struct srp_direct_buf *md, int nmd, + enum dma_data_direction dir, unsigned int rest); +static int ibmvscsis_cmnd_done(struct scsi_cmnd *sc); +static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter, + struct iu_entry *iue); +static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len); +static int tcm_queuecommand(struct ibmvscsis_adapter *adapter, + struct ibmvscsis_cmnd *vsc, + struct srp_cmd *scmd); + +/* + * Hypervisor calls. + */ +#define h_reg_crq(ua, tok, sz)\ + plpar_hcall_norets(H_REG_CRQ, ua, tok, sz); + static inline long h_copy_rdma(s64 length, u64 sliobn, u64 slioba, u64 dliobn, u64 dlioba) { @@ -188,94 +286,41 @@ static inline long h_send_crq(struct ibmvscsis_adapter *adapter, return rc; } -static inline union viosrp_iu *vio_iu(struct iu_entry *iue) -{ - return (union viosrp_iu *)(iue->sbuf->buf); -} - -static int ibmvscsis_check_true(struct se_portal_group *se_tpg) -{ - return 1; -} - -static int ibmvscsis_check_false(struct se_portal_group *se_tpg) -{ - return 0; -} - -static char *ibmvscsis_get_fabric_name(void) -{ - return "ibmvscsis"; -} - -static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg) -{ - struct ibmvscsis_tport *tport = - container_of(se_tpg, struct ibmvscsis_tport, se_tpg); - - return &tport->tport_name[0]; -} - -static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg) -{ - struct ibmvscsis_tport *tport = - container_of(se_tpg, struct ibmvscsis_tport, se_tpg); - - return tport->tport_tpgt; -} - -static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg) -{ - return 1; -} - -static u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg) -{ - return 1; -} - -static void ibmvscsis_release_cmd(struct se_cmd *se_cmd) -{ - struct ibmvscsis_cmnd *cmd = - container_of(se_cmd, struct ibmvscsis_cmnd, se_cmd); - - kfree(cmd); -} +/*****************************************************************************/ +/* Global device driver data areas */ +/*****************************************************************************/ -static int ibmvscsis_shutdown_session(struct se_session *se_sess) -{ - return 0; -} +static const char ibmvscsis_driver_name[] = "ibmvscsis"; +static char system_id[64] = ""; +static char partition_name[97] = "UNKNOWN"; +static unsigned int partition_number = -1; -static void ibmvscsis_close_session(struct se_session *se_sess) -{ - return; -} +static struct class_attribute ibmvscsis_class_attrs[] = { + __ATTR_NULL, +}; -static u32 ibmvscsis_sess_get_index(struct se_session *se_sess) -{ - return 0; -} +static struct device_attribute dev_attr_system_id = + __ATTR(system_id, S_IRUGO, system_id_show, NULL); -static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} +static struct device_attribute dev_attr_partition_number = + __ATTR(partition_number, S_IRUGO, partition_number_show, NULL); -static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl) -{ - return; -} +static struct device_attribute dev_attr_unit_address = + __ATTR(unit_address, S_IRUGO, unit_address_show, NULL); -static int ibmvscsis_get_cmd_state(struct se_cmd *se_cmd) -{ - return 0; -} +static struct attribute *ibmvscsis_dev_attrs[] = { + &dev_attr_system_id.attr, + &dev_attr_partition_number.attr, + &dev_attr_unit_address.attr, +}; +ATTRIBUTE_GROUPS(ibmvscsis_dev); -static void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd) -{ - return; -} +static struct class ibmvscsis_class = { + .name = "ibmvscsis", + .dev_release = ibmvscsis_dev_release, + .class_attrs = ibmvscsis_class_attrs, + .dev_groups = ibmvscsis_dev_groups, +}; static ssize_t ibmvscsis_wwn_version_show(struct config_item *item, char *page) @@ -323,6 +368,7 @@ static ssize_t ibmvscsis_tpg_enable_store(struct config_item *item, tmp); return -EINVAL; } + if (tmp == 1) tport->enabled = true; else @@ -338,1650 +384,1480 @@ static struct configfs_attribute *ibmvscsis_tpg_attrs[] = { NULL, }; -static int ibmvscsis_write_pending(struct se_cmd *se_cmd); -static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd); -static int ibmvscsis_queue_status(struct se_cmd *se_cmd); -static int ibmvscsis_check_stop_free(struct se_cmd *se_cmd) -{ - return target_put_sess_cmd(se_cmd); -} -static void ibmvscsis_aborted_task(struct se_cmd *se_cmd) -{ - pr_debug("ibmvscsis: ibmvscsis_aborted_task\n"); - return; -} +static const struct target_core_fabric_ops ibmvscsis_ops = { + .module = THIS_MODULE, + .name = "ibmvscsis", + .max_data_sg_nents = SCSI_MAX_SG_SEGMENTS, + .get_fabric_name = ibmvscsis_get_fabric_name, + .tpg_get_wwn = ibmvscsis_get_fabric_wwn, + .tpg_get_tag = ibmvscsis_get_tag, + .tpg_get_default_depth = ibmvscsis_get_default_depth, + .tpg_check_demo_mode = ibmvscsis_check_true, + .tpg_check_demo_mode_cache = ibmvscsis_check_true, + .tpg_check_demo_mode_write_protect = ibmvscsis_check_false, + .tpg_check_prod_mode_write_protect = ibmvscsis_check_false, + .tpg_get_inst_index = ibmvscsis_tpg_get_inst_index, + .check_stop_free = ibmvscsis_check_stop_free, + .release_cmd = ibmvscsis_release_cmd, + .shutdown_session = ibmvscsis_shutdown_session, + .close_session = ibmvscsis_close_session, + .sess_get_index = ibmvscsis_sess_get_index, + .write_pending = ibmvscsis_write_pending, + .write_pending_status = ibmvscsis_write_pending_status, + .set_default_node_attributes = ibmvscsis_set_default_node_attrs, + .get_cmd_state = ibmvscsis_get_cmd_state, + .queue_data_in = ibmvscsis_queue_data_in, + .queue_status = ibmvscsis_queue_status, + .queue_tm_rsp = ibmvscsis_queue_tm_rsp, + .aborted_task = ibmvscsis_aborted_task, + /* + * Setup function pointers for logic in target_cor_fabric_configfs.c + */ + .fabric_make_wwn = ibmvscsis_make_tport, + .fabric_drop_wwn = ibmvscsis_drop_tport, + .fabric_make_tpg = ibmvscsis_make_tpg, + .fabric_drop_tpg = ibmvscsis_drop_tpg, -static struct se_portal_group *ibmvscsis_make_nexus(struct ibmvscsis_tport *tport, - const char *name) + .tfc_wwn_attrs = ibmvscsis_wwn_attrs, + .tfc_tpg_base_attrs = ibmvscsis_tpg_attrs, +}; + +static struct vio_device_id ibmvscsis_device_table[] = { + {"v-scsi-host", "IBM,v-scsi-host"}, + {"", ""} +}; + +MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table); + +static struct vio_driver ibmvscsis_driver = { + .name = ibmvscsis_driver_name, + .id_table = ibmvscsis_device_table, + .probe = ibmvscsis_probe, + .remove = ibmvscsis_remove, +}; + +/*****************************************************************************/ +/* End of global device driver data areas */ +/*****************************************************************************/ +static int crq_queue_create(struct crq_queue *queue, + struct ibmvscsis_adapter *adapter) { - struct se_node_acl *acl; + int retrc; + int err; + struct vio_dev *vdev = adapter->dma_dev; - pr_debug("ibmvscsis: make nexus"); - if (tport->se_sess) { - pr_debug("tport->se_sess already exists\n"); - ERR_PTR(-EEXIST); - } + queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); - /* - * Initialize the struct se_session pointer and setup tagpool - * for struct ibmvscsis_cmd descriptors - */ - tport->se_sess = transport_init_session(TARGET_PROT_NORMAL); - if (IS_ERR(tport->se_sess)) { - goto transport_init_fail; + if (!queue->msgs) + goto malloc_failed; + + queue->size = PAGE_SIZE / sizeof(*queue->msgs); + + queue->msg_token = dma_map_single(&vdev->dev, queue->msgs, + queue->size * sizeof(*queue->msgs), + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(&vdev->dev, queue->msg_token)) { + goto map_failed; } - pr_debug("ibmvscsis: make_nexus: se_sess:%p, tport(%p)\n", - tport->se_sess, tport); - pr_debug("ibmvsciss: initiator name:%s, se_tpg:%p\n", - tport->se_sess->se_node_acl->initiatorname, - tport->se_sess->se_tpg); - /* - * Since we are running in 'demo mode' this call will generate a - * struct se_node_acl for the ibmvscsis struct se_portal_group with - * the SCSI Initiator port name of the passed configfs group 'name'. + + retrc = err = h_reg_crq(vdev->unit_address, queue->msg_token, + PAGE_SIZE); + + /* If the adapter was left active for some reason (like kexec) + * try freeing and re-registering */ + if (err == H_RESOURCE) { + err = ibmvscsis_reset_crq_queue(adapter); + } + if( err == 2 ) { + pr_warn("ibmvscsis: Partner adapter not ready\n"); + retrc = 0; + } else if ( err != 0 ) { + pr_err("ibmvscsis: Error 0x%x opening virtual adapter\n", err); + goto reg_crq_failed; + } - acl = core_tpg_check_initiator_node_acl(&tport->se_tpg, - (unsigned char *)name); - if (!acl) { - pr_debug("core_tpg_check_initiator_node_acl() failed" - " for %s\n", name); - goto acl_failed; + queue->cur = 0; + spin_lock_init(&queue->lock); + + INIT_WORK(&adapter->crq_work, handle_crq); + + err = request_irq(vdev->irq, &ibmvscsis_interrupt, + 0, "ibmvscsis", adapter); + if (err) { + pr_err("ibmvscsis: Error 0x%x h_send_crq\n", err); + goto req_irq_failed; } - tport->se_sess->se_node_acl = acl; - /* - * Now register the TCM ibmvscsis virtual I_T Nexus as active. - */ - transport_register_session(&tport->se_tpg, - tport->se_sess->se_node_acl, - tport->se_sess, tport); + err = vio_enable_interrupts(vdev); + if (err != 0 ) { + pr_err("ibmvscsis: Error %d enabling interrupts!!!\n", err); + goto req_irq_failed; + } - return &tport->se_tpg; + return retrc; -acl_failed: - transport_free_session(tport->se_sess); -transport_init_fail: - kfree(tport); - return ERR_PTR(-ENOMEM); +req_irq_failed: + h_free_crq(vdev->unit_address); +reg_crq_failed: + dma_unmap_single(&vdev->dev, queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); +map_failed: + free_page((unsigned long) queue->msgs); +malloc_failed: + return -1; } -static int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport) +/* + * ibmvscsis_probe - ibm vscsis target initialize entry point + * @param dev vio device struct + * @param id vio device id struct + * @return 0 - Success + * Non-zero - Failure + */ +static int ibmvscsis_probe(struct vio_dev *vdev, const struct vio_device_id *id) { - struct se_session *se_sess; + int ret = -ENOMEM; + struct ibmvscsis_adapter *adapter; + struct srp_target *target; + unsigned long flags; - pr_debug("ibmvscsis: drop nexus"); + pr_debug("ibmvscsis: Probe for UA 0x%x\n", vdev->unit_address); - se_sess = tport->se_sess; - if (!se_sess) { - return -ENODEV; + adapter = kzalloc(sizeof(struct ibmvscsis_adapter), GFP_KERNEL); + if (!adapter) + return ret; + target = kzalloc(sizeof(struct srp_target), GFP_KERNEL); + if (!target) + goto free_adapter; + + adapter->dma_dev = vdev; + adapter->target = target; + snprintf(&adapter->tport.tport_name[0], 256, "%s", dev_name(&vdev->dev)); + + ret = read_dma_window(adapter->dma_dev, adapter); + if(ret != 0) { + pr_debug("ibmvscsis: probe failed read dma window\n"); + goto free_target; } + pr_debug("ibmvscsis: Probe: liobn 0x%x, riobn 0x%x\n", adapter->liobn, + adapter->riobn); - /* - * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port - */ - transport_deregister_session(tport->se_sess); + spin_lock_irqsave(&ibmvscsis_dev_lock, flags); + list_add_tail(&adapter->list, &ibmvscsis_dev_list); + spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags); - transport_free_session(tport->se_sess); + ret = srp_target_alloc(target, &vdev->dev, + INITIAL_SRP_LIMIT, + SRP_MAX_IU_LEN); - return 0; -} + adapter->target->ldata = adapter; -static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg) -{ - struct ibmvscsis_tport *tport = container_of(se_tpg, - struct ibmvscsis_tport, se_tpg); + if(ret) { + pr_debug("ibmvscsis: failed target alloc ret: %d\n", ret); + goto free_srp_target; + } - tport->releasing = true; - /* - * Release the virtual I_T Nexus for this ibmvscsis TPG - */ - ibmvscsis_drop_nexus(tport); - /* - * Deregister the se_tpg from TCM.. - */ - core_tpg_deregister(se_tpg); -} + ret = crq_queue_create(&adapter->crq_queue, adapter); + pr_debug("ibmvscsis: failed crq_queue_create ret: %d\n", ret); + if(ret != 0 && ret != H_RESOURCE) { + pr_debug("ibmvscsis: failed crq_queue_create ret: %d\n", ret); + ret = -1; + } -static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn, - struct config_group *group, - const char *name) -{ - struct ibmvscsis_tport *tport = - container_of(wwn, struct ibmvscsis_tport, tport_wwn); - int ret; + if(h_send_crq(adapter, 0xC001000000000000LL, 0) != 0 + && ret != H_RESOURCE) { + pr_warn("ibmvscsis: Failed to send CRQ message\n"); + ret = 0; + } - tport->releasing = false; - ret = core_tpg_register(&tport->tport_wwn, - &tport->se_tpg, - tport->tport_proto_id); - if(ret) - return ERR_PTR(ret); + dev_set_drvdata(&vdev->dev, adapter); - return &tport->se_tpg; + return 0; + +free_srp_target: + srp_target_free(target); +free_target: + kfree(target); +free_adapter: + kfree(adapter); + return ret; } -static struct ibmvscsis_tport *ibmvscsis_lookup_port(const char *name) +static int ibmvscsis_remove(struct vio_dev *dev) { - struct ibmvscsis_tport *tport; - struct vio_dev *vdev; - struct ibmvscsis_adapter *adapter; - int ret; unsigned long flags; + struct ibmvscsis_adapter *adapter = dev_get_drvdata(&dev->dev); spin_lock_irqsave(&ibmvscsis_dev_lock, flags); - list_for_each_entry(adapter, &ibmvscsis_dev_list, list) { - vdev = adapter->dma_dev; - ret = strcmp(dev_name(&vdev->dev), name); - if(ret == 0) { - tport = &adapter->tport; - } - if(tport) - goto found; - } - spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags); - return NULL; -found: + list_del(&adapter->list); spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags); - return tport; -} -static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf, - struct config_group *group, - const char *name) -{ - struct ibmvscsis_tport *tport; - int ret; + crq_queue_destroy(adapter); + srp_target_free(adapter->target); - tport = ibmvscsis_lookup_port(name); - ret = -EINVAL; - if(!tport) - goto err; + kfree(adapter); - tport->tport_proto_id = SCSI_PROTOCOL_SRP; - pr_debug("ibmvscsis: make_tport(%s), pointer:%p tport_id:%x\n", name, - tport, tport->tport_proto_id); - return &tport->tport_wwn; -err: - return ERR_PTR(ret); + return 0; } -static void ibmvscsis_drop_tport(struct se_wwn *wwn) +static int read_dma_window(struct vio_dev *vdev, + struct ibmvscsis_adapter *adapter) { - struct ibmvscsis_tport *tport = container_of(wwn, - struct ibmvscsis_tport, tport_wwn); + const __be32 *dma_window; + const __be32 *prop; - pr_debug("drop_tport(%s\n", - config_item_name(&tport->tport_wwn.wwn_group.cg_item)); -} + /* TODO Using of_parse_dma_window would be better, but it doesn't give + * a way to read multiple windows without already knowing the size of + * a window or the number of windows + */ + dma_window = + (const __be32 *)vio_get_attribute(vdev, "ibm,my-dma-window", + NULL); + if (!dma_window) { + pr_err("ibmvscsis: Couldn't find ibm,my-dma-window property\n"); + return -1; + } -static ssize_t system_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", system_id); -} + adapter->liobn = be32_to_cpu(*dma_window); + dma_window++; -static ssize_t partition_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%x\n", partition_number); -} + prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells", + NULL); + if (!prop) { + pr_warn("ibmvscsis: Couldn't find ibm, \ + #dma-address-cells property\n"); + dma_window++; + } else + dma_window += be32_to_cpu(*prop); -static ssize_t unit_address_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ibmvscsis_adapter *adapter = - container_of(dev, struct ibmvscsis_adapter, dev); - return snprintf(buf, PAGE_SIZE, "%x\n", adapter->dma_dev->unit_address); -} - -static struct class_attribute ibmvscsis_class_attrs[] = { - __ATTR_NULL, -}; - -static struct device_attribute dev_attr_system_id = - __ATTR(system_id, S_IRUGO, system_id_show, NULL); + prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells", + NULL); + if (!prop) { + pr_warn("ibmvscsis: Couldn't find ibm,#dma-size-cells property\n"); + dma_window++; + } else + dma_window += be32_to_cpu(*prop); -static struct device_attribute dev_attr_partition_number = - __ATTR(partition_number, S_IRUGO, partition_number_show, NULL); + /* dma_window should point to the second window now */ + adapter->riobn = be32_to_cpu(*dma_window); -static struct device_attribute dev_attr_unit_address = - __ATTR(unit_address, S_IRUGO, unit_address_show, NULL); + return 0; +} -static struct attribute *ibmvscsis_dev_attrs[] = { - &dev_attr_system_id.attr, - &dev_attr_partition_number.attr, - &dev_attr_unit_address.attr, -}; -ATTRIBUTE_GROUPS(ibmvscsis_dev); +static inline union viosrp_iu *vio_iu(struct iu_entry *iue) +{ + return (union viosrp_iu *)(iue->sbuf->buf); +} static void ibmvscsis_dev_release(struct device *dev) {}; -static struct class ibmvscsis_class = { - .name = "ibmvscsis", - .dev_release = ibmvscsis_dev_release, - .class_attrs = ibmvscsis_class_attrs, - .dev_groups = ibmvscsis_dev_groups, -}; - -static int send_iu(struct iu_entry *iue, u64 length, u8 format) +static char *ibmvscsis_get_fabric_name(void) { - struct srp_target *target = iue->target; - struct ibmvscsis_adapter *adapter = target->ldata; - struct ibmvscsis_crq_msg crq_msg; - __be64 *crq_as_u64 = (__be64*)&crq_msg; - long rc, rc1; - - pr_debug("ibmvscsis: send_iu: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", - (unsigned long)length, - (unsigned long)adapter->liobn, - (unsigned long)iue->sbuf->dma, - (unsigned long)adapter->riobn, - (unsigned long)be64_to_cpu(iue->remote_token)); - - /* First copy the SRP */ - rc = h_copy_rdma(length, adapter->liobn, iue->sbuf->dma, - adapter->riobn, be64_to_cpu(iue->remote_token)); - - if (rc) - pr_err("ibmvscsis: Error %ld transferring data\n", rc); + return "ibmvscsis"; +} - pr_debug("ibmvscsis: crq pre cooked: 0x%x, 0x%llx, 0x%llx\n", - format, length, vio_iu(iue)->srp.rsp.tag); +static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg) +{ + struct ibmvscsis_tport *tport = + container_of(se_tpg, struct ibmvscsis_tport, se_tpg); - crq_msg.valid = 0x80; - crq_msg.format = format; - crq_msg.rsvd = 0; - if (rc == 0) { - crq_msg.status = 0x99; - } else { - crq_msg.status = 0; - } - crq_msg.rsvd1 = 0; - crq_msg.IU_length = cpu_to_be16(length); - crq_msg.IU_data_ptr = vio_iu(iue)->srp.rsp.tag; + return &tport->tport_name[0]; +} - pr_debug("ibmvscsis: send crq: 0x%x, 0x%llx, 0x%llx\n", - adapter->dma_dev->unit_address, - be64_to_cpu(crq_as_u64[0]), - be64_to_cpu(crq_as_u64[1])); +static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg) +{ + struct ibmvscsis_tport *tport = + container_of(se_tpg, struct ibmvscsis_tport, se_tpg); - srp_iu_put(iue); + return tport->tport_tpgt; +} - rc1 = h_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), - be64_to_cpu(crq_as_u64[1])); +static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg) +{ + return 1; +} - if (rc1) { - pr_err("ibmvscsis: %ld sending response\n", rc1); - return rc1; - } +static int ibmvscsis_check_true(struct se_portal_group *se_tpg) +{ + return 1; +} - return rc; +static int ibmvscsis_check_false(struct se_portal_group *se_tpg) +{ + return 0; } -#define SRP_RSP_SENSE_DATA_LEN 18 +static u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg) +{ + return 1; +} -static int send_rsp(struct iu_entry *iue, struct scsi_cmnd *sc, - unsigned char status, unsigned char asc) +static int ibmvscsis_check_stop_free(struct se_cmd *se_cmd) { - union viosrp_iu *iu = vio_iu(iue); - uint64_t tag = iu->srp.rsp.tag; + return target_put_sess_cmd(se_cmd); +} - /* If the linked bit is on and status is good */ - if (test_bit(V_LINKED, &iue->flags) && (status == NO_SENSE)) - status = 0x10; +static void ibmvscsis_release_cmd(struct se_cmd *se_cmd) +{ + struct ibmvscsis_cmnd *cmd = + container_of(se_cmd, struct ibmvscsis_cmnd, se_cmd); - memset(iu, 0, sizeof(struct srp_rsp)); - iu->srp.rsp.opcode = SRP_RSP; - iu->srp.rsp.req_lim_delta = 1; - iu->srp.rsp.tag = tag; + kfree(cmd); +} - if (test_bit(V_DIOVER, &iue->flags)) - iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER; +static int ibmvscsis_shutdown_session(struct se_session *se_sess) +{ + return 0; +} - iu->srp.rsp.data_in_res_cnt = 0; - iu->srp.rsp.data_out_res_cnt = 0; +static void ibmvscsis_close_session(struct se_session *se_sess) +{ + return; +} - iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; +static u32 ibmvscsis_sess_get_index(struct se_session *se_sess) +{ + return 0; +} - iu->srp.rsp.resp_data_len = 0; - iu->srp.rsp.status = status; - if (status) { - uint8_t *sense = iu->srp.rsp.data; +static int ibmvscsis_write_pending(struct se_cmd *se_cmd) +{ + struct ibmvscsis_cmnd *cmd = container_of(se_cmd, + struct ibmvscsis_cmnd, se_cmd); + struct scsi_cmnd *sc = &cmd->sc; + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; + int ret; - if (sc) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; - iu->srp.rsp.sense_data_len = SCSI_SENSE_BUFFERSIZE; - memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE); - } else { - iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION; - iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; - iu->srp.rsp.sense_data_len = SRP_RSP_SENSE_DATA_LEN; + pr_debug("ibmvscsis: ibmvscsis_write_pending\n"); + sc->sdb.table.nents = se_cmd->t_data_nents; + sc->sdb.table.sgl = se_cmd->t_data_sg; - /* Valid bit and 'current errors' */ - sense[0] = (0x1 << 7 | 0x70); - /* Sense key */ - sense[2] = status; - /* Additional sense length */ - sense[7] = 0xa; /* 10 bytes */ - /* Additional sense code */ - sense[12] = asc; - } + ret = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, + ibmvscsis_rdma, 1, 1); + if (ret) { + pr_err("ibmvscsis: srp_transfer_data() failed: %d\n", ret); + return -EAGAIN; /* Signal QUEUE_FULL */ } + /* + * We now tell TCM to add this WRITE CDB directly into the TCM storage + * object execution queue. + */ + target_execute_cmd(se_cmd); + return 0; +} - send_iu(iue, sizeof(iu->srp.rsp) + SRP_RSP_SENSE_DATA_LEN, - VIOSRP_SRP_FORMAT); - +static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd) +{ return 0; } -static int send_adapter_info(struct iu_entry *iue, - dma_addr_t remote_buffer, u16 length) +static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl) { - struct srp_target *target = iue->target; - struct ibmvscsis_adapter *adapter = target->ldata; - dma_addr_t data_token; - struct mad_adapter_info_data *info; - int err; + return; +} - info = dma_alloc_coherent(&adapter->dma_dev->dev, sizeof(*info), - &data_token, GFP_KERNEL); - if (!info) { - pr_err("ibmvscsis: bad dma_alloc_coherent %p\n", target); - return 1; - } +static int ibmvscsis_get_cmd_state(struct se_cmd *se_cmd) +{ + return 0; +} - pr_debug("ibmvscsis: get_remote_info: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", - (unsigned long)sizeof(*info), - (unsigned long)adapter->liobn, - (unsigned long)data_token, - (unsigned long)adapter->riobn, - (unsigned long)remote_buffer); +static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd) +{ + struct ibmvscsis_cmnd *cmd = container_of(se_cmd, + struct ibmvscsis_cmnd, se_cmd); + struct scsi_cmnd *sc = &cmd->sc; + /* + * Check for overflow residual count + */ + pr_debug("ibmvscsis: ibmvscsis_queue_data_in\n"); - /* Get remote info */ - err = h_copy_rdma(sizeof(*info), adapter->riobn, - be64_to_cpu(remote_buffer), - adapter->liobn, data_token); + if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) + scsi_set_resid(sc, se_cmd->residual_count); - if (err == H_SUCCESS) { - pr_err("ibmvscsis: Client connect: %s (%d)\n", - info->partition_name, info->partition_number); - } + sc->sdb.length = se_cmd->data_length; - memset(info, 0, sizeof(*info)); - - strcpy(info->srp_version, "16.a"); - strncpy(info->partition_name, partition_name, - sizeof(info->partition_name)); - pr_debug("ibmvscsis: partition_number: %x\n", partition_number); - - info->partition_number = cpu_to_be32(partition_number); - info->mad_version = cpu_to_be32(1); - info->os_type = cpu_to_be32(2); - info->port_max_txu[0] = cpu_to_be32(SCSI_MAX_SG_SEGMENTS * PAGE_SIZE); - - pr_debug("ibmvscsis: send info to remote: 0x%lx 0x%lx 0x%lx \ - 0x%lx 0x%lx\n",(unsigned long)sizeof(*info), - (unsigned long)adapter->liobn, - (unsigned long)data_token, - (unsigned long)adapter->riobn, - (unsigned long)remote_buffer); - - /* Send our info to remote */ - err = h_copy_rdma(sizeof(*info), adapter->liobn, data_token, - adapter->riobn, be64_to_cpu(remote_buffer)); - - dma_free_coherent(&adapter->dma_dev->dev, sizeof(*info), info, - data_token); - if (err != H_SUCCESS) { - pr_err("ibmvscsis: Error sending adapter info %d\n", err); - return 1; - } + sc->sdb.table.nents = se_cmd->t_data_nents; + sc->sdb.table.sgl = se_cmd->t_data_sg; + /* + * This will call srp_transfer_data() and post the response + * to VIO via libsrp. + */ + ibmvscsis_cmnd_done(sc); + pr_debug("ibmvscsis: queue_data_in"); return 0; } -static int process_mad_iu(struct iu_entry *iue) +static int ibmvscsis_queue_status(struct se_cmd *se_cmd) { - union viosrp_iu *iu = vio_iu(iue); - struct viosrp_adapter_info *info; - struct viosrp_host_config *conf; - - switch (be32_to_cpu(iu->mad.empty_iu.common.type)) { - case VIOSRP_EMPTY_IU_TYPE: - pr_err("ibmvscsis: %s\n", "Unsupported EMPTY MAD IU"); - break; - case VIOSRP_ERROR_LOG_TYPE: - pr_err("ibmvscsis: %s\n", "Unsupported ERROR LOG MAD IU"); - iu->mad.error_log.common.status = 1; - send_iu(iue, sizeof(iu->mad.error_log), VIOSRP_MAD_FORMAT); - break; - case VIOSRP_ADAPTER_INFO_TYPE: - info = &iu->mad.adapter_info; - info->common.status = send_adapter_info(iue, info->buffer, - info->common.length); - send_iu(iue, sizeof(*info), VIOSRP_MAD_FORMAT); - break; - case VIOSRP_HOST_CONFIG_TYPE: - conf = &iu->mad.host_config; - conf->common.status = 1; - send_iu(iue, sizeof(*conf), VIOSRP_MAD_FORMAT); - break; - default: - pr_err("ibmvscsis: Unknown type %u\n", iu->srp.rsp.opcode); - iu->mad.empty_iu.common.status = - cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); - send_iu(iue, sizeof(iu->mad), VIOSRP_MAD_FORMAT); - break; - } + struct ibmvscsis_cmnd *cmd = container_of(se_cmd, + struct ibmvscsis_cmnd, se_cmd); + struct scsi_cmnd *sc = &cmd->sc; + /* + * Copy any generated SENSE data into sc->sense_buffer and + * set the appropriate sc->result to be translated by + * ibmvscsis_cmnd_done() + */ + pr_debug("ibmvscsis: ibmvscsis_queue_status\n"); + if (se_cmd->sense_buffer && + ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) || + (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { + memcpy(sc->sense_buffer, se_cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE); + sc->result = SAM_STAT_CHECK_CONDITION; + set_driver_byte(sc, DRIVER_SENSE); + } else + sc->result = se_cmd->scsi_status; - return 1; + set_host_byte(sc, DID_OK); + if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) || + (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)) + scsi_set_resid(sc, se_cmd->residual_count); +// sc->scsi_done(sc); + /* + * Finally post the response to VIO via libsrp. + */ + ibmvscsis_cmnd_done(sc); + return 0; } -static void process_login(struct iu_entry *iue) +static void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd) { - union viosrp_iu *iu = vio_iu(iue); - struct srp_login_rsp *rsp = &iu->srp.login_rsp; - struct srp_login_rej *rej = &iu->srp.login_rej; - u64 tag = iu->srp.rsp.tag; + struct ibmvscsis_cmnd *cmd = container_of(se_cmd, + struct ibmvscsis_cmnd, se_cmd); + struct scsi_cmnd *sc = &cmd->sc; + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; struct srp_target *target = iue->target; struct ibmvscsis_adapter *adapter = target->ldata; - struct vio_dev *vdev = adapter->dma_dev; - char name[16]; - struct se_portal_group *se_tpg; - - /* - * TODO handle case that requested size is wrong and buffer - * format is wrong - */ - memset(iu, 0, max(sizeof(*rsp), sizeof(*rej))); + struct srp_cmd *srp_cmd; + int ret; + enum dma_data_direction dir; - snprintf(name, sizeof(name), "%x", vdev->unit_address); + pr_debug("ibmvscsis: ibmvscsis_queue_tm_rsp\n"); - if(!&adapter->tport.enabled) { - rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); - pr_err("ibmvscsis: Rejected SRP_LOGIN_REQ because target %s" - " has not yet been enabled", name); - goto reject; + if (unlikely(transport_check_aborted_status(se_cmd, false))) { + atomic_inc(&adapter->req_lim_delta); + srp_iu_put(iue); + goto out; } - se_tpg = ibmvscsis_make_nexus(&adapter->tport, &adapter->tport.tport_name[0]); - if(!se_tpg) { - pr_debug("ibmvscsis: login make nexus fail se_tpg(%p)\n", - se_tpg); - goto reject; + srp_cmd = &vio_iu(iue)->srp.cmd; + dir = srp_cmd_direction(srp_cmd); + + if (dir == DMA_FROM_DEVICE) { + ret = srp_transfer_data(sc, srp_cmd, ibmvscsis_rdma, 1, 1); + if( ret == -ENOMEM) + pr_debug("ibmvscsis: res queue full\n"); + else if (ret) + pr_err("ibmvscsis: tm_rsp failed\n"); } - rsp->opcode = SRP_LOGIN_RSP; + send_rsp(iue, sc, NO_SENSE, 0x00); + return; +out: + return; +} - rsp->req_lim_delta = cpu_to_be32(INITIAL_SRP_LIMIT); +static void ibmvscsis_aborted_task(struct se_cmd *se_cmd) +{ + pr_debug("ibmvscsis: ibmvscsis_aborted_task\n"); + return; +} - pr_debug("ibmvscsis: process_login, tag:%llu\n", tag); +static struct se_portal_group *ibmvscsis_make_nexus(struct ibmvscsis_tport *tport, + const char *name) +{ + struct se_node_acl *acl; - rsp->tag = tag; - rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); - rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); - /* direct and indirect */ - rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT - | SRP_BUF_FORMAT_INDIRECT); + pr_debug("ibmvscsis: make nexus"); + if (tport->se_sess) { + pr_debug("tport->se_sess already exists\n"); + ERR_PTR(-EEXIST); + } - send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT); - return; + /* + * Initialize the struct se_session pointer and setup tagpool + * for struct ibmvscsis_cmd descriptors + */ + tport->se_sess = transport_init_session(TARGET_PROT_NORMAL); + if (IS_ERR(tport->se_sess)) { + goto transport_init_fail; + } + pr_debug("ibmvscsis: make_nexus: se_sess:%p, tport(%p)\n", + tport->se_sess, tport); + pr_debug("ibmvsciss: initiator name:%s, se_tpg:%p\n", + tport->se_sess->se_node_acl->initiatorname, + tport->se_sess->se_tpg); + /* + * Since we are running in 'demo mode' this call will generate a + * struct se_node_acl for the ibmvscsis struct se_portal_group with + * the SCSI Initiator port name of the passed configfs group 'name'. + */ -reject: - rej->opcode = SRP_LOGIN_REJ; - rej->tag = tag; - rej->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT - | SRP_BUF_FORMAT_INDIRECT); + acl = core_tpg_check_initiator_node_acl(&tport->se_tpg, + (unsigned char *)name); + if (!acl) { + pr_debug("core_tpg_check_initiator_node_acl() failed" + " for %s\n", name); + goto acl_failed; + } + tport->se_sess->se_node_acl = acl; - send_iu(iue, sizeof(*rej), VIOSRP_SRP_FORMAT); + /* + * Now register the TCM ibmvscsis virtual I_T Nexus as active. + */ + transport_register_session(&tport->se_tpg, + tport->se_sess->se_node_acl, + tport->se_sess, tport); + + return &tport->se_tpg; + +acl_failed: + transport_free_session(tport->se_sess); +transport_init_fail: + kfree(tport); + return ERR_PTR(-ENOMEM); } -static void process_tsk_mgmt(struct iu_entry *iue) +static int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport) { - union viosrp_iu *iu = vio_iu(iue); - uint64_t tag = iu->srp.rsp.tag; - uint8_t *resp_data = iu->srp.rsp.data; + struct se_session *se_sess; - memset(iu, 0, sizeof(struct srp_rsp)); - iu->srp.rsp.opcode = SRP_RSP; - iu->srp.rsp.req_lim_delta = 1; - iu->srp.rsp.tag = tag; + pr_debug("ibmvscsis: drop nexus"); - iu->srp.rsp.data_in_res_cnt = 0; - iu->srp.rsp.data_out_res_cnt = 0; + se_sess = tport->se_sess; + if (!se_sess) { + return -ENODEV; + } - iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; + /* + * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port + */ + transport_deregister_session(tport->se_sess); - iu->srp.rsp.resp_data_len = 4; - /* TASK MANAGEMENT FUNCTION NOT SUPPORTED for now */ - resp_data[3] = 4; + transport_free_session(tport->se_sess); - send_iu(iue, sizeof(iu->srp.rsp) + iu->srp.rsp.resp_data_len, - VIOSRP_SRP_FORMAT); + return 0; } -static inline struct viosrp_crq *next_crq(struct crq_queue *queue) +static struct ibmvscsis_tport *ibmvscsis_lookup_port(const char *name) { - struct viosrp_crq *crq; + struct ibmvscsis_tport *tport; + struct vio_dev *vdev; + struct ibmvscsis_adapter *adapter; + int ret; unsigned long flags; - spin_lock_irqsave(&queue->lock, flags); - crq = &queue->msgs[queue->cur]; - if (crq->valid & 0x80) { - if (++queue->cur == queue->size) - queue->cur = 0; - rmb(); - } else - crq = NULL; - spin_unlock_irqrestore(&queue->lock, flags); - - return crq; + spin_lock_irqsave(&ibmvscsis_dev_lock, flags); + list_for_each_entry(adapter, &ibmvscsis_dev_list, list) { + vdev = adapter->dma_dev; + ret = strcmp(dev_name(&vdev->dev), name); + if(ret == 0) { + tport = &adapter->tport; + } + if(tport) + goto found; + } + spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags); + return NULL; +found: + spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags); + return tport; } -#define NO_SUCH_LUN ((uint64_t)-1LL) - -enum scsi_lun_addr_method { - SCSI_LUN_ADDR_METHOD_PERIPHERAL = 0, - SCSI_LUN_ADDR_METHOD_FLAT = 1, - SCSI_LUN_ADDR_METHOD_LUN = 2, - SCSI_LUN_ADDR_METHOD_EXTENDED_LUN = 3, -}; - -static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len) +static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf, + struct config_group *group, + const char *name) { - uint64_t res = NO_SUCH_LUN; - int addressing_method; - - if (unlikely(len < 2)) { - pr_err("Illegal LUN length %d, expected 2 bytes or more\n", - len); - goto out; - } - - switch (len) { - case 8: - if ((*((__be64 *)lun) & - cpu_to_be64(0x0000FFFFFFFFFFFFLL)) != 0) - goto out_err; - break; - case 4: - if (*((__be16 *)&lun[2]) != 0) - goto out_err; - break; - case 6: - if (*((__be32 *)&lun[2]) != 0) - goto out_err; - break; - case 2: - break; - default: - goto out_err; - } + struct ibmvscsis_tport *tport; + int ret; - addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */ - switch (addressing_method) { - case SCSI_LUN_ADDR_METHOD_PERIPHERAL: - case SCSI_LUN_ADDR_METHOD_FLAT: - case SCSI_LUN_ADDR_METHOD_LUN: - res = *(lun + 1) | (((*lun) & 0x3f) << 8); - break; + tport = ibmvscsis_lookup_port(name); + ret = -EINVAL; + if(!tport) + goto err; - case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN: - default: - pr_err("Unimplemented LUN addressing method %u\n", - addressing_method); - break; - } + tport->tport_proto_id = SCSI_PROTOCOL_SRP; + pr_debug("ibmvscsis: make_tport(%s), pointer:%p tport_id:%x\n", name, + tport, tport->tport_proto_id); + return &tport->tport_wwn; +err: + return ERR_PTR(ret); +} -out: - return res; +static void ibmvscsis_drop_tport(struct se_wwn *wwn) +{ + struct ibmvscsis_tport *tport = container_of(wwn, + struct ibmvscsis_tport, tport_wwn); -out_err: - pr_err("Support for multi-level LUNs has not yet been implemented\n"); - goto out; + pr_debug("drop_tport(%s\n", + config_item_name(&tport->tport_wwn.wwn_group.cg_item)); } -static int tcm_queuecommand(struct ibmvscsis_adapter *adapter, - struct ibmvscsis_cmnd *vsc, - struct srp_cmd *scmd) +static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn, + struct config_group *group, + const char *name) { - struct se_cmd *se_cmd; - int attr; - u64 data_len; + struct ibmvscsis_tport *tport = + container_of(wwn, struct ibmvscsis_tport, tport_wwn); int ret; - uint64_t unpacked_lun; - switch (scmd->task_attr) { - case SRP_SIMPLE_TASK: - attr = TCM_SIMPLE_TAG; - break; - case SRP_ORDERED_TASK: - attr = TCM_ORDERED_TAG; - break; - case SRP_HEAD_TASK: - attr = TCM_HEAD_TAG; - break; - case SRP_ACA_TASK: - attr = TCM_ACA_TAG; - break; - default: - pr_err("ibmvscsis: Task attribute %d not supported\n", - scmd->task_attr); - attr = TCM_SIMPLE_TAG; - } + tport->releasing = false; - pr_debug("ibmvscsis: srp_data_length: %llx, srp_direction:%x\n", - srp_data_length(scmd, srp_cmd_direction(scmd)), - srp_cmd_direction(scmd)); - data_len = srp_data_length(scmd, srp_cmd_direction(scmd)); + ret = core_tpg_register(&tport->tport_wwn, + &tport->se_tpg, + tport->tport_proto_id); + if(ret) + return ERR_PTR(ret); - vsc->se_cmd.tag = scmd->tag; - se_cmd = &vsc->se_cmd; + return &tport->se_tpg; +} - pr_debug("ibmvscsis: size of lun:%lx, lun:%s\n", sizeof(scmd->lun), - &scmd->lun.scsi_lun[0]); +static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg) +{ + struct ibmvscsis_tport *tport = container_of(se_tpg, + struct ibmvscsis_tport, se_tpg); - unpacked_lun = ibmvscsis_unpack_lun((uint8_t *)&scmd->lun, - sizeof(scmd->lun)); + tport->releasing = true; + tport->enabled = false; - pr_debug("ibmvscsis: tcm_queuecommand- se_cmd(%p), se_sess(%p)," - " cdb: %x, sense: %x, unpacked_lun: %llx," - " data_length: %llx, task_attr: %x, data_dir: %x" - " flags: %x, tag:%llx, packed_lun:%llx\n", - se_cmd, adapter->tport.se_sess, - scmd->cdb[0], vsc->sense_buf[0], unpacked_lun, - data_len, attr, srp_cmd_direction(scmd), - TARGET_SCF_ACK_KREF, scmd->tag, - be64_to_cpu(&scmd->lun)); + /* + * Release the virtual I_T Nexus for this ibmvscsis TPG + */ + ibmvscsis_drop_nexus(tport); + /* + * Deregister the se_tpg from TCM.. + */ + core_tpg_deregister(se_tpg); +} - ret = target_submit_cmd(se_cmd, adapter->tport.se_sess, - &scmd->cdb[0], &vsc->sense_buf[0], unpacked_lun, - data_len, attr, srp_cmd_direction(scmd), - 0); - if(ret != 0) { - ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - pr_debug("ibmvscsis: tcm_queuecommand fail submit_cmd\n"); - goto send_sense; - } - return 0; +static ssize_t system_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", system_id); +} -send_sense: - transport_send_check_condition_and_sense(&vsc->se_cmd, ret, 0); - return -1; +static ssize_t partition_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%x\n", partition_number); } -struct inquiry_data { - u8 qual_type; - u8 rmb_reserve; - u8 version; - u8 aerc_naca_hisup_format; - u8 addl_len; - u8 sccs_reserved; - u8 bque_encserv_vs_multip_mchngr_reserved; - u8 reladr_reserved_linked_cmdqueue_vs; - char vendor[8]; - char product[16]; - char revision[4]; - char vendor_specific[20]; - char reserved1[2]; - char version_descriptor[16]; - char reserved2[22]; - char unique[158]; -}; +static ssize_t unit_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ibmvscsis_adapter *adapter = + container_of(dev, struct ibmvscsis_adapter, dev); + return snprintf(buf, PAGE_SIZE, "%x\n", adapter->dma_dev->unit_address); +} -static int ibmvscsis_inquiry(struct ibmvscsis_adapter *adapter, - struct srp_cmd *cmd, char *data) +static int get_system_info(void) { - struct se_portal_group *se_tpg = &adapter->tport.se_tpg; - struct inquiry_data *id = (struct inquiry_data *)data; - u64 unpacked_lun, lun = scsilun_to_int(&cmd->lun); - u8 *cdb = cmd->cdb; - int len; - struct se_lun *se_lun; - int found_lun = 0; - - if (!data) - pr_err("ibmvscsis: %s %d: oomu\n", __func__, __LINE__); - - if (((cdb[1] & 0x3) == 0x3) || (!(cdb[1] & 0x3) && cdb[2])) { - pr_err("ibmvscsis: %s %d: invalid req\n", __func__, __LINE__); - return 0; - } + struct device_node *rootdn, *vdevdn; + const char *id, *model, *name; + const unsigned int *num; - if (cdb[1] & 0x3) - pr_err("ibmvscsis: %s %d: needs the normal path\n", - __func__, __LINE__); - else { - id->qual_type = TYPE_DISK; - id->rmb_reserve = 0x00; - id->version = 0x84; /* ISO/IE */ - id->aerc_naca_hisup_format = 0x22; /* naca & fmt 0x02 */ - id->addl_len = sizeof(*id) - 4; - id->bque_encserv_vs_multip_mchngr_reserved = 0x00; - id->reladr_reserved_linked_cmdqueue_vs = 0x02; /* CMDQ */ - memcpy(id->vendor, "IBM ", 8); - /* - * Don't even ask about the next bit. AIX uses - * hardcoded device naming to recognize device types - * and their client won't work unless we use VOPTA and - * VDASD. - */ - if (id->qual_type == TYPE_ROM) - memcpy(id->product, "VOPTA blkdev ", 16); - else - memcpy(id->product, "VDASD blkdev ", 16); - - memcpy(id->revision, "0001", 4); - - snprintf(id->unique, sizeof(id->unique), - "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n", - system_id, - partition_number, - adapter->dma_dev->unit_address, - GETBUS(lun), - GETTARGET(lun), - GETLUN(lun)); - } + pr_debug("ibmvscsis: getsysteminfo"); + rootdn = of_find_node_by_path("/"); + if (!rootdn) + return -ENOENT; - len = min_t(int, sizeof(*id), cdb[4]); + model = of_get_property(rootdn, "model", NULL); + id = of_get_property(rootdn, "system-id", NULL); + if (model && id) + snprintf(system_id, sizeof(system_id), "%s-%s", model, id); - unpacked_lun = scsilun_to_int(&cmd->lun); - pr_debug("ibmvscsis: inquiry, unpacked_lun:%llx\n", unpacked_lun); + name = of_get_property(rootdn, "ibm,partition-name", NULL); + if (name) + strncpy(partition_name, name, sizeof(partition_name)); - spin_lock(&se_tpg->session_lock); + num = of_get_property(rootdn, "ibm,partition-no", NULL); + if (num) + partition_number = of_read_number(num, 1); - hlist_for_each_entry(se_lun, &se_tpg->tpg_lun_hlist, link) { - pr_debug("ibmvscsis: inquiry, se_lun:%llx unpacked_lun:%llx\n", - se_lun->unpacked_lun, unpacked_lun); - if (se_lun->unpacked_lun == unpacked_lun) { - pr_debug("ibmvscsis: found lun\n"); - found_lun = 1; - break; - } - } + of_node_put(rootdn); - spin_unlock(&se_tpg->session_lock); + vdevdn = of_find_node_by_path("/vdevice"); + vdevdn = of_find_node_by_path("/vdevice"); + if (vdevdn) { + const unsigned *mvds; - if (!found_lun) { - data[0] = TYPE_NO_LUN; + mvds = of_get_property(vdevdn, "ibm,max-virtual-dma-size", + NULL); + if (mvds) + max_vdma_size = *mvds; + of_node_put(vdevdn); } - return len; -} + return 0; +}; -static int ibmvscsis_mode_sense(struct ibmvscsis_adapter *adapter, - struct srp_cmd *cmd, char *mode) +static irqreturn_t ibmvscsis_interrupt(int dummy, void *data) { - int bytes = 0; - struct se_portal_group *se_tpg = &adapter->tport.se_tpg; - u64 unpacked_lun; - struct se_lun *lun = NULL; - u32 blocks = 0 ; - - unpacked_lun = scsilun_to_int(&cmd->lun); - - spin_lock(&se_tpg->session_lock); - - hlist_for_each_entry(lun, &se_tpg->tpg_lun_hlist, link) { - if (lun->unpacked_lun == unpacked_lun) { - blocks = lun->lun_se_dev->transport->get_blocks( - lun->lun_se_dev); - break; - } - } + struct ibmvscsis_adapter *adapter = data; - spin_unlock(&se_tpg->session_lock); - - switch (cmd->cdb[2]) { - case 0: - case 0x3f: - /* Default Medium*/ - mode[1] = 0x00; - /* if (iue->req.vd->b.ro) */ - if (0) - mode[2] = 0x80; /* device specific */ - else - mode[2] = 0x00; /*device specific */ - - /* note the DPOFUA bit is set to zero */ - mode[3] = 0x08; /* block descriptor length */ - *((u32 *) &mode[4]) = blocks - 1; - *((u32 *) &mode[8]) = 512; - bytes = mode[0] = 12; /* length */ - break; - /* cache page */ - case 0x08: - mode[1] = 0x00; /* Default Medium */ - if (0) - mode[2] = 0x80; - else - mode[2] = 0x00; - - mode[3] = 0x08; - *((u32 *) &mode[4]) = blocks - 1; - *((u32 *) &mode[8]) = 512; - - /* cache page */ - mode[12] = 0x08; /* page */ - mode[13] = 0x12; /* page length */ - mode[14] = 0x01; /* no cache (0x04 for read/write cache) */ - - bytes = mode[0] = 12 + mode[13]; /* length */ - break; - } + pr_debug("ibmvscsis: there is an interrupt\n"); + vio_disable_interrupts(adapter->dma_dev); + queue_work(vtgtd, &adapter->crq_work); - return bytes; + return IRQ_HANDLED; } -static int ibmvscsis_report_luns(struct ibmvscsis_adapter *adapter, - struct srp_cmd *cmd, u64 *data) +static int process_srp_iu(struct iu_entry *iue) { - struct se_portal_group *se_tpg = &adapter->tport.se_tpg; - int idx; - int alen, oalen, nr_luns, rbuflen = 4096; - struct se_lun *se_lun; - - alen = get_unaligned_be32(&cmd->cdb[6]); - - alen &= ~(8 - 1); - oalen = alen; + union viosrp_iu *iu = vio_iu(iue); + struct srp_target *target = iue->target; + struct ibmvscsis_adapter *adapter = target->ldata; + u8 opcode = iu->srp.rsp.opcode; + unsigned long flags; + int err = 1; - if (scsilun_to_int(&cmd->lun)) { - nr_luns = 1; + spin_lock_irqsave(&target->lock, flags); + if (adapter->tport.releasing) { + spin_unlock_irqrestore(&target->lock, flags); + pr_err("ibmvscsis: process_srp_iu, no tpg, releasing:%x\n", + adapter->tport.releasing); + srp_iu_put(iue); goto done; } + spin_unlock_irqrestore(&target->lock, flags); - alen -= 8; - rbuflen -= 8; /* FIXME */ - idx = 2; - nr_luns = 1; - - spin_lock(&se_tpg->session_lock); - hlist_for_each_entry(se_lun, &se_tpg->tpg_lun_hlist, link) { - data[idx++] = cpu_to_be64(se_lun->unpacked_lun); - alen -= 8; - if (!alen) - break; - rbuflen -= 8; - if (!rbuflen) - break; - - nr_luns++; + switch (opcode) { + case SRP_LOGIN_REQ: + pr_debug("ibmvscsis: srploginreq"); + process_login(iue); + break; + case SRP_TSK_MGMT: + pr_debug("ibmvscsis: srp task mgmt"); + process_tsk_mgmt(iue); + break; + case SRP_CMD: + pr_debug("ibmvscsis: srpcmd"); + pr_debug("ibmvscsis: process_srp_iu, iu_entry: %llx\n", + (u64)iue->sbuf->buf); + err = ibmvscsis_queuecommand(adapter, iue); + if(err) { + srp_iu_put(iue); + pr_debug("ibmvscsis: can't queue cmd\n"); + } + break; + case SRP_LOGIN_RSP: + case SRP_I_LOGOUT: + case SRP_T_LOGOUT: + case SRP_RSP: + case SRP_CRED_REQ: + case SRP_CRED_RSP: + case SRP_AER_REQ: + case SRP_AER_RSP: + pr_err("ibmvscsis: Unsupported type %u\n", opcode); + break; + default: + pr_err("ibmvscsis: Unknown type %u\n", opcode); } - spin_unlock(&se_tpg->session_lock); done: - put_unaligned_be32(nr_luns * 8, data); - return min(oalen, nr_luns * 8 + 8); + return err; } -static int ibmvscsis_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg, - struct srp_direct_buf *md, int nmd, - enum dma_data_direction dir, unsigned int rest) +static void process_iu(struct viosrp_crq *crq, + struct ibmvscsis_adapter *adapter) { - struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; - struct srp_target *target = iue->target; - struct ibmvscsis_adapter *adapter = target->ldata; - dma_addr_t token; + struct iu_entry *iue; long err; - unsigned int done = 0; - int i, sidx, soff; - - sidx = soff = 0; - token = sg_dma_address(sg + sidx); - - for (i = 0; i < nmd && rest; i++) { - unsigned int mdone, mlen; - mlen = min(rest, be32_to_cpu(md[i].len)); - for (mdone = 0; mlen;) { - int slen = min(sg_dma_len(sg + sidx) - soff, mlen); - - if (dir == DMA_TO_DEVICE) - err = h_copy_rdma(slen, - adapter->riobn, - be64_to_cpu(md[i].va) + mdone, - adapter->liobn, - token + soff); - else - err = h_copy_rdma(slen, - adapter->liobn, - token + soff, - adapter->riobn, - be64_to_cpu(md[i].va)+mdone); + iue = srp_iu_get(adapter->target); + if (!iue) { + pr_err("Error getting IU from pool %p\n", iue); + return; + } - if (err != H_SUCCESS) { - pr_err("ibmvscsis: rdma error %d %d %ld\n", - dir, slen, err); - return -EIO; - } + iue->remote_token = crq->IU_data_ptr; - mlen -= slen; - mdone += slen; - soff += slen; - done += slen; + err = h_copy_rdma(be16_to_cpu(crq->IU_length), adapter->riobn, + be64_to_cpu(crq->IU_data_ptr), + adapter->liobn, iue->sbuf->dma); - if (soff == sg_dma_len(sg + sidx)) { - sidx++; - soff = 0; - token = sg_dma_address(sg + sidx); + if (err != H_SUCCESS) { + pr_err("ibmvscsis: %ld transferring data error %p\n", err, iue); + srp_iu_put(iue); + } - if (sidx > nsg) { - pr_err("ibmvscsis: out of sg %p %d %d\n", - iue, sidx, nsg); - return -EIO; - } - } - } - rest -= mlen; + if (crq->format == VIOSRP_MAD_FORMAT) { + process_mad_iu(iue); + } + else { + pr_debug("ibmvscsis: process srpiu"); + process_srp_iu(iue); } - return 0; } -static int ibmvscsis_cmnd_done(struct scsi_cmnd *sc) +static void process_crq(struct viosrp_crq *crq, + struct ibmvscsis_adapter *adapter) { - unsigned long flags; - struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; - struct srp_target *target = iue->target; - int err = 0; - - if (scsi_sg_count(sc)) - err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, - ibmvscsis_rdma, 1, 1); - - spin_lock_irqsave(&target->lock, flags); - list_del(&iue->ilist); - spin_unlock_irqrestore(&target->lock, flags); - - if (err || sc->result != SAM_STAT_GOOD) { - pr_err("ibmvscsis: operation failed %p %d %x\n", - iue, sc->result, vio_iu(iue)->srp.cmd.cdb[0]); - send_rsp(iue, sc, HARDWARE_ERROR, 0x00); - } else - send_rsp(iue, sc, NO_SENSE, 0x00); - - srp_iu_put(iue); - return 0; + switch (crq->valid) { + case 0xC0: + /* initialization */ + switch (crq->format) { + case 0x01: + h_send_crq(adapter, 0xC002000000000000, 0); + break; + case 0x02: + break; + default: + pr_err("ibmvscsis: Unknown format %u\n", crq->format); + } + break; + case 0xFF: + /* transport event */ + break; + case 0x80: + /* real payload */ + switch (crq->format) { + case VIOSRP_SRP_FORMAT: + case VIOSRP_MAD_FORMAT: + pr_debug("ibmvscsis: case viosrp mad crq: 0x%x, 0x%x," + " 0x%x, 0x%x, 0x%x, 0x%x, 0x%llx\n", + crq->valid, crq->format, crq->reserved, + crq->status, be16_to_cpu(crq->timeout), + be16_to_cpu(crq->IU_length), + be64_to_cpu(crq->IU_data_ptr)); + process_iu(crq, adapter); + break; + case VIOSRP_OS400_FORMAT: + case VIOSRP_AIX_FORMAT: + case VIOSRP_LINUX_FORMAT: + case VIOSRP_INLINE_FORMAT: + pr_err("ibmvscsis: Unsupported format %u\n", + crq->format); + break; + default: + pr_err("ibmvscsis: Unknown format %u\n", + crq->format); + } + break; + default: + pr_err("ibmvscsis: unknown message type 0x%02x!?\n", + crq->valid); + } } -static int ibmvscsis_write_pending(struct se_cmd *se_cmd) +static void handle_crq(struct work_struct *work) { - struct ibmvscsis_cmnd *cmd = container_of(se_cmd, - struct ibmvscsis_cmnd, se_cmd); - struct scsi_cmnd *sc = &cmd->sc; - struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; - int ret; + struct ibmvscsis_adapter *adapter = + container_of(work, struct ibmvscsis_adapter, crq_work); + struct viosrp_crq *crq; + int done = 0; - pr_debug("ibmvscsis: ibmvscsis_write_pending\n"); - sc->sdb.table.nents = se_cmd->t_data_nents; - sc->sdb.table.sgl = se_cmd->t_data_sg; + while (!done) { + while ((crq = next_crq(&adapter->crq_queue)) != NULL) { + process_crq(crq, adapter); + crq->valid = 0x00; + } - ret = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, - ibmvscsis_rdma, 1, 1); - if (ret) { - pr_err("ibmvscsis: srp_transfer_data() failed: %d\n", ret); - return -EAGAIN; /* Signal QUEUE_FULL */ + vio_enable_interrupts(adapter->dma_dev); + + crq = next_crq(&adapter->crq_queue); + if (crq) { + vio_disable_interrupts(adapter->dma_dev); + process_crq(crq, adapter); + crq->valid = 0x00; + } else + done = 1; } - /* - * We now tell TCM to add this WRITE CDB directly into the TCM storage - * object execution queue. - */ - target_execute_cmd(se_cmd); - return 0; } -static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd) +static int ibmvscsis_reset_crq_queue(struct ibmvscsis_adapter *adapter) { - struct ibmvscsis_cmnd *cmd = container_of(se_cmd, - struct ibmvscsis_cmnd, se_cmd); - struct scsi_cmnd *sc = &cmd->sc; - /* - * Check for overflow residual count - */ - pr_debug("ibmvscsis: ibmvscsis_queue_data_in\n"); - - if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) - scsi_set_resid(sc, se_cmd->residual_count); + int rc = 0; + struct vio_dev *vdev = adapter->dma_dev; + struct crq_queue *queue = &adapter->crq_queue; - sc->sdb.length = se_cmd->data_length; + /* Close the CRQ */ + h_free_crq(vdev->unit_address); - sc->sdb.table.nents = se_cmd->t_data_nents; - sc->sdb.table.sgl = se_cmd->t_data_sg; + /* Clean out the queue */ + memset(queue->msgs, 0x00, PAGE_SIZE); + queue->cur = 0; - /* - * This will call srp_transfer_data() and post the response - * to VIO via libsrp. - */ - ibmvscsis_cmnd_done(sc); - pr_debug("ibmvscsis: queue_data_in"); - return 0; -} + /* And re-open it again */ + rc = h_reg_crq(vdev->unit_address, queue->msg_token, + PAGE_SIZE); + if (rc == 2) + /* Adapter is good, but other end is not ready */ + pr_warn("ibmvscsis: Partner adapter not ready\n"); + else if (rc != 0) + pr_err("ibmvscsis: couldn't register crq--rc 0x%x\n", rc); -static int ibmvscsis_queue_status(struct se_cmd *se_cmd) -{ - struct ibmvscsis_cmnd *cmd = container_of(se_cmd, - struct ibmvscsis_cmnd, se_cmd); - struct scsi_cmnd *sc = &cmd->sc; - /* - * Copy any generated SENSE data into sc->sense_buffer and - * set the appropriate sc->result to be translated by - * ibmvscsis_cmnd_done() - */ - pr_debug("ibmvscsis: ibmvscsis_queue_status\n"); - if (se_cmd->sense_buffer && - ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) || - (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { - memcpy((void *)sc->sense_buffer, (void *)se_cmd->sense_buffer, - SCSI_SENSE_BUFFERSIZE); - sc->result = host_byte(DID_OK) | driver_byte(DRIVER_SENSE) | - SAM_STAT_CHECK_CONDITION; - } else - sc->result = host_byte(DID_OK) | se_cmd->scsi_status; - /* - * Finally post the response to VIO via libsrp. - */ - ibmvscsis_cmnd_done(sc); - return 0; + return rc; } -static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter, - struct iu_entry *iue) +static void crq_queue_destroy(struct ibmvscsis_adapter *adapter) { - int data_len; - struct srp_cmd *cmd = iue->sbuf->buf; - struct scsi_cmnd *sc; - struct page *pg; - struct ibmvscsis_cmnd *vsc; - struct se_cmd *se_cmd; - int ret; - - pr_debug("ibmvscsis: ibmvscsis_queuecommand\n"); - - data_len = srp_data_length(cmd, srp_cmd_direction(cmd)); - - vsc = kzalloc(sizeof(*vsc), GFP_KERNEL); - sc = &vsc->sc; - sc->sense_buffer = vsc->sense_buf; - sc->cmnd = cmd->cdb; - sc->SCp.ptr = (char *)iue; - se_cmd = &vsc->se_cmd; + struct vio_dev *vdev = adapter->dma_dev; + struct crq_queue *queue = &adapter->crq_queue; - switch (cmd->cdb[0]) { - case INQUIRY: - pr_debug("ibmvscsis: inquiry\n"); - sg_alloc_table(&sc->sdb.table, 1, GFP_KERNEL); - pg = alloc_page(GFP_KERNEL|__GFP_ZERO); - sc->sdb.length = ibmvscsis_inquiry(adapter, cmd, - page_address(pg)); - sg_set_page(sc->sdb.table.sgl, pg, sc->sdb.length, 0); - ret = ibmvscsis_cmnd_done(sc); - sg_free_table(&sc->sdb.table); - __free_page(pg); - kfree(vsc); - break; - case REPORT_LUNS: - pr_debug("ibmvscsis: report_luns\n"); - sg_alloc_table(&sc->sdb.table, 1, GFP_KERNEL); - pg = alloc_page(GFP_KERNEL|__GFP_ZERO); - sc->sdb.length = ibmvscsis_report_luns(adapter, cmd, - page_address(pg)); - sg_set_page(sc->sdb.table.sgl, pg, sc->sdb.length, 0); - ret = ibmvscsis_cmnd_done(sc); - sg_free_table(&sc->sdb.table); - __free_page(pg); - kfree(vsc); - break; - case MODE_SENSE: - pr_debug("ibmvscsis: mode_sense\n"); - sg_alloc_table(&sc->sdb.table, 1, GFP_KERNEL); - pg = alloc_page(GFP_KERNEL|__GFP_ZERO); - sc->sdb.length = ibmvscsis_mode_sense(adapter, - cmd, page_address(pg)); - sg_set_page(sc->sdb.table.sgl, pg, sc->sdb.length, 0); - ret = ibmvscsis_cmnd_done(sc); - sg_free_table(&sc->sdb.table); - __free_page(pg); - kfree(vsc); - break; - default: - pr_debug("ibmvscsis: tcm_queuecommand\n"); - ret = tcm_queuecommand(adapter, vsc, cmd); - break; - } + free_irq(vdev->irq, (void *)adapter); + flush_work(&adapter->crq_work); + h_free_crq(vdev->unit_address); + dma_unmap_single(&adapter->dma_dev->dev, queue->msg_token, + queue->size * sizeof(*queue->msgs), + DMA_BIDIRECTIONAL); - return ret; + free_page((unsigned long)queue->msgs); } -static void handle_cmd_queue(struct ibmvscsis_adapter *adapter) +static inline struct viosrp_crq *next_crq(struct crq_queue *queue) { - struct srp_target *target = &adapter->target; - struct iu_entry *iue; + struct viosrp_crq *crq; unsigned long flags; - int err; - pr_debug("ibmvscsis: entering handle_cmd_queue\n"); -retry: - spin_lock_irqsave(&target->lock, flags); - list_for_each_entry(iue, &target->cmd_queue, ilist) { - pr_debug("ibmvscsis: iueflag: %lx\n",iue->flags); - if (!test_and_set_bit(V_FLYING, &iue->flags)) { - spin_unlock_irqrestore(&target->lock, flags); - err = ibmvscsis_queuecommand(adapter, iue); - if (err) { - pr_err("ibmvscsis: cannot queue iue %p %d\n", - iue, err); - srp_iu_put(iue); - } - goto retry; - } - } + spin_lock_irqsave(&queue->lock, flags); + crq = &queue->msgs[queue->cur]; + if (crq->valid & 0x80) { + if (++queue->cur == queue->size) + queue->cur = 0; + rmb(); + } else + crq = NULL; + spin_unlock_irqrestore(&queue->lock, flags); - spin_unlock_irqrestore(&target->lock, flags); + return crq; } -static irqreturn_t ibmvscsis_interrupt(int dummy, void *data) +static int send_iu(struct iu_entry *iue, u64 length, u8 format) { - struct ibmvscsis_adapter *adapter = data; + struct srp_target *target = iue->target; + struct ibmvscsis_adapter *adapter = target->ldata; + struct ibmvscsis_crq_msg crq_msg; + __be64 *crq_as_u64 = (__be64*)&crq_msg; + long rc, rc1; - pr_debug("ibmvscsis: there is an interrupt\n"); - vio_disable_interrupts(adapter->dma_dev); - schedule_work(&adapter->crq_work); + pr_debug("ibmvscsis: send_iu: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + (unsigned long)length, + (unsigned long)adapter->liobn, + (unsigned long)iue->sbuf->dma, + (unsigned long)adapter->riobn, + (unsigned long)be64_to_cpu(iue->remote_token)); - return IRQ_HANDLED; -} + /* First copy the SRP */ + rc = h_copy_rdma(length, adapter->liobn, iue->sbuf->dma, + adapter->riobn, be64_to_cpu(iue->remote_token)); + + if (rc) + pr_err("ibmvscsis: Error %ld transferring data\n", rc); + + pr_debug("ibmvscsis: crq pre cooked: 0x%x, 0x%llx, 0x%llx\n", + format, length, vio_iu(iue)->srp.rsp.tag); + + crq_msg.valid = 0x80; + crq_msg.format = format; + crq_msg.rsvd = 0; + if (rc == 0) { + crq_msg.status = 0x99; + } else { + crq_msg.status = 0; + } + crq_msg.rsvd1 = 0; + crq_msg.IU_length = cpu_to_be16(length); + crq_msg.IU_data_ptr = vio_iu(iue)->srp.rsp.tag; -static int ibmvscsis_reset_crq_queue(struct ibmvscsis_adapter *adapter) -{ - int rc = 0; - struct vio_dev *vdev = adapter->dma_dev; - struct crq_queue *queue = &adapter->crq_queue; + pr_debug("ibmvscsis: send crq: 0x%x, 0x%llx, 0x%llx\n", + adapter->dma_dev->unit_address, + be64_to_cpu(crq_as_u64[0]), + be64_to_cpu(crq_as_u64[1])); - /* Close the CRQ */ - h_free_crq(vdev->unit_address); + srp_iu_put(iue); - /* Clean out the queue */ - memset(queue->msgs, 0x00, PAGE_SIZE); - queue->cur = 0; + rc1 = h_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), + be64_to_cpu(crq_as_u64[1])); - /* And re-open it again */ - rc = h_reg_crq(vdev->unit_address, queue->msg_token, - PAGE_SIZE); - if (rc == 2) - /* Adapter is good, but other end is not ready */ - pr_warn("ibmvscsis: Partner adapter not ready\n"); - else if (rc != 0) - pr_err("ibmvscsis: couldn't register crq--rc 0x%x\n", rc); + if (rc1) { + pr_err("ibmvscsis: %ld sending response\n", rc1); + return rc1; + } return rc; } - -static void crq_queue_destroy(struct ibmvscsis_adapter *adapter) +static int send_rsp(struct iu_entry *iue, struct scsi_cmnd *sc, + unsigned char status, unsigned char asc) { - struct vio_dev *vdev = adapter->dma_dev; - struct crq_queue *queue = &adapter->crq_queue; + struct srp_target *target = iue->target; + struct ibmvscsis_adapter *adapter = target->ldata; + union viosrp_iu *iu = vio_iu(iue); + uint64_t tag = iu->srp.rsp.tag; - free_irq(vdev->irq, (void *)adapter); - flush_work(&adapter->crq_work); - h_free_crq(vdev->unit_address); - dma_unmap_single(&adapter->dma_dev->dev, queue->msg_token, - queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + /* If the linked bit is on and status is good */ + if (test_bit(V_LINKED, &iue->flags) && (status == NO_SENSE)) + status = 0x10; - free_page((unsigned long)queue->msgs); -} + memset(iu, 0, sizeof(struct srp_rsp)); + iu->srp.rsp.opcode = SRP_RSP; +// iu->srp.rsp.req_lim_delta = 1; + iu->srp.rsp.req_lim_delta = cpu_to_be32(1 + + atomic_xchg(&adapter->req_lim_delta, 0)); + iu->srp.rsp.tag = tag; -/* Fill in the liobn and riobn fields on the adapter */ -static int read_dma_window(struct vio_dev *vdev, - struct ibmvscsis_adapter *adapter) -{ - const __be32 *dma_window; - const __be32 *prop; + if (test_bit(V_DIOVER, &iue->flags)) + iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER; - /* TODO Using of_parse_dma_window would be better, but it doesn't give - * a way to read multiple windows without already knowing the size of - * a window or the number of windows - */ - dma_window = - (const __be32 *)vio_get_attribute(vdev, "ibm,my-dma-window", - NULL); - if (!dma_window) { - pr_err("ibmvscsis: Couldn't find ibm,my-dma-window property\n"); - return -1; - } + iu->srp.rsp.data_in_res_cnt = 0; + iu->srp.rsp.data_out_res_cnt = 0; - adapter->liobn = be32_to_cpu(*dma_window); - dma_window++; + iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; - prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells", - NULL); - if (!prop) { - pr_warn("ibmvscsis: Couldn't find ibm, \ - #dma-address-cells property\n"); - dma_window++; - } else - dma_window += be32_to_cpu(*prop); + iu->srp.rsp.resp_data_len = 0; + iu->srp.rsp.status = status; + if (status) { + uint8_t *sense = iu->srp.rsp.data; - prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells", - NULL); - if (!prop) { - pr_warn("ibmvscsis: Couldn't find ibm,#dma-size-cells property\n"); - dma_window++; - } else - dma_window += be32_to_cpu(*prop); + if (sc) { + iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + iu->srp.rsp.sense_data_len = SCSI_SENSE_BUFFERSIZE; + memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE); + } else { + iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION; + iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + iu->srp.rsp.sense_data_len = SRP_RSP_SENSE_DATA_LEN; - /* dma_window should point to the second window now */ - adapter->riobn = be32_to_cpu(*dma_window); + /* Valid bit and 'current errors' */ + sense[0] = (0x1 << 7 | 0x70); + /* Sense key */ + sense[2] = status; + /* Additional sense length */ + sense[7] = 0xa; /* 10 bytes */ + /* Additional sense code */ + sense[12] = asc; + } + } + + send_iu(iue, sizeof(iu->srp.rsp) + SRP_RSP_SENSE_DATA_LEN, + VIOSRP_SRP_FORMAT); return 0; } -static int process_srp_iu(struct iu_entry *iue) +static int send_adapter_info(struct iu_entry *iue, + dma_addr_t remote_buffer, u16 length) { - union viosrp_iu *iu = vio_iu(iue); struct srp_target *target = iue->target; struct ibmvscsis_adapter *adapter = target->ldata; - u8 opcode = iu->srp.rsp.opcode; - unsigned long flags; - int err = 1; + dma_addr_t data_token; + struct mad_adapter_info_data *info; + int err; - spin_lock_irqsave(&target->lock, flags); - if (adapter->tport.releasing) { - spin_unlock_irqrestore(&target->lock, flags); - pr_err("ibmvscsis: process_srp_iu, no tpg, releasing:%x\n", - adapter->tport.releasing); - srp_iu_put(iue); - goto done; + info = dma_alloc_coherent(&adapter->dma_dev->dev, sizeof(*info), + &data_token, GFP_KERNEL); + if (!info) { + pr_err("ibmvscsis: bad dma_alloc_coherent %p\n", target); + return 1; } - spin_unlock_irqrestore(&target->lock, flags); - switch (opcode) { - case SRP_LOGIN_REQ: - pr_debug("ibmvscsis: srploginreq"); - process_login(iue); - break; - case SRP_TSK_MGMT: - pr_debug("ibmvscsis: srp task mgmt"); - process_tsk_mgmt(iue); - break; - case SRP_CMD: - pr_debug("ibmvscsis: srpcmd"); - pr_debug("ibmvscsis: process_srp_iu, iu_entry: %llx\n", - (u64)iue->sbuf->buf); - spin_lock_irqsave(&target->lock, flags); - list_add_tail(&iue->ilist, &target->cmd_queue); - spin_unlock_irqrestore(&target->lock, flags); - err = 0; - break; - case SRP_LOGIN_RSP: - case SRP_I_LOGOUT: - case SRP_T_LOGOUT: - case SRP_RSP: - case SRP_CRED_REQ: - case SRP_CRED_RSP: - case SRP_AER_REQ: - case SRP_AER_RSP: - pr_err("ibmvscsis: Unsupported type %u\n", opcode); - break; - default: - pr_err("ibmvscsis: Unknown type %u\n", opcode); - } -done: - return err; -} + pr_debug("ibmvscsis: get_remote_info: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + (unsigned long)sizeof(*info), + (unsigned long)adapter->liobn, + (unsigned long)data_token, + (unsigned long)adapter->riobn, + (unsigned long)remote_buffer); -static void process_iu(struct viosrp_crq *crq, - struct ibmvscsis_adapter *adapter) -{ - struct iu_entry *iue; - long err; + /* Get remote info */ + err = h_copy_rdma(sizeof(*info), adapter->riobn, + be64_to_cpu(remote_buffer), + adapter->liobn, data_token); - iue = srp_iu_get(&adapter->target); - if (!iue) { - pr_err("Error getting IU from pool %p\n", iue); - return; + if (err == H_SUCCESS) { + pr_err("ibmvscsis: Client connect: %s (%d)\n", + info->partition_name, info->partition_number); } - iue->remote_token = crq->IU_data_ptr; + memset(info, 0, sizeof(*info)); - err = h_copy_rdma(be16_to_cpu(crq->IU_length), adapter->riobn, - be64_to_cpu(crq->IU_data_ptr), - adapter->liobn, iue->sbuf->dma); + strcpy(info->srp_version, "16.a"); + strncpy(info->partition_name, partition_name, + sizeof(info->partition_name)); + pr_debug("ibmvscsis: partition_number: %x\n", partition_number); + + info->partition_number = cpu_to_be32(partition_number); + info->mad_version = cpu_to_be32(1); + info->os_type = cpu_to_be32(2); + info->port_max_txu[0] = cpu_to_be32(SCSI_MAX_SG_SEGMENTS * PAGE_SIZE); + + pr_debug("ibmvscsis: send info to remote: 0x%lx 0x%lx 0x%lx \ + 0x%lx 0x%lx\n",(unsigned long)sizeof(*info), + (unsigned long)adapter->liobn, + (unsigned long)data_token, + (unsigned long)adapter->riobn, + (unsigned long)remote_buffer); + + /* Send our info to remote */ + err = h_copy_rdma(sizeof(*info), adapter->liobn, data_token, + adapter->riobn, be64_to_cpu(remote_buffer)); + dma_free_coherent(&adapter->dma_dev->dev, sizeof(*info), info, + data_token); if (err != H_SUCCESS) { - pr_err("ibmvscsis: %ld transferring data error %p\n", err, iue); - srp_iu_put(iue); + pr_err("ibmvscsis: Error sending adapter info %d\n", err); + return 1; } - if (crq->format == VIOSRP_MAD_FORMAT) { - process_mad_iu(iue); - } - else { - pr_debug("ibmvscsis: process srpiu"); - process_srp_iu(iue); - } + return 0; } -static void process_crq(struct viosrp_crq *crq, - struct ibmvscsis_adapter *adapter) +static int process_mad_iu(struct iu_entry *iue) { - switch (crq->valid) { - case 0xC0: - /* initialization */ - switch (crq->format) { - case 0x01: - h_send_crq(adapter, 0xC002000000000000, 0); - break; - case 0x02: - break; - default: - pr_err("ibmvscsis: Unknown format %u\n", crq->format); - } + union viosrp_iu *iu = vio_iu(iue); + struct viosrp_adapter_info *info; + struct viosrp_host_config *conf; + + switch (be32_to_cpu(iu->mad.empty_iu.common.type)) { + case VIOSRP_EMPTY_IU_TYPE: + pr_err("ibmvscsis: %s\n", "Unsupported EMPTY MAD IU"); break; - case 0xFF: - /* transport event */ + case VIOSRP_ERROR_LOG_TYPE: + pr_err("ibmvscsis: %s\n", "Unsupported ERROR LOG MAD IU"); + iu->mad.error_log.common.status = 1; + send_iu(iue, sizeof(iu->mad.error_log), VIOSRP_MAD_FORMAT); break; - case 0x80: - /* real payload */ - switch (crq->format) { - case VIOSRP_SRP_FORMAT: - case VIOSRP_MAD_FORMAT: - pr_debug("ibmvscsis: case viosrp mad crq: 0x%x, 0x%x," - " 0x%x, 0x%x, 0x%x, 0x%x, 0x%llx\n", - crq->valid, crq->format, crq->reserved, - crq->status, be16_to_cpu(crq->timeout), - be16_to_cpu(crq->IU_length), - be64_to_cpu(crq->IU_data_ptr)); - process_iu(crq, adapter); - break; - case VIOSRP_OS400_FORMAT: - case VIOSRP_AIX_FORMAT: - case VIOSRP_LINUX_FORMAT: - case VIOSRP_INLINE_FORMAT: - pr_err("ibmvscsis: Unsupported format %u\n", - crq->format); - break; - default: - pr_err("ibmvscsis: Unknown format %u\n", - crq->format); - } + case VIOSRP_ADAPTER_INFO_TYPE: + info = &iu->mad.adapter_info; + info->common.status = send_adapter_info(iue, info->buffer, + info->common.length); + send_iu(iue, sizeof(*info), VIOSRP_MAD_FORMAT); + break; + case VIOSRP_HOST_CONFIG_TYPE: + conf = &iu->mad.host_config; + conf->common.status = 1; + send_iu(iue, sizeof(*conf), VIOSRP_MAD_FORMAT); break; default: - pr_err("ibmvscsis: unknown message type 0x%02x!?\n", - crq->valid); + pr_err("ibmvscsis: Unknown type %u\n", iu->srp.rsp.opcode); + iu->mad.empty_iu.common.status = + cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); + send_iu(iue, sizeof(iu->mad), VIOSRP_MAD_FORMAT); + break; } + + return 1; } -static void handle_crq(struct work_struct *work) +static void process_login(struct iu_entry *iue) { - struct ibmvscsis_adapter *adapter = - container_of(work, struct ibmvscsis_adapter, crq_work); - struct viosrp_crq *crq; - int done = 0; + union viosrp_iu *iu = vio_iu(iue); + struct srp_login_rsp *rsp = &iu->srp.login_rsp; + struct srp_login_rej *rej = &iu->srp.login_rej; + u64 tag = iu->srp.rsp.tag; + struct srp_target *target = iue->target; + struct ibmvscsis_adapter *adapter = target->ldata; + struct vio_dev *vdev = adapter->dma_dev; + char name[16]; + struct se_portal_group *se_tpg; - while (!done) { - while ((crq = next_crq(&adapter->crq_queue)) != NULL) { - process_crq(crq, adapter); - crq->valid = 0x00; - } + /* + * TODO handle case that requested size is wrong and buffer + * format is wrong + */ + memset(iu, 0, max(sizeof(*rsp), sizeof(*rej))); - vio_enable_interrupts(adapter->dma_dev); + snprintf(name, sizeof(name), "%x", vdev->unit_address); - crq = next_crq(&adapter->crq_queue); - if (crq) { - vio_disable_interrupts(adapter->dma_dev); - process_crq(crq, adapter); - crq->valid = 0x00; - } else - done = 1; + if(!&adapter->tport.enabled) { + rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); + pr_err("ibmvscsis: Rejected SRP_LOGIN_REQ because target %s" + " has not yet been enabled", name); + goto reject; } - handle_cmd_queue(adapter); -} -static int crq_queue_create(struct crq_queue *queue, - struct ibmvscsis_adapter *adapter) -{ - int retrc; - int err; - struct vio_dev *vdev = adapter->dma_dev; + se_tpg = ibmvscsis_make_nexus(&adapter->tport, + &adapter->tport.tport_name[0]); + if(!se_tpg) { + pr_debug("ibmvscsis: login make nexus fail se_tpg(%p)\n", + se_tpg); + goto reject; + } - queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); + rsp->opcode = SRP_LOGIN_RSP; - if (!queue->msgs) - goto malloc_failed; + rsp->req_lim_delta = cpu_to_be32(INITIAL_SRP_LIMIT); - queue->size = PAGE_SIZE / sizeof(*queue->msgs); + pr_debug("ibmvscsis: process_login, tag:%llu\n", tag); - queue->msg_token = dma_map_single(&vdev->dev, queue->msgs, - queue->size * sizeof(*queue->msgs), - DMA_BIDIRECTIONAL); + rsp->tag = tag; + rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); + rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); + /* direct and indirect */ + rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT + | SRP_BUF_FORMAT_INDIRECT); - if (dma_mapping_error(&vdev->dev, queue->msg_token)) { - goto map_failed; - } + send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT); + return; - retrc = err = h_reg_crq(vdev->unit_address, queue->msg_token, - PAGE_SIZE); +reject: + rej->opcode = SRP_LOGIN_REJ; + rej->tag = tag; + rej->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT + | SRP_BUF_FORMAT_INDIRECT); - /* If the adapter was left active for some reason (like kexec) - * try freeing and re-registering - */ - if (err == H_RESOURCE) { - err = ibmvscsis_reset_crq_queue(adapter); - } - if( err == 2 ) { - pr_warn("ibmvscsis: Partner adapter not ready\n"); - retrc = 0; - } else if ( err != 0 ) { - pr_err("ibmvscsis: Error 0x%x opening virtual adapter\n", err); - goto reg_crq_failed; - } + send_iu(iue, sizeof(*rej), VIOSRP_SRP_FORMAT); +} - queue->cur = 0; - spin_lock_init(&queue->lock); +static void process_tsk_mgmt(struct iu_entry *iue) +{ + union viosrp_iu *iu = vio_iu(iue); + uint64_t tag = iu->srp.rsp.tag; + uint8_t *resp_data = iu->srp.rsp.data; + struct srp_target *target = iue->target; + struct ibmvscsis_adapter *adapter = target->ldata; - INIT_WORK(&adapter->crq_work, handle_crq); + memset(iu, 0, sizeof(struct srp_rsp)); + iu->srp.rsp.opcode = SRP_RSP; + iu->srp.rsp.req_lim_delta = cpu_to_be32(1 + + atomic_xchg(&adapter->req_lim_delta, 0)); +// iu->srp.rsp.req_lim_delta = 1; + iu->srp.rsp.tag = tag; - err = request_irq(vdev->irq, &ibmvscsis_interrupt, - 0, "ibmvscsis", adapter); - if (err) { - pr_err("ibmvscsis: Error 0x%x h_send_crq\n", err); - goto req_irq_failed; - } + iu->srp.rsp.data_in_res_cnt = 0; + iu->srp.rsp.data_out_res_cnt = 0; - err = vio_enable_interrupts(vdev); - if (err != 0 ) { - pr_err("ibmvscsis: Error %d enabling interrupts!!!\n", err); - goto req_irq_failed; - } + iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; - return retrc; + iu->srp.rsp.resp_data_len = 4; + /* TASK MANAGEMENT FUNCTION NOT SUPPORTED for now */ + resp_data[3] = 4; -req_irq_failed: - h_free_crq(vdev->unit_address); -reg_crq_failed: - dma_unmap_single(&vdev->dev, queue->msg_token, - queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); -map_failed: - free_page((unsigned long) queue->msgs); -malloc_failed: - return -1; + send_iu(iue, sizeof(iu->srp.rsp) + iu->srp.rsp.resp_data_len, + VIOSRP_SRP_FORMAT); } -/** - * ibmvscsis_probe - ibm vscsis target initialize entry point - * @param dev vio device struct - * @param id vio device id struct - * @return 0 - Success - * Non-zero - Failure - */ -static int ibmvscsis_probe(struct vio_dev *vdev, const struct vio_device_id *id) +static int ibmvscsis_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg, + struct srp_direct_buf *md, int nmd, + enum dma_data_direction dir, unsigned int rest) { - int ret = 0; - struct ibmvscsis_adapter *adapter; - unsigned long flags; - - pr_debug("ibmvscsis: Probe for UA 0x%x\n", vdev->unit_address); + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; + struct srp_target *target = iue->target; + struct ibmvscsis_adapter *adapter = target->ldata; + dma_addr_t token; + long err; + unsigned int done = 0; + int i, sidx, soff; - adapter = kzalloc(sizeof(struct ibmvscsis_adapter), GFP_KERNEL); - if (!adapter) - return -ENOMEM; + sidx = soff = 0; + token = sg_dma_address(sg + sidx); - adapter->dma_dev = vdev; + for (i = 0; i < nmd && rest; i++) { + unsigned int mdone, mlen; - snprintf(&adapter->tport.tport_name[0], 256, "%s", dev_name(&vdev->dev)); - pr_debug("ibmvscsis: probe tport_names:%s\n", - &adapter->tport.tport_name[0]); + mlen = min(rest, be32_to_cpu(md[i].len)); + for (mdone = 0; mlen;) { + int slen = min(sg_dma_len(sg + sidx) - soff, mlen); - ret = read_dma_window(adapter->dma_dev, adapter); - if(ret != 0) { - pr_debug("ibmvscsis: probe failed read dma window\n"); - } - pr_debug("ibmvscsis: Probe: liobn 0x%x, riobn 0x%x\n", adapter->liobn, - adapter->riobn); + if (dir == DMA_TO_DEVICE) + err = h_copy_rdma(slen, + adapter->riobn, + be64_to_cpu(md[i].va) + mdone, + adapter->liobn, + token + soff); + else + err = h_copy_rdma(slen, + adapter->liobn, + token + soff, + adapter->riobn, + be64_to_cpu(md[i].va)+mdone); - spin_lock_irqsave(&ibmvscsis_dev_lock, flags); - list_add_tail(&adapter->list, &ibmvscsis_dev_list); - spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags); + if (err != H_SUCCESS) { + pr_err("ibmvscsis: rdma error %d %d %ld\n", + dir, slen, err); + return -EIO; + } - ret = srp_target_alloc(&adapter->target, &vdev->dev, - INITIAL_SRP_LIMIT, - SRP_MAX_IU_LEN); + mlen -= slen; + mdone += slen; + soff += slen; + done += slen; - adapter->target.ldata = adapter; + if (soff == sg_dma_len(sg + sidx)) { + sidx++; + soff = 0; + token = sg_dma_address(sg + sidx); - if(ret) { - pr_debug("ibmvscsis: failed target alloc ret: %d\n", ret); + if (sidx > nsg) { + pr_err("ibmvscsis: out of sg %p %d %d\n", + iue, sidx, nsg); + return -EIO; + } + } + } + rest -= mlen; } + return 0; +} - ret = crq_queue_create(&adapter->crq_queue, adapter); - pr_debug("ibmvscsis: failed crq_queue_create ret: %d\n", ret); - if(ret != 0 && ret != H_RESOURCE) { - pr_debug("ibmvscsis: failed crq_queue_create ret: %d\n", ret); - ret = -1; - } +static int ibmvscsis_cmnd_done(struct scsi_cmnd *sc) +{ + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; + int err = 0; - if(h_send_crq(adapter, 0xC001000000000000LL, 0) != 0 - && ret != H_RESOURCE) { - pr_warn("ibmvscsis: Failed to send CRQ message\n"); - ret = 0; - } + if (scsi_sg_count(sc)) + err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, + ibmvscsis_rdma, 1, 1); - dev_set_drvdata(&vdev->dev, adapter); + if (err || sc->result != SAM_STAT_GOOD) { + pr_err("ibmvscsis: operation failed %p %d %x\n", + iue, sc->result, vio_iu(iue)->srp.cmd.cdb[0]); + send_rsp(iue, sc, HARDWARE_ERROR, 0x00); + } else + send_rsp(iue, sc, NO_SENSE, 0x00); + + srp_iu_put(iue); return 0; } -static int ibmvscsis_remove(struct vio_dev *dev) +static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter, + struct iu_entry *iue) { - unsigned long flags; - struct ibmvscsis_adapter *adapter = dev_get_drvdata(&dev->dev); - - spin_lock_irqsave(&ibmvscsis_dev_lock, flags); - list_del(&adapter->list); - spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags); + struct srp_cmd *cmd = iue->sbuf->buf; + struct scsi_cmnd *sc; + struct ibmvscsis_cmnd *vsc; + int ret; - crq_queue_destroy(adapter); + pr_debug("ibmvscsis: ibmvscsis_queuecommand\n"); - srp_target_free(&adapter->target); + vsc = kzalloc(sizeof(*vsc), GFP_KERNEL); + sc = &vsc->sc; + sc->sense_buffer = vsc->se_cmd.sense_buffer; + sc->cmnd = cmd->cdb; + sc->SCp.ptr = (char *)iue; - kfree(adapter); + pr_debug("ibmvscsis: tcm_queuecommand\n"); + ret = tcm_queuecommand(adapter, vsc, cmd); - return 0; + return ret; } -static struct vio_device_id ibmvscsis_device_table[] = { - {"v-scsi-host", "IBM,v-scsi-host"}, - {"", ""} -}; +static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len) +{ + uint64_t res = NO_SUCH_LUN; + int addressing_method; -MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table); + if (unlikely(len < 2)) { + pr_err("Illegal LUN length %d, expected 2 bytes or more\n", + len); + goto out; + } -static struct vio_driver ibmvscsis_driver = { - .name = ibmvscsis_driver_name, - .id_table = ibmvscsis_device_table, - .probe = ibmvscsis_probe, - .remove = ibmvscsis_remove, -}; + switch (len) { + case 8: + if ((*((__be64 *)lun) & + cpu_to_be64(0x0000FFFFFFFFFFFFLL)) != 0) + goto out_err; + break; + case 4: + if (*((__be16 *)&lun[2]) != 0) + goto out_err; + break; + case 6: + if (*((__be32 *)&lun[2]) != 0) + goto out_err; + break; + case 2: + break; + default: + goto out_err; + } -static int get_system_info(void) + addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */ + switch (addressing_method) { + case SCSI_LUN_ADDR_METHOD_PERIPHERAL: + case SCSI_LUN_ADDR_METHOD_FLAT: + case SCSI_LUN_ADDR_METHOD_LUN: + res = *(lun + 1) | (((*lun) & 0x3f) << 8); + break; + + case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN: + default: + pr_err("Unimplemented LUN addressing method %u\n", + addressing_method); + break; + } + +out: + return res; + +out_err: + pr_err("Support for multi-level LUNs has not yet been implemented\n"); + goto out; +} + +static int tcm_queuecommand(struct ibmvscsis_adapter *adapter, + struct ibmvscsis_cmnd *vsc, + struct srp_cmd *scmd) { - struct device_node *rootdn, *vdevdn; - const char *id, *model, *name; - const unsigned int *num; + struct se_cmd *se_cmd; + int attr; + u64 data_len; + int ret; + uint64_t unpacked_lun; - pr_debug("ibmvscsis: getsysteminfo"); - rootdn = of_find_node_by_path("/"); - if (!rootdn) - return -ENOENT; + switch (scmd->task_attr) { + case SRP_SIMPLE_TASK: + attr = TCM_SIMPLE_TAG; + break; + case SRP_ORDERED_TASK: + attr = TCM_ORDERED_TAG; + break; + case SRP_HEAD_TASK: + attr = TCM_HEAD_TAG; + break; + case SRP_ACA_TASK: + attr = TCM_ACA_TAG; + break; + default: + pr_err("ibmvscsis: Task attribute %d not supported\n", + scmd->task_attr); + attr = TCM_SIMPLE_TAG; + } - model = of_get_property(rootdn, "model", NULL); - id = of_get_property(rootdn, "system-id", NULL); - if (model && id) - snprintf(system_id, sizeof(system_id), "%s-%s", model, id); + pr_debug("ibmvscsis: srp_data_length: %llx, srp_direction:%x\n", + srp_data_length(scmd, srp_cmd_direction(scmd)), + srp_cmd_direction(scmd)); + data_len = srp_data_length(scmd, srp_cmd_direction(scmd)); - name = of_get_property(rootdn, "ibm,partition-name", NULL); - if (name) - strncpy(partition_name, name, sizeof(partition_name)); + vsc->se_cmd.tag = scmd->tag; + se_cmd = &vsc->se_cmd; - num = of_get_property(rootdn, "ibm,partition-no", NULL); - if (num) - partition_number = of_read_number(num, 1); + pr_debug("ibmvscsis: size of lun:%lx, lun:%s\n", sizeof(scmd->lun), + &scmd->lun.scsi_lun[0]); - of_node_put(rootdn); + unpacked_lun = ibmvscsis_unpack_lun((uint8_t *)&scmd->lun, + sizeof(scmd->lun)); - vdevdn = of_find_node_by_path("/vdevice"); - vdevdn = of_find_node_by_path("/vdevice"); - if (vdevdn) { - const unsigned *mvds; + pr_debug("ibmvscsis: tcm_queuecommand- se_cmd(%p), se_sess(%p)," + " cdb: %x, sense: %x, unpacked_lun: %llx," + " data_length: %llx, task_attr: %x, data_dir: %x" + " flags: %x, tag:%llx, packed_lun:%llx\n", + se_cmd, adapter->tport.se_sess, + scmd->cdb[0], vsc->sense_buf[0], unpacked_lun, + data_len, attr, srp_cmd_direction(scmd), + TARGET_SCF_ACK_KREF, scmd->tag, + be64_to_cpu(&scmd->lun)); - mvds = of_get_property(vdevdn, "ibm,max-virtual-dma-size", - NULL); - if (mvds) - max_vdma_size = *mvds; - of_node_put(vdevdn); + ret = target_submit_cmd(se_cmd, adapter->tport.se_sess, + &scmd->cdb[0], &vsc->sense_buf[0], unpacked_lun, + data_len, attr, srp_cmd_direction(scmd), + 0); + if(ret != 0) { + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + pr_debug("ibmvscsis: tcm_queuecommand fail submit_cmd\n"); + goto send_sense; } - return 0; -}; - -static const struct target_core_fabric_ops ibmvscsis_ops = { - .module = THIS_MODULE, - .name = "ibmvscsis", - .max_data_sg_nents = SCSI_MAX_SG_SEGMENTS, - .get_fabric_name = ibmvscsis_get_fabric_name, - .tpg_get_wwn = ibmvscsis_get_fabric_wwn, - .tpg_get_tag = ibmvscsis_get_tag, - .tpg_get_default_depth = ibmvscsis_get_default_depth, - .tpg_check_demo_mode = ibmvscsis_check_true, - .tpg_check_demo_mode_cache = ibmvscsis_check_true, - .tpg_check_demo_mode_write_protect = ibmvscsis_check_false, - .tpg_check_prod_mode_write_protect = ibmvscsis_check_false, - .tpg_get_inst_index = ibmvscsis_tpg_get_inst_index, - .check_stop_free = ibmvscsis_check_stop_free, - .release_cmd = ibmvscsis_release_cmd, - .shutdown_session = ibmvscsis_shutdown_session, - .close_session = ibmvscsis_close_session, - .sess_get_index = ibmvscsis_sess_get_index, - .write_pending = ibmvscsis_write_pending, - .write_pending_status = ibmvscsis_write_pending_status, - .set_default_node_attributes = ibmvscsis_set_default_node_attrs, - .get_cmd_state = ibmvscsis_get_cmd_state, - .queue_data_in = ibmvscsis_queue_data_in, - .queue_status = ibmvscsis_queue_status, - .queue_tm_rsp = ibmvscsis_queue_tm_rsp, - .aborted_task = ibmvscsis_aborted_task, - /* - * Setup function pointers for logic in target_cor_fabric_configfs.c - */ - .fabric_make_wwn = ibmvscsis_make_tport, - .fabric_drop_wwn = ibmvscsis_drop_tport, - .fabric_make_tpg = ibmvscsis_make_tpg, - .fabric_drop_tpg = ibmvscsis_drop_tpg, - .tfc_wwn_attrs = ibmvscsis_wwn_attrs, - .tfc_tpg_base_attrs = ibmvscsis_tpg_attrs, -}; +send_sense: + transport_send_check_condition_and_sense(&vsc->se_cmd, ret, 0); + return -1; +} -/** +/* * ibmvscsis_init() - Kernel Module initialization * * Note: vio_register_driver() registers callback functions, and atleast one @@ -2017,15 +1893,20 @@ static int __init ibmvscsis_init(void) goto unregister_class; } + vtgtd = create_workqueue("ibmvscsis"); + if(!vtgtd) + goto unregister_target; + ret = vio_register_driver(&ibmvscsis_driver); if (ret) { pr_err("ibmvscsis: ret %d from vio_register_driver\n", ret); - goto unregister_target; + goto destroy_wq; } return 0; - +destroy_wq: + destroy_workqueue(vtgtd); unregister_target: target_unregister_template(&ibmvscsis_ops); unregister_class: @@ -2038,6 +1919,7 @@ static void __exit ibmvscsis_exit(void) { pr_info("ibmvscsis: Unregister IBM virtual SCSI driver\n"); vio_unregister_driver(&ibmvscsis_driver); + destroy_workqueue(vtgtd); target_unregister_template(&ibmvscsis_ops); class_unregister(&ibmvscsis_class); }; diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c index 0cd02b45cd3e23..3ec0157a9dceb6 100644 --- a/drivers/scsi/libsrp.c +++ b/drivers/scsi/libsrp.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ +#include #include #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -120,10 +120,8 @@ int srp_target_alloc(struct srp_target *target, struct device *dev, int err; spin_lock_init(&target->lock); - INIT_LIST_HEAD(&target->cmd_queue); target->dev = dev; - dev_set_drvdata(target->dev, target); target->srp_iu_size = iu_size; target->rx_ring_size = nr; @@ -166,7 +164,6 @@ struct iu_entry *srp_iu_get(struct srp_target *target) if (!iue) return iue; iue->target = target; - INIT_LIST_HEAD(&iue->ilist); iue->flags = 0; return iue; } @@ -193,8 +190,11 @@ static int srp_direct_data(struct scsi_cmnd *sc, struct srp_direct_buf *md, sg = scsi_sglist(sc); pr_debug("libsrp: iue: %p scsi_buff_len: %u srp_buff_len: %u" - " scsi_sg_count: %d\n", - iue, scsi_bufflen(sc), md->len, scsi_sg_count(sc)); + " scsi_sg_count: %d va:%llx, key:%x, len:%x\n", + iue, scsi_bufflen(sc), be32_to_cpu(md->len), + scsi_sg_count(sc), be64_to_cpu(md->va), + be32_to_cpu(md->key), + be32_to_cpu(md->len)); nsg = dma_map_sg(iue->target->dev, sg, scsi_sg_count(sc), DMA_BIDIRECTIONAL); @@ -203,9 +203,9 @@ static int srp_direct_data(struct scsi_cmnd *sc, struct srp_direct_buf *md, iue, scsi_sg_count(sc)); return 0; } - len = min(scsi_bufflen(sc), md->len); + len = min(scsi_bufflen(sc), be32_to_cpu(md->len)); } else - len = md->len; + len = be32_to_cpu(md->len); err = rdma_io(sc, sg, nsg, md, 1, dir, len); @@ -232,12 +232,16 @@ static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, sg = scsi_sglist(sc); pr_debug("libsrp: iue: %p scsi_buff_len: %u srp_ind_len: %u" - " in_desc_count: %d out_desc_count: %d\n", + " in_desc_count: %d out_desc_count: %d, va:%llx," + " key:%x, len:%x\n", iue, scsi_bufflen(sc), id->len, - cmd->data_in_desc_cnt, cmd->data_out_desc_cnt); + cmd->data_in_desc_cnt, cmd->data_out_desc_cnt, + be64_to_cpu(id->table_desc.va), + be32_to_cpu(id->table_desc.key), + be32_to_cpu(id->table_desc.len)); } - nmd = id->table_desc.len / sizeof(struct srp_direct_buf); + nmd = be32_to_cpu(id->table_desc.len) / sizeof(struct srp_direct_buf); if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) || (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) { @@ -246,19 +250,20 @@ static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, } if (ext_desc && dma_map) { - md = dma_alloc_coherent(iue->target->dev, id->table_desc.len, - &token, GFP_KERNEL); + md = dma_alloc_coherent(iue->target->dev, + be32_to_cpu(id->table_desc.len), + &token, GFP_KERNEL); if (!md) { pr_err("libsrp: Can't get dma memory %u\n", - id->table_desc.len); + be32_to_cpu(id->table_desc.len)); return -ENOMEM; } - sg_init_one(&dummy, md, id->table_desc.len); + sg_init_one(&dummy, md, be32_to_cpu(id->table_desc.len)); sg_dma_address(&dummy) = token; - sg_dma_len(&dummy) = id->table_desc.len; + sg_dma_len(&dummy) = be32_to_cpu(id->table_desc.len); err = rdma_io(sc, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE, - id->table_desc.len); + be32_to_cpu(id->table_desc.len)); if (err) { pr_err("libsrp: Error copying indirect table %d\n", err); @@ -279,9 +284,9 @@ static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, err = -EIO; goto free_mem; } - len = min(scsi_bufflen(sc), id->len); + len = min(scsi_bufflen(sc), be32_to_cpu(id->len)); } else - len = id->len; + len = be32_to_cpu(id->len); err = rdma_io(sc, sg, nsg, md, nmd, dir, len); @@ -290,8 +295,8 @@ static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, free_mem: if (token && dma_map) { - dma_free_coherent(iue->target->dev, id->table_desc.len, - md, token); + dma_free_coherent(iue->target->dev, + be32_to_cpu(id->table_desc.len), md, token); } return err; } @@ -401,5 +406,5 @@ u64 srp_data_length(struct srp_cmd *cmd, enum dma_data_direction dir) EXPORT_SYMBOL_GPL(srp_data_length); MODULE_DESCRIPTION("SCSI RDMA Protocol lib functions"); -MODULE_AUTHOR("Bryant G. Ly"); +MODULE_AUTHOR("FUJITA Tomonori"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c deleted file mode 100644 index 7eae07f28aed94..00000000000000 --- a/drivers/scsi/scsi_tgt_if.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * SCSI target kernel/user interface functions - * - * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2005 Mike Christie - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "scsi_tgt_priv.h" - -#if TGT_RING_SIZE < PAGE_SIZE -# define TGT_RING_SIZE PAGE_SIZE -#endif - -#define TGT_RING_PAGES (TGT_RING_SIZE >> PAGE_SHIFT) -#define TGT_EVENT_PER_PAGE (PAGE_SIZE / sizeof(struct tgt_event)) -#define TGT_MAX_EVENTS (TGT_EVENT_PER_PAGE * TGT_RING_PAGES) - -struct tgt_ring { - u32 tr_idx; - unsigned long tr_pages[TGT_RING_PAGES]; - spinlock_t tr_lock; -}; - -/* tx_ring : kernel->user, rx_ring : user->kernel */ -static struct tgt_ring tx_ring, rx_ring; -static DECLARE_WAIT_QUEUE_HEAD(tgt_poll_wait); - -static inline void tgt_ring_idx_inc(struct tgt_ring *ring) -{ - if (ring->tr_idx == TGT_MAX_EVENTS - 1) - ring->tr_idx = 0; - else - ring->tr_idx++; -} - -static struct tgt_event *tgt_head_event(struct tgt_ring *ring, u32 idx) -{ - u32 pidx, off; - - pidx = idx / TGT_EVENT_PER_PAGE; - off = idx % TGT_EVENT_PER_PAGE; - - return (struct tgt_event *) - (ring->tr_pages[pidx] + sizeof(struct tgt_event) * off); -} - -static int tgt_uspace_send_event(u32 type, struct tgt_event *p) -{ - struct tgt_event *ev; - struct tgt_ring *ring = &tx_ring; - unsigned long flags; - int err = 0; - - spin_lock_irqsave(&ring->tr_lock, flags); - - ev = tgt_head_event(ring, ring->tr_idx); - if (!ev->hdr.status) - tgt_ring_idx_inc(ring); - else - err = -BUSY; - - spin_unlock_irqrestore(&ring->tr_lock, flags); - - if (err) - return err; - - memcpy(ev, p, sizeof(*ev)); - ev->hdr.type = type; - /* Ensure the command buffer is flushed to memory before - * handing it over to the other side to prevent stale data - */ - mb(); - ev->hdr.status = 1; - - flush_dcache_page(virt_to_page(ev)); - - wake_up_interruptible(&tgt_poll_wait); - - return 0; -} - -int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, u64 itn_id, - struct scsi_lun *lun, u64 tag) -{ - struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.cmd_req.host_no = shost->host_no; - ev.p.cmd_req.itn_id = itn_id; - ev.p.cmd_req.data_len = scsi_bufflen(cmd); - memcpy(ev.p.cmd_req.scb, cmd->cmnd, sizeof(ev.p.cmd_req.scb)); - memcpy(ev.p.cmd_req.lun, lun, sizeof(ev.p.cmd_req.lun)); - ev.p.cmd_req.attribute = cmd->tag; - ev.p.cmd_req.tag = tag; - - pr_debug("%p %d %u %x %llx\n", cmd, shost->host_no, - ev.p.cmd_req.data_len, cmd->tag, - (unsigned long long) ev.p.cmd_req.tag); - - err = tgt_uspace_send_event(TGT_KEVENT_CMD_REQ, &ev); - if (err) - pr_err("tx buf is full, could not send\n"); - - return err; -} - -int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 itn_id, u64 tag) -{ - struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.cmd_done.host_no = shost->host_no; - ev.p.cmd_done.itn_id = itn_id; - ev.p.cmd_done.tag = tag; - ev.p.cmd_done.result = cmd->result; - - pr_debug("%p %d %llu %u %x\n", cmd, shost->host_no, - (unsigned long long) ev.p.cmd_req.tag, - ev.p.cmd_req.data_len, cmd->tag); - - err = tgt_uspace_send_event(TGT_KEVENT_CMD_DONE, &ev); - if (err) - pr_err("tx buf is full, could not send\n"); - - return err; -} - -int scsi_tgt_uspace_send_tsk_mgmt(int host_no, u64 itn_id, int function, - u64 tag, struct scsi_lun *scsilun, void *data) -{ - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.tsk_mgmt_req.host_no = host_no; - ev.p.tsk_mgmt_req.itn_id = itn_id; - ev.p.tsk_mgmt_req.function = function; - ev.p.tsk_mgmt_req.tag = tag; - memcpy(ev.p.tsk_mgmt_req.lun, scsilun, sizeof(ev.p.tsk_mgmt_req.lun)); - ev.p.tsk_mgmt_req.mid = (u64) (unsigned long) data; - - pr_debug("%d %x %llx %llx\n", host_no, function, - (unsigned long long)tag, - (unsigned long long)ev.p.tsk_mgmt_req.mid); - - err = tgt_uspace_send_event(TGT_KEVENT_TSK_MGMT_REQ, &ev); - if (err) - pr_err("tx buf is full, could not send\n"); - - return err; -} - -int scsi_tgt_uspace_send_it_nexus_request(int host_no, u64 itn_id, - int function, char *initiator_id) -{ - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.it_nexus_req.host_no = host_no; - ev.p.it_nexus_req.function = function; - ev.p.it_nexus_req.itn_id = itn_id; - if (initiator_id) - strncpy(ev.p.it_nexus_req.initiator_id, initiator_id, - sizeof(ev.p.it_nexus_req.initiator_id)); - - pr_debug("%d %x %llx\n", host_no, function, (unsigned long long)itn_id); - - err = tgt_uspace_send_event(TGT_KEVENT_IT_NEXUS_REQ, &ev); - if (err) - pr_err("tx buf is full, could not send\n"); - - return err; -} - -static int event_recv_msg(struct tgt_event *ev) -{ - int err = 0; - - switch (ev->hdr.type) { - case TGT_UEVENT_CMD_RSP: - err = scsi_tgt_kspace_exec(ev->p.cmd_rsp.host_no, - ev->p.cmd_rsp.itn_id, - ev->p.cmd_rsp.result, - ev->p.cmd_rsp.tag, - ev->p.cmd_rsp.uaddr, - ev->p.cmd_rsp.len, - ev->p.cmd_rsp.sense_uaddr, - ev->p.cmd_rsp.sense_len, - ev->p.cmd_rsp.rw); - break; - case TGT_UEVENT_TSK_MGMT_RSP: - err = scsi_tgt_kspace_tsk_mgmt(ev->p.tsk_mgmt_rsp.host_no, - ev->p.tsk_mgmt_rsp.itn_id, - ev->p.tsk_mgmt_rsp.mid, - ev->p.tsk_mgmt_rsp.result); - break; - case TGT_UEVENT_IT_NEXUS_RSP: - err = scsi_tgt_kspace_it_nexus_rsp(ev->p.it_nexus_rsp.host_no, - ev->p.it_nexus_rsp.itn_id, - ev->p.it_nexus_rsp.result); - break; - default: - pr_err("unknown type %d\n", ev->hdr.type); - err = -EINVAL; - } - - return err; -} - -static ssize_t tgt_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - struct tgt_event *ev; - struct tgt_ring *ring = &rx_ring; - - while (1) { - ev = tgt_head_event(ring, ring->tr_idx); - /* do we need this? */ - flush_dcache_page(virt_to_page(ev)); - - if (!ev->hdr.status) - break; - - tgt_ring_idx_inc(ring); - event_recv_msg(ev); - ev->hdr.status = 0; - }; - - return count; -} - -static unsigned int tgt_poll(struct file *file, struct poll_table_struct *wait) -{ - struct tgt_event *ev; - struct tgt_ring *ring = &tx_ring; - unsigned long flags; - unsigned int mask = 0; - u32 idx; - - poll_wait(file, &tgt_poll_wait, wait); - - spin_lock_irqsave(&ring->tr_lock, flags); - - idx = ring->tr_idx ? ring->tr_idx - 1 : TGT_MAX_EVENTS - 1; - ev = tgt_head_event(ring, idx); - if (ev->hdr.status) - mask |= POLLIN | POLLRDNORM; - - spin_unlock_irqrestore(&ring->tr_lock, flags); - - return mask; -} - -static int uspace_ring_map(struct vm_area_struct *vma, unsigned long addr, - struct tgt_ring *ring) -{ - int i, err; - struct page *page; - - for (i = 0; i < TGT_RING_PAGES; i++) { - page = virt_to_page(ring->tr_pages[i]); - err = vm_insert_page(vma, addr, page); - if (err) - return err; - addr += PAGE_SIZE; - } - - return 0; -} - -static int tgt_mmap(struct file *filp, struct vm_area_struct *vma) -{ - unsigned long addr; - int err; - - if (vma->vm_pgoff) - return -EINVAL; - - if (vma->vm_end - vma->vm_start != TGT_RING_SIZE * 2) { - pr_err("mmap size must be %lu, not %lu\n", - TGT_RING_SIZE * 2, vma->vm_end - vma->vm_start); - return -EINVAL; - } - - addr = vma->vm_start; - err = uspace_ring_map(vma, addr, &tx_ring); - if (err) - return err; - err = uspace_ring_map(vma, addr + TGT_RING_SIZE, &rx_ring); - - return err; -} - -static int tgt_open(struct inode *inode, struct file *file) -{ - tx_ring.tr_idx = rx_ring.tr_idx = 0; - - return 0; -} - -static const struct file_operations tgt_fops = { - .owner = THIS_MODULE, - .open = tgt_open, - .poll = tgt_poll, - .write = tgt_write, - .mmap = tgt_mmap, - .llseek = noop_llseek, -}; - -static struct miscdevice tgt_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "tgt", - .fops = &tgt_fops, -}; - -static void tgt_ring_exit(struct tgt_ring *ring) -{ - int i; - - for (i = 0; i < TGT_RING_PAGES; i++) - free_page(ring->tr_pages[i]); -} - -static int tgt_ring_init(struct tgt_ring *ring) -{ - int i; - - spin_lock_init(&ring->tr_lock); - - for (i = 0; i < TGT_RING_PAGES; i++) { - ring->tr_pages[i] = get_zeroed_page(GFP_KERNEL); - if (!ring->tr_pages[i]) { - pr_err("out of memory\n"); - return -ENOMEM; - } - } - - return 0; -} - -void scsi_tgt_if_exit(void) -{ - tgt_ring_exit(&tx_ring); - tgt_ring_exit(&rx_ring); - misc_deregister(&tgt_miscdev); -} - -int scsi_tgt_if_init(void) -{ - int err; - - err = tgt_ring_init(&tx_ring); - if (err) - return err; - - err = tgt_ring_init(&rx_ring); - if (err) - goto free_tx_ring; - - err = misc_register(&tgt_miscdev); - if (err) - goto free_rx_ring; - - return 0; -free_rx_ring: - tgt_ring_exit(&rx_ring); -free_tx_ring: - tgt_ring_exit(&tx_ring); - - return err; -} diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c deleted file mode 100644 index 50abc53e9ab14c..00000000000000 --- a/drivers/scsi/scsi_tgt_lib.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * SCSI target lib functions - * - * Copyright (C) 2005 Mike Christie - * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2016 Bryant G. Ly IBM Corp. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scsi_tgt_priv.h" - - -static struct workqueue_struct *scsi_tgtd; -static struct kmem_cache *scsi_tgt_cmd_cache; - -/* - * TODO: this struct will be killed when the block layer supports large bios - * and James's work struct code is in - */ -struct scsi_tgt_cmd { - /* TODO replace work with James b's code */ - struct work_struct work; - /* TODO fix limits of some drivers */ - struct bio *bio; - - struct list_head hash_list; - struct request *rq; - u64 itn_id; - u64 tag; -}; - -#define TGT_HASH_ORDER 4 -#define cmd_hashfn(tag) hash_long((unsigned long) (tag), TGT_HASH_ORDER) - -struct scsi_tgt_queuedata { - struct Scsi_Host *shost; - struct list_head cmd_hash[1 << TGT_HASH_ORDER]; - spinlock_t cmd_hash_lock; -}; - -/* - * Function: scsi_host_get_command() - * - * Purpose: Allocate and setup a scsi command block and blk request - * - * Arguments: shost - scsi host - * data_dir - dma data dir - * gfp_mask- allocator flags - * - * Returns: The allocated scsi command structure. - * - * This should be called by target LLDs to get a command. - */ -struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost, - enum dma_data_direction data_dir, - gfp_t gfp_mask) -{ - int write = (data_dir == DMA_TO_DEVICE); - struct request *rq; - struct scsi_cmnd *cmd; - struct scsi_tgt_cmd *tcmd; - struct scsi_device *sdev = scsi_get_host_dev(shost); - - /* Bail if we can't get a reference to the device */ - if (!get_device(&shost->shost_gendev)) - return NULL; - - tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC); - if (!tcmd) - goto put_dev; - - /* - * The blk helpers are used to the READ/WRITE requests - * transferring data from a initiator point of view. Since - * we are in target mode we want the opposite. - */ - rq = blk_get_request(shost->uspace_req_q, !write, gfp_mask); - if (!rq) - goto free_tcmd; - - cmd = scsi_get_command(sdev, gfp_mask); - if (!cmd) - goto release_rq; - - cmd->sc_data_direction = data_dir; - cmd->jiffies_at_alloc = jiffies; - cmd->request = rq; - - cmd->cmnd = rq->cmd; - - rq->special = cmd; - rq->cmd_type = REQ_TYPE_DRV_PRIV; - rq->cmd_flags |= REQ_TYPE_BLOCK_PC; - rq->end_io_data = tcmd; - - tcmd->rq = rq; - - return cmd; - -release_rq: - blk_put_request(rq); -free_tcmd: - kmem_cache_free(scsi_tgt_cmd_cache, tcmd); -put_dev: - put_device(&shost->shost_gendev); - return NULL; - -} -EXPORT_SYMBOL_GPL(scsi_host_get_command); - -/* - * Function: scsi_host_put_command() - * - * Purpose: Free a scsi command block - * - * Arguments: shost - scsi host - * cmd - command block to free - * - * Returns: Nothing. - * - * Notes: The command must not belong to any lists. - */ -void scsi_host_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) -{ - struct request_queue *q = shost->uspace_req_q; - struct request *rq = cmd->request; - struct scsi_tgt_cmd *tcmd = rq->end_io_data; - unsigned long flags; - - kmem_cache_free(scsi_tgt_cmd_cache, tcmd); - - spin_lock_irqsave(q->queue_lock, flags); - __blk_put_request(q, rq); - spin_unlock_irqrestore(q->queue_lock, flags); - - scsi_put_command(cmd); - put_device(&shost->shost_gendev); -} -EXPORT_SYMBOL_GPL(scsi_host_put_command); - -static void cmd_hashlist_del(struct scsi_cmnd *cmd) -{ - struct request_queue *q = cmd->request->q; - struct scsi_tgt_queuedata *qdata = q->queuedata; - unsigned long flags; - struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; - - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - list_del(&tcmd->hash_list); - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); -} - -static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd) -{ - blk_rq_unmap_user(tcmd->bio); -} - -static void scsi_tgt_cmd_destroy(struct work_struct *work) -{ - struct scsi_tgt_cmd *tcmd = - container_of(work, struct scsi_tgt_cmd, work); - struct scsi_cmnd *cmd = tcmd->rq->special; - - pr_debug("cmd %p %d %u\n", cmd, cmd->sc_data_direction, - rq_data_dir(cmd->request)); - scsi_unmap_user_pages(tcmd); - tcmd->rq->bio = NULL; - scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd); -} - -static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd, - u64 itn_id, u64 tag) -{ - struct scsi_tgt_queuedata *qdata = rq->q->queuedata; - unsigned long flags; - struct list_head *head; - - tcmd->itn_id = itn_id; - tcmd->tag = tag; - tcmd->bio = NULL; - INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy); - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - head = &qdata->cmd_hash[cmd_hashfn(tag)]; - list_add(&tcmd->hash_list, head); - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); -} - -/* - * scsi_tgt_alloc_queue - setup queue used for message passing - * shost: scsi host - * - * This should be called by the LLD after host allocation. - * And will be released when the host is released. - */ -int scsi_tgt_alloc_queue(struct Scsi_Host *shost) -{ - struct scsi_tgt_queuedata *queuedata; - struct request_queue *q; - int err, i; - - /* - * Do we need to send a netlink event or should uspace - * just respond to the hotplug event? - */ - q = __scsi_alloc_queue(shost, NULL); - if (!q) - return -ENOMEM; - - queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL); - if (!queuedata) { - err = -ENOMEM; - goto cleanup_queue; - } - queuedata->shost = shost; - q->queuedata = queuedata; - - /* - * this is a silly hack. We should probably just queue as many - * command as is recvd to userspace. uspace can then make - * sure we do not overload the HBA - */ - q->nr_requests = shost->can_queue; - /* - * We currently only support software LLDs so this does - * not matter for now. Do we need this for the cards we support? - * If so we should make it a host template value. - */ - blk_queue_dma_alignment(q, 0); - shost->uspace_req_q = q; - - for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++) - INIT_LIST_HEAD(&queuedata->cmd_hash[i]); - spin_lock_init(&queuedata->cmd_hash_lock); - - return 0; - -cleanup_queue: - blk_cleanup_queue(q); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue); - -void scsi_tgt_free_queue(struct Scsi_Host *shost) -{ - int i; - unsigned long flags; - struct request_queue *q = shost->uspace_req_q; - struct scsi_cmnd *cmd; - struct scsi_tgt_queuedata *qdata = q->queuedata; - struct scsi_tgt_cmd *tcmd, *n; - LIST_HEAD(cmds); - - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - - for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) { - list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i], - hash_list) - list_move(&tcmd->hash_list, &cmds); - } - - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); - - while (!list_empty(&cmds)) { - tcmd = list_entry(cmds.next, struct scsi_tgt_cmd, hash_list); - list_del(&tcmd->hash_list); - cmd = tcmd->rq->special; - - shost->hostt->eh_abort_handler(cmd); - scsi_tgt_cmd_destroy(&tcmd->work); - } -} -EXPORT_SYMBOL_GPL(scsi_tgt_free_queue); - -struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd) -{ - struct scsi_tgt_queuedata *queue = cmd->request->q->queuedata; - - return queue->shost; -} -EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host); - -/* - * scsi_tgt_queue_command - queue command for userspace processing - * @cmd: scsi command - * @scsilun: scsi lun - * @tag: unique value to identify this command for tmf - */ -int scsi_tgt_queue_command(struct scsi_cmnd *cmd, u64 itn_id, - struct scsi_lun *scsilun, u64 tag) -{ - struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; - int err; - - init_scsi_tgt_cmd(cmd->request, tcmd, itn_id, tag); - err = scsi_tgt_uspace_send_cmd(cmd, itn_id, scsilun, tag); - if (err) - cmd_hashlist_del(cmd); - - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_queue_command); - -/* - * This is run from a interrupt handler normally and the unmap - * needs process context so we must queue - */ -static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd) -{ - struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; - - pr_debug("cmd %p %u\n", cmd, rq_data_dir(cmd->request)); - - scsi_tgt_uspace_send_status(cmd, tcmd->itn_id, tcmd->tag); - - scsi_release_buffers(cmd); - - queue_work(scsi_tgtd, &tcmd->work); -} - -static int scsi_tgt_transfer_response(struct scsi_cmnd *cmd) -{ - struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); - int err; - - pr_debug("cmd %p %u\n", cmd, rq_data_dir(cmd->request)); - - err = shost->hostt->transfer_response(cmd, scsi_tgt_cmd_done); - switch (err) { - case SCSI_MLQUEUE_HOST_BUSY: - case SCSI_MLQUEUE_DEVICE_BUSY: - return -EAGAIN; - } - return 0; -} - -/* TODO: test this crap and replace bio_map_user with new interface maybe */ -static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, - unsigned long uaddr, unsigned int len, int rw) -{ - struct request_queue *q = cmd->request->q; - struct request *rq = cmd->request; - int err; - - pr_debug("%lx %u\n", uaddr, len); - err = blk_rq_map_user(q, rq, NULL, (void *)uaddr, len, GFP_KERNEL); - if (err) { - /* - * TODO: need to fixup sg_tablesize, max_segment_size, - * max_sectors, etc for modern HW and software drivers - * where this value is bogus. - * - * TODO2: we can alloc a reserve buffer of max size - * we can handle and do the slow copy path for really large - * IO. - */ - pr_err("Could not handle request of size %u.\n", len); - return err; - } - - tcmd->bio = rq->bio; - err = scsi_init_io(cmd); - if (err) { - scsi_release_buffers(cmd); - goto unmap_rq; - } - /* - * we use REQ_TYPE_BLOCK_PC so scsi_init_io doesn't set the - * length for us. - */ - cmd->sdb.length = blk_rq_bytes(rq); - - return 0; - -unmap_rq: - scsi_unmap_user_pages(tcmd); - return err; -} - -static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr, - unsigned len) -{ - char __user *p = (char __user *) uaddr; - - if (copy_from_user(cmd->sense_buffer, p, - min_t(unsigned, SCSI_SENSE_BUFFERSIZE, len))) { - pr_err("Could not copy the sense buffer\n"); - return -EIO; - } - return 0; -} - -static int scsi_tgt_abort_cmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd) -{ - struct scsi_tgt_cmd *tcmd; - int err; - - err = shost->hostt->eh_abort_handler(cmd); - if (err) - pr_err("fail to abort %p\n", cmd); - - tcmd = cmd->request->end_io_data; - scsi_tgt_cmd_destroy(&tcmd->work); - return err; -} - -static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u64 tag) -{ - struct scsi_tgt_queuedata *qdata = q->queuedata; - struct request *rq = NULL; - struct list_head *head; - struct scsi_tgt_cmd *tcmd; - unsigned long flags; - - head = &qdata->cmd_hash[cmd_hashfn(tag)]; - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - list_for_each_entry(tcmd, head, hash_list) { - if (tcmd->tag == tag) { - rq = tcmd->rq; - list_del(&tcmd->hash_list); - break; - } - } - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); - - return rq; -} - -int scsi_tgt_kspace_exec(int host_no, u64 itn_id, int result, u64 tag, - unsigned long uaddr, u32 len, - unsigned long sense_uaddr, u32 sense_len, u8 rw) -{ - struct Scsi_Host *shost; - struct scsi_cmnd *cmd; - struct request *rq; - struct scsi_tgt_cmd *tcmd; - int err = 0; - - pr_debug("%d %llu %d %u %lx %u\n", host_no, (unsigned long long) tag, - result, len, uaddr, rw); - - /* TODO: replace with a O(1) alg */ - shost = scsi_host_lookup(host_no); - if (!shost) { - pr_err("Could not find host no %d\n", host_no); - return -EINVAL; - } - - if (!shost->uspace_req_q) { - pr_err("Not target scsi host %d\n", host_no); - goto done; - } - - rq = tgt_cmd_hash_lookup(shost->uspace_req_q, tag); - if (!rq) { - pr_err("Could not find tag %llu\n", - (unsigned long long) tag); - err = -EINVAL; - goto done; - } - cmd = rq->special; - - pr_debug("cmd %p scb %x result %d len %d bufflen %u %u %x\n", - cmd, cmd->cmnd[0], result, len, scsi_bufflen(cmd), - rq_data_dir(rq), cmd->cmnd[0]); - - if (result == TASK_ABORTED) { - scsi_tgt_abort_cmd(shost, cmd); - goto done; - } - /* - * store the userspace values here, the working values are - * in the request_* values - */ - tcmd = cmd->request->end_io_data; - cmd->result = result; - - if (cmd->result == SAM_STAT_CHECK_CONDITION) - scsi_tgt_copy_sense(cmd, sense_uaddr, sense_len); - - if (len) { - err = scsi_map_user_pages(rq->end_io_data, cmd, uaddr, len, rw); - if (err) { - /* - * user-space daemon bugs or OOM - * TODO: we can do better for OOM. - */ - struct scsi_tgt_queuedata *qdata; - struct list_head *head; - unsigned long flags; - - pr_err("cmd %p ret %d uaddr %lx len %d rw %d\n", - cmd, err, uaddr, len, rw); - - qdata = shost->uspace_req_q->queuedata; - head = &qdata->cmd_hash[cmd_hashfn(tcmd->tag)]; - - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - list_add(&tcmd->hash_list, head); - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); - - goto done; - } - } - err = scsi_tgt_transfer_response(cmd); -done: - scsi_host_put(shost); - return err; -} - -int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, u64 itn_id, - int function, u64 tag, struct scsi_lun *scsilun, - void *data) -{ - int err; - - /* TODO: need to retry if this fails. */ - err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, itn_id, - function, tag, scsilun, data); - if (err < 0) - pr_err("The task management request lost!\n"); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request); - -int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 itn_id, u64 mid, int result) -{ - struct Scsi_Host *shost; - int err = -EINVAL; - - pr_debug("%d %d %llx\n", host_no, result, (unsigned long long) mid); - - shost = scsi_host_lookup(host_no); - if (!shost) { - pr_err("Could not find host no %d\n", host_no); - return err; - } - - if (!shost->uspace_req_q) { - pr_err("Not target scsi host %d\n", host_no); - goto done; - } - - err = shost->transportt->tsk_mgmt_response(shost, itn_id, mid, result); -done: - scsi_host_put(shost); - return err; -} - -int scsi_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - int err; - - /* TODO: need to retry if this fails. */ - err = scsi_tgt_uspace_send_it_nexus_request(shost->host_no, itn_id, 0, - initiator); - if (err < 0) - pr_err("The i_t_neuxs request lost, %d %llx!\n", - shost->host_no, (unsigned long long)itn_id); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_it_nexus_create); - -int scsi_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - int err; - - /* TODO: need to retry if this fails. */ - err = scsi_tgt_uspace_send_it_nexus_request(shost->host_no, - itn_id, 1, NULL); - if (err < 0) - pr_err("The i_t_neuxs request lost, %d %llx!\n", - shost->host_no, (unsigned long long)itn_id); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_it_nexus_destroy); - -int scsi_tgt_kspace_it_nexus_rsp(int host_no, u64 itn_id, int result) -{ - struct Scsi_Host *shost; - int err = -EINVAL; - - pr_debug("%d %d%llx\n", host_no, result, (unsigned long long)itn_id); - - shost = scsi_host_lookup(host_no); - if (!shost) { - pr_err("Could not find host no %d\n", host_no); - return err; - } - - if (!shost->uspace_req_q) { - pr_err("Not target scsi host %d\n", host_no); - goto done; - } - - err = shost->transportt->it_nexus_response(shost, itn_id, result); -done: - scsi_host_put(shost); - return err; -} - -static int __init scsi_tgt_init(void) -{ - int err; - - scsi_tgt_cmd_cache = KMEM_CACHE(scsi_tgt_cmd, 0); - if (!scsi_tgt_cmd_cache) - return -ENOMEM; - - scsi_tgtd = alloc_workqueue("scsi_tgtd", 0, 1); - if (!scsi_tgtd) { - err = -ENOMEM; - goto free_kmemcache; - } - - err = scsi_tgt_if_init(); - if (err) - goto destroy_wq; - - return 0; - -destroy_wq: - destroy_workqueue(scsi_tgtd); -free_kmemcache: - kmem_cache_destroy(scsi_tgt_cmd_cache); - return err; -} - -static void __exit scsi_tgt_exit(void) -{ - destroy_workqueue(scsi_tgtd); - scsi_tgt_if_exit(); - kmem_cache_destroy(scsi_tgt_cmd_cache); -} - -module_init(scsi_tgt_init); -module_exit(scsi_tgt_exit); - -MODULE_DESCRIPTION("SCSI target core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h deleted file mode 100644 index c44309a0e319cc..00000000000000 --- a/drivers/scsi/scsi_tgt_priv.h +++ /dev/null @@ -1,25 +0,0 @@ -struct scsi_cmnd; -struct scsi_lun; -struct Scsi_Host; -struct task_struct; - -extern void scsi_tgt_if_exit(void); -extern int scsi_tgt_if_init(void); - -extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, u64 it_nexus_id, - struct scsi_lun *lun, u64 tag); -extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 it_nexus_id, - u64 tag); -extern int scsi_tgt_kspace_exec(int host_no, u64 it_nexus_id, int result, - u64 tag, unsigned long uaddr, u32 len, - unsigned long sense_uaddr, u32 sense_len, - u8 rw); -extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, u64 it_nexus_id, - int function, u64 tag, - struct scsi_lun *scsilun, void *data); -extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 it_nexus_id, - u64 mid, int result); -extern int scsi_tgt_uspace_send_it_nexus_request(int host_no, u64 it_nexus_id, - int function, char *initiator); -extern int scsi_tgt_kspace_it_nexus_rsp(int host_no, u64 it_nexus_id, - int result); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 4b50da541e31dc..8a8822641b2663 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -39,7 +39,6 @@ #include #include #include "scsi_priv.h" -#include "scsi_transport_fc_internal.h" static int fc_queue_work(struct Scsi_Host *, struct work_struct *); static void fc_vport_sched_delete(struct work_struct *work); @@ -3013,10 +3012,6 @@ fc_remote_port_delete(struct fc_rport *rport) spin_unlock_irqrestore(shost->host_lock, flags); - if (rport->roles & FC_PORT_ROLE_FCP_INITIATOR && - shost->active_mode & MODE_TARGET) - fc_tgt_it_nexus_destroy(shost, (unsigned long)rport); - scsi_target_block(&rport->dev); /* see if we need to kill io faster than waiting for device loss */ @@ -3057,7 +3052,6 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsigned long flags; int create = 0; - int ret; spin_lock_irqsave(shost->host_lock, flags); if (roles & FC_PORT_ROLE_FCP_TARGET) { @@ -3066,12 +3060,6 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) create = 1; } else if (!(rport->roles & FC_PORT_ROLE_FCP_TARGET)) create = 1; - } else if (shost->active_mode & MODE_TARGET) { - ret = fc_tgt_it_nexus_create(shost, (unsigned long)rport, - (char *)&rport->node_name); - if (ret) - printk(KERN_ERR "FC Remore Port tgt nexus failed %d\n", - ret); } rport->roles = roles; @@ -3105,10 +3093,6 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) /* ensure any stgt delete functions are done */ fc_flush_work(shost); - if (rport->roles & FC_PORT_ROLE_FCP_INITIATOR && - shost->active_mode & MODE_TARGET) - fc_tgt_it_nexus_destroy(shost, (unsigned long)rport); - scsi_target_unblock(&rport->dev, SDEV_RUNNING); /* initiate a scan of the target */ spin_lock_irqsave(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_transport_fc_internal.h b/drivers/scsi/scsi_transport_fc_internal.h deleted file mode 100644 index e7bfbe751c1f76..00000000000000 --- a/drivers/scsi/scsi_transport_fc_internal.h +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#ifdef CONFIG_SCSI_FC_TGT_ATTRS -static inline int fc_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return scsi_tgt_it_nexus_create(shost, itn_id, initiator); -} - -static inline int fc_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return scsi_tgt_it_nexus_destroy(shost, itn_id); -} -#else -static inline int fc_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return 0; -} - -static inline int fc_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return 0; -} - -#endif diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index 6d82a5d0ba7858..2bc7a88b3043be 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -33,7 +33,6 @@ #include #include #include "scsi_priv.h" -#include "scsi_transport_srp_internal.h" struct srp_host_attrs { atomic_t next_port_id; @@ -766,18 +765,6 @@ struct srp_rport *srp_rport_add(struct Scsi_Host *shost, return ERR_PTR(ret); } - if (shost->active_mode & MODE_TARGET && - ids->roles == SRP_RPORT_ROLE_INITIATOR) { - ret = srp_tgt_it_nexus_create(shost, (unsigned long)rport, - rport->port_id); - if (ret) { - device_del(&rport->dev); - transport_destroy_device(&rport->dev); - put_device(&rport->dev); - return ERR_PTR(ret); - } - } - transport_add_device(&rport->dev); transport_configure_device(&rport->dev); @@ -794,11 +781,6 @@ EXPORT_SYMBOL_GPL(srp_rport_add); void srp_rport_del(struct srp_rport *rport) { struct device *dev = &rport->dev; - struct Scsi_Host *shost = dev_to_shost(dev->parent); - - if (shost->active_mode & MODE_TARGET && - rport->roles == SRP_RPORT_ROLE_INITIATOR) - srp_tgt_it_nexus_destroy(shost, (unsigned long)rport); transport_remove_device(dev); device_del(dev); diff --git a/drivers/scsi/scsi_transport_srp_internal.h b/drivers/scsi/scsi_transport_srp_internal.h deleted file mode 100644 index 8a79747f9f3df8..00000000000000 --- a/drivers/scsi/scsi_transport_srp_internal.h +++ /dev/null @@ -1,25 +0,0 @@ -#include - -#ifdef CONFIG_SCSI_SRP_TGT_ATTRS -static inline int srp_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return scsi_tgt_it_nexus_create(shost, itn_id, initiator); -} - -static inline int srp_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return scsi_tgt_it_nexus_destroy(shost, itn_id); -} - -#else -static inline int srp_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return 0; -} -static inline int srp_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return 0; -} -#endif diff --git a/include/scsi/scsi_tgt.h b/include/scsi/scsi_tgt.h deleted file mode 100644 index 4b9d62f05306c7..00000000000000 --- a/include/scsi/scsi_tgt.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * SCSI target definitions - */ - -#include - -struct Scsi_Host; -struct scsi_cmnd; -struct scsi_lun; - -extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *); -extern int scsi_tgt_alloc_queue(struct Scsi_Host *); -extern void scsi_tgt_free_queue(struct Scsi_Host *); -extern int scsi_tgt_queue_command(struct scsi_cmnd *, u64, - struct scsi_lun *, u64); -extern int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *, u64, int, u64, - struct scsi_lun *, void *); -extern struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *, - enum dma_data_direction, gfp_t); -extern void scsi_host_put_command(struct Scsi_Host *, struct scsi_cmnd *); -extern int scsi_tgt_it_nexus_create(struct Scsi_Host *, u64, char *); -extern int scsi_tgt_it_nexus_destroy(struct Scsi_Host *, u64); diff --git a/include/scsi/scsi_tgt_if.h b/include/scsi/scsi_tgt_if.h deleted file mode 100644 index cffa54b3eac87e..00000000000000 --- a/include/scsi/scsi_tgt_if.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * SCSI target kernel/user interface - * - * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2005 Mike Christie - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#ifndef __SCSI_TARGET_IF_H -#define __SCSI_TARGET_IF_H - -/* user -> kernel */ -#define TGT_UEVENT_CMD_RSP 0x0001 -#define TGT_UEVENT_IT_NEXUS_RSP 0x0002 -#define TGT_UEVENT_TSK_MGMT_RSP 0x0003 - -/* kernel -> user */ -#define TGT_KEVENT_CMD_REQ 0x1001 -#define TGT_KEVENT_CMD_DONE 0x1002 -#define TGT_KEVENT_IT_NEXUS_REQ 0x1003 -#define TGT_KEVENT_TSK_MGMT_REQ 0x1004 - -struct tgt_event_hdr { - uint16_t version; - uint16_t status; - uint16_t type; - uint16_t len; -} __aligned(sizeof(uint64_t)); - -struct tgt_event { - struct tgt_event_hdr hdr; - - union { - /* user-> kernel */ - struct { - int host_no; - int result; - aligned_u64 itn_id; - aligned_u64 tag; - aligned_u64 uaddr; - aligned_u64 sense_uaddr; - uint32_t len; - uint32_t sense_len; - uint8_t rw; - } cmd_rsp; - struct { - int host_no; - int result; - aligned_u64 itn_id; - aligned_u64 mid; - } tsk_mgmt_rsp; - struct { - __s32 host_no; - __s32 result; - aligned_u64 itn_id; - __u32 function; - } it_nexus_rsp; - - /* kernel -> user */ - struct { - int host_no; - uint32_t data_len; - aligned_u64 itn_id; - uint8_t scb[16]; - uint8_t lun[8]; - int attribute; - aligned_u64 tag; - } cmd_req; - struct { - int host_no; - int result; - aligned_u64 itn_id; - aligned_u64 tag; - } cmd_done; - struct { - int host_no; - int function; - aligned_u64 itn_id; - aligned_u64 tag; - uint8_t lun[8]; - aligned_u64 mid; - } tsk_mgmt_req; - struct { - __s32 host_no; - __u32 function; - aligned_u64 itn_id; - __u32 max_cmds; - __u8 initiator_id[16]; - } it_nexus_req; - } p; -} __aligned(sizeof(uint64_t)); - -#define TGT_RING_SIZE (1UL << 16) - -#endif