/******************************************************************************* * * This file contains the Linux/SCSI LLD virtual SCSI initiator driver * for emulated SAS initiator ports * * © Copyright 2011 RisingTide Systems LLC. * * Licensed to the Linux Foundation under the General Public License (GPL) version 2. * * Author: Nicholas A. Bellinger <nab@risingtidesystems.com> * * 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. ****************************************************************************/ #include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/configfs.h>#include <scsi/scsi.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_host.h>#include <scsi/scsi_device.h>#include <scsi/scsi_cmnd.h>#include <scsi/libsas.h> /* For TASK_ATTR_* */ #include <target/target_core_base.h>#include <target/target_core_transport.h>#include <target/target_core_fabric_ops.h>#include <target/target_core_fabric_configfs.h>#include <target/target_core_fabric_lib.h>#include <target/target_core_configfs.h>#include <target/target_core_device.h>#include <target/target_core_tpg.h>#include <target/target_core_tmr.h> #include "tcm_loop.h" #define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev) /* Local pointer to allocated TCM configfs fabric module */static struct target_fabric_configfs *tcm_loop_fabric_configfs; static struct kmem_cache *tcm_loop_cmd_cache; static int tcm_loop_hba_no_cnt; /* * Allocate a tcm_loop cmd descriptor from target_core_mod code * * Can be called from interrupt context in tcm_loop_queuecommand() below */static struct se_cmd *tcm_loop_allocate_core_cmd( struct tcm_loop_hba *tl_hba, struct se_portal_group *se_tpg, struct scsi_cmnd *sc){ struct se_cmd *se_cmd; struct se_session *se_sess; struct tcm_loop_nexus *tl_nexus = tl_hba->tl_nexus; struct tcm_loop_cmd *tl_cmd; int sam_task_attr; if (!tl_nexus) { scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" " does not exist\n"); set_host_byte(sc, DID_ERROR); return NULL; } se_sess = tl_nexus->se_sess; tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC); if (!tl_cmd) { printk(KERN_ERR "Unable to allocate struct tcm_loop_cmd\n"); set_host_byte(sc, DID_ERROR); return NULL; } se_cmd = &tl_cmd->tl_se_cmd; /* * Save the pointer to struct scsi_cmnd *sc */ tl_cmd->sc = sc; /* * Locate the SAM Task Attr from struct scsi_cmnd * */ if (sc->device->tagged_supported) { switch (sc->tag) { case HEAD_OF_QUEUE_TAG: sam_task_attr = TASK_ATTR_HOQ; break; case ORDERED_QUEUE_TAG: sam_task_attr = TASK_ATTR_ORDERED; break; default: sam_task_attr = TASK_ATTR_SIMPLE; break; } } else sam_task_attr = TASK_ATTR_SIMPLE; /* * Initialize struct se_cmd descriptor from target_core_mod infrastructure */ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, scsi_bufflen(sc), sc->sc_data_direction, sam_task_attr, &tl_cmd->tl_sense_buf[0]); /* * Signal BIDI usage with T_TASK(cmd)->t_tasks_bidi */ if (scsi_bidi_cmnd(sc)) T_TASK(se_cmd)->t_tasks_bidi = 1; /* * Locate the struct se_lun pointer and attach it to struct se_cmd */ if (transport_get_lun_for_cmd(se_cmd, NULL, tl_cmd->sc->device->lun) < 0) { kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); set_host_byte(sc, DID_NO_CONNECT); return NULL; } transport_device_setup_cmd(se_cmd); return se_cmd;} /* * Called by struct target_core_fabric_ops->new_cmd_map() * * Always called in process context. A non zero return value * here will signal to handle an exception based on the return code. */static int tcm_loop_new_cmd_map(struct se_cmd *se_cmd){ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, struct tcm_loop_cmd, tl_se_cmd); struct scsi_cmnd *sc = tl_cmd->sc; void *mem_ptr, *mem_bidi_ptr = NULL; u32 sg_no_bidi = 0; int ret; /* * Allocate the necessary tasks to complete the received CDB+data */ ret = transport_generic_allocate_tasks(se_cmd, tl_cmd->sc->cmnd); if (ret == -1) { /* Out of Resources */ return PYX_TRANSPORT_LU_COMM_FAILURE; } else if (ret == -2) { /* * Handle case for SAM_STAT_RESERVATION_CONFLICT */ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) return PYX_TRANSPORT_RESERVATION_CONFLICT; /* * Otherwise, return SAM_STAT_CHECK_CONDITION and return * sense data. */ return PYX_TRANSPORT_USE_SENSE_REASON; } /* * Setup the struct scatterlist memory from the received * struct scsi_cmnd. */ if (scsi_sg_count(sc)) { se_cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM; mem_ptr = (void *)scsi_sglist(sc); /* * For BIDI commands, pass in the extra READ buffer * to transport_generic_map_mem_to_cmd() below.. */ if (T_TASK(se_cmd)->t_tasks_bidi) { struct scsi_data_buffer *sdb = scsi_in(sc); mem_bidi_ptr = (void *)sdb->table.sgl; sg_no_bidi = sdb->table.nents; } } else { /* * Used for DMA_NONE */ mem_ptr = NULL; } /* * Map the SG memory into struct se_mem->page linked list using the same * physical memory at sg->page_link. */ ret = transport_generic_map_mem_to_cmd(se_cmd, mem_ptr, scsi_sg_count(sc), mem_bidi_ptr, sg_no_bidi); if (ret < 0) return PYX_TRANSPORT_LU_COMM_FAILURE; return 0;} /* * Called from struct target_core_fabric_ops->check_stop_free() */static void tcm_loop_check_stop_free(struct se_cmd *se_cmd){ /* * Do not release struct se_cmd's containing a valid TMR * pointer. These will be released directly in tcm_loop_device_reset() * with transport_generic_free_cmd(). */ if (se_cmd->se_tmr_req) return; /* * Release the struct se_cmd, which will make a callback to release * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd() */ transport_generic_free_cmd(se_cmd, 0, 1, 0);} /* * Called from struct target_core_fabric_ops->release_cmd_to_pool() */static void tcm_loop_deallocate_core_cmd(struct se_cmd *se_cmd){ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, struct tcm_loop_cmd, tl_se_cmd); kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);} static int tcm_loop_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout){ return sprintf(buffer, "tcm_loop_proc_info()\n");} static int tcm_loop_driver_probe(struct device *);static int tcm_loop_driver_remove(struct device *); static int pseudo_lld_bus_match(struct device *dev, struct device_driver *dev_driver){ return 1;} static struct bus_type tcm_loop_lld_bus = { .name = "tcm_loop_bus", .match = pseudo_lld_bus_match, .probe = tcm_loop_driver_probe, .remove = tcm_loop_driver_remove,}; static struct device_driver tcm_loop_driverfs = { .name = "tcm_loop", .bus = &tcm_loop_lld_bus,};/* * Used with root_device_register() in tcm_loop_alloc_core_bus() below */struct device *tcm_loop_primary; /* * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and * drivers/scsi/libiscsi.c:iscsi_change_queue_depth() */static int tcm_loop_change_queue_depth( struct scsi_device *sdev, int depth, int reason){ switch (reason) { case SCSI_QDEPTH_DEFAULT: scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); break; case SCSI_QDEPTH_QFULL: scsi_track_queue_full(sdev, depth); break; case SCSI_QDEPTH_RAMP_UP: scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); break; default: return -EOPNOTSUPP; } return sdev->queue_depth;} /* * Main entry point from struct scsi_host_template for incoming SCSI CDB+Data * from Linux/SCSI subsystem for SCSI low level device drivers (LLDs) */static int tcm_loop_queuecommand( struct Scsi_Host *sh, struct scsi_cmnd *sc){ struct se_cmd *se_cmd; struct se_portal_group *se_tpg; struct tcm_loop_hba *tl_hba; struct tcm_loop_tpg *tl_tpg; TL_CDB_DEBUG("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x" " scsi_buf_len: %u\n", sc->device->host->host_no, sc->device->id, sc->device->channel, sc->device->lun, sc->cmnd[0], scsi_bufflen(sc)); /* * Locate the tcm_loop_hba_t pointer */ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; se_tpg = &tl_tpg->tl_se_tpg; /* * Determine the SAM Task Attribute and allocate tl_cmd and * tl_cmd->tl_se_cmd from TCM infrastructure */ se_cmd = tcm_loop_allocate_core_cmd(tl_hba, se_tpg, sc); if (!se_cmd) { sc->scsi_done(sc); return 0; } /* * Queue up the newly allocated to be processed in TCM thread context. */ transport_generic_handle_cdb_map(se_cmd); return 0;} /* * Called from SCSI EH process context to issue a LUN_RESET TMR * to struct scsi_device */static int tcm_loop_device_reset(struct scsi_cmnd *sc){ struct se_cmd *se_cmd = NULL; struct se_portal_group *se_tpg; struct se_session *se_sess; struct tcm_loop_cmd *tl_cmd = NULL; struct tcm_loop_hba *tl_hba; struct tcm_loop_nexus *tl_nexus; struct tcm_loop_tmr *tl_tmr = NULL; struct tcm_loop_tpg *tl_tpg; int ret = FAILED; /* * Locate the tcm_loop_hba_t pointer */ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); /* * Locate the tl_nexus and se_sess pointers */ tl_nexus = tl_hba->tl_nexus; if (!tl_nexus) { printk(KERN_ERR "Unable to perform device reset without" " active I_T Nexus\n"); return FAILED; } se_sess = tl_nexus->se_sess; /* * Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id */ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; se_tpg = &tl_tpg->tl_se_tpg; tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL); if (!tl_cmd) { printk(KERN_ERR "Unable to allocate memory for tl_cmd\n"); return FAILED; } tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL); if (!tl_tmr) { printk(KERN_ERR "Unable to allocate memory for tl_tmr\n"); goto release; } init_waitqueue_head(&tl_tmr->tl_tmr_wait); se_cmd = &tl_cmd->tl_se_cmd; /* * Initialize struct se_cmd descriptor from target_core_mod infrastructure */ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0, DMA_NONE, TASK_ATTR_SIMPLE, &tl_cmd->tl_sense_buf[0]); /* * Allocate the LUN_RESET TMR */ se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, (void *)tl_tmr, TMR_LUN_RESET); if (!se_cmd->se_tmr_req) goto release; /* * Locate the underlying TCM struct se_lun from sc->device->lun */ if (transport_get_lun_for_tmr(se_cmd, sc->device->lun) < 0) goto release; /* * Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp() * to wake us up. */ transport_generic_handle_tmr(se_cmd); wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete)); /* * The TMR LUN_RESET has completed, check the response status and * then release allocations. */ ret = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;release: if (se_cmd) transport_generic_free_cmd(se_cmd, 1, 1, 0); else kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); kfree(tl_tmr); return ret;} static int tcm_loop_slave_alloc(struct scsi_device *sd){ set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags); return 0;} static int tcm_loop_slave_configure(struct scsi_device *sd){ return 0;} static struct scsi_host_template tcm_loop_driver_template = { .proc_info = tcm_loop_proc_info, .proc_name = "tcm_loopback", .name = "TCM_Loopback", .queuecommand = tcm_loop_queuecommand, .change_queue_depth = tcm_loop_change_queue_depth, .eh_device_reset_handler = tcm_loop_device_reset, .can_queue = TL_SCSI_CAN_QUEUE, .this_id = -1, .sg_tablesize = TL_SCSI_SG_TABLESIZE, .cmd_per_lun = TL_SCSI_CMD_PER_LUN, .max_sectors = TL_SCSI_MAX_SECTORS, .use_clustering = DISABLE_CLUSTERING, .slave_alloc = tcm_loop_slave_alloc, .slave_configure = tcm_loop_slave_configure, .module = THIS_MODULE,}; static int tcm_loop_driver_probe(struct device *dev){ struct tcm_loop_hba *tl_hba; struct Scsi_Host *sh; int error; tl_hba = to_tcm_loop_hba(dev); sh = scsi_host_alloc(&tcm_loop_driver_template, sizeof(struct tcm_loop_hba)); if (!sh) { printk(KERN_ERR "Unable to allocate struct scsi_host\n"); return -ENODEV; } tl_hba->sh = sh; /* * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata */ *((struct tcm_loop_hba **)sh->hostdata) = tl_hba; /* * Setup single ID, Channel and LUN for now.. */ sh->max_id = 2; sh->max_lun = 0; sh->max_channel = 0; sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN; error = scsi_add_host(sh, &tl_hba->dev); if (error) { printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); scsi_host_put(sh); return -ENODEV; } return 0;} static int tcm_loop_driver_remove(struct device *dev){ struct tcm_loop_hba *tl_hba; struct Scsi_Host *sh; tl_hba = to_tcm_loop_hba(dev); sh = tl_hba->sh; scsi_remove_host(sh); scsi_host_put(sh); return 0;} static void tcm_loop_release_adapter(struct device *dev){ struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev); kfree(tl_hba);Lines 1–500 of 1579. Load more
/******************************************************************************* * * This file contains the Linux/SCSI LLD virtual SCSI initiator driver * for emulated SAS initiator ports * * © Copyright 2011 RisingTide Systems LLC. * * Licensed to the Linux Foundation under the General Public License (GPL) version 2. * * Author: Nicholas A. Bellinger <nab@risingtidesystems.com> * * 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. ****************************************************************************/ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/configfs.h> #include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> #include <scsi/scsi_cmnd.h> #include <scsi/libsas.h> /* For TASK_ATTR_* */ #include <target/target_core_base.h> #include <target/target_core_transport.h> #include <target/target_core_fabric_ops.h> #include <target/target_core_fabric_configfs.h> #include <target/target_core_fabric_lib.h> #include <target/target_core_configfs.h> #include <target/target_core_device.h> #include <target/target_core_tpg.h> #include <target/target_core_tmr.h> #include "tcm_loop.h" #define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev) /* Local pointer to allocated TCM configfs fabric module */ static struct target_fabric_configfs *tcm_loop_fabric_configfs; static struct kmem_cache *tcm_loop_cmd_cache; static int tcm_loop_hba_no_cnt; /* * Allocate a tcm_loop cmd descriptor from target_core_mod code * * Can be called from interrupt context in tcm_loop_queuecommand() below */ static struct se_cmd *tcm_loop_allocate_core_cmd( struct tcm_loop_hba *tl_hba, struct se_portal_group *se_tpg, struct scsi_cmnd *sc) { struct se_cmd *se_cmd; struct se_session *se_sess; struct tcm_loop_nexus *tl_nexus = tl_hba->tl_nexus; struct tcm_loop_cmd *tl_cmd; int sam_task_attr; if (!tl_nexus) { scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" " does not exist\n"); set_host_byte(sc, DID_ERROR); return NULL; } se_sess = tl_nexus->se_sess; tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC); if (!tl_cmd) { printk(KERN_ERR "Unable to allocate struct tcm_loop_cmd\n"); set_host_byte(sc, DID_ERROR); return NULL; } se_cmd = &tl_cmd->tl_se_cmd; /* * Save the pointer to struct scsi_cmnd *sc */ tl_cmd->sc = sc; /* * Locate the SAM Task Attr from struct scsi_cmnd * */ if (sc->device->tagged_supported) { switch (sc->tag) { case HEAD_OF_QUEUE_TAG: sam_task_attr = TASK_ATTR_HOQ; break; case ORDERED_QUEUE_TAG: sam_task_attr = TASK_ATTR_ORDERED; break; default: sam_task_attr = TASK_ATTR_SIMPLE; break; } } else sam_task_attr = TASK_ATTR_SIMPLE; /* * Initialize struct se_cmd descriptor from target_core_mod infrastructure */ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, scsi_bufflen(sc), sc->sc_data_direction, sam_task_attr, &tl_cmd->tl_sense_buf[0]); /* * Signal BIDI usage with T_TASK(cmd)->t_tasks_bidi */ if (scsi_bidi_cmnd(sc)) T_TASK(se_cmd)->t_tasks_bidi = 1; /* * Locate the struct se_lun pointer and attach it to struct se_cmd */ if (transport_get_lun_for_cmd(se_cmd, NULL, tl_cmd->sc->device->lun) < 0) { kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); set_host_byte(sc, DID_NO_CONNECT); return NULL; } transport_device_setup_cmd(se_cmd); return se_cmd; } /* * Called by struct target_core_fabric_ops->new_cmd_map() * * Always called in process context. A non zero return value * here will signal to handle an exception based on the return code. */ static int tcm_loop_new_cmd_map(struct se_cmd *se_cmd) { struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, struct tcm_loop_cmd, tl_se_cmd); struct scsi_cmnd *sc = tl_cmd->sc; void *mem_ptr, *mem_bidi_ptr = NULL; u32 sg_no_bidi = 0; int ret; /* * Allocate the necessary tasks to complete the received CDB+data */ ret = transport_generic_allocate_tasks(se_cmd, tl_cmd->sc->cmnd); if (ret == -1) { /* Out of Resources */ return PYX_TRANSPORT_LU_COMM_FAILURE; } else if (ret == -2) { /* * Handle case for SAM_STAT_RESERVATION_CONFLICT */ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) return PYX_TRANSPORT_RESERVATION_CONFLICT; /* * Otherwise, return SAM_STAT_CHECK_CONDITION and return * sense data. */ return PYX_TRANSPORT_USE_SENSE_REASON; } /* * Setup the struct scatterlist memory from the received * struct scsi_cmnd. */ if (scsi_sg_count(sc)) { se_cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM; mem_ptr = (void *)scsi_sglist(sc); /* * For BIDI commands, pass in the extra READ buffer * to transport_generic_map_mem_to_cmd() below.. */ if (T_TASK(se_cmd)->t_tasks_bidi) { struct scsi_data_buffer *sdb = scsi_in(sc); mem_bidi_ptr = (void *)sdb->table.sgl; sg_no_bidi = sdb->table.nents; } } else { /* * Used for DMA_NONE */ mem_ptr = NULL; } /* * Map the SG memory into struct se_mem->page linked list using the same * physical memory at sg->page_link. */ ret = transport_generic_map_mem_to_cmd(se_cmd, mem_ptr, scsi_sg_count(sc), mem_bidi_ptr, sg_no_bidi); if (ret < 0) return PYX_TRANSPORT_LU_COMM_FAILURE; return 0; } /* * Called from struct target_core_fabric_ops->check_stop_free() */ static void tcm_loop_check_stop_free(struct se_cmd *se_cmd) { /* * Do not release struct se_cmd's containing a valid TMR * pointer. These will be released directly in tcm_loop_device_reset() * with transport_generic_free_cmd(). */ if (se_cmd->se_tmr_req) return; /* * Release the struct se_cmd, which will make a callback to release * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd() */ transport_generic_free_cmd(se_cmd, 0, 1, 0); } /* * Called from struct target_core_fabric_ops->release_cmd_to_pool() */ static void tcm_loop_deallocate_core_cmd(struct se_cmd *se_cmd) { struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, struct tcm_loop_cmd, tl_se_cmd); kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); } static int tcm_loop_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout) { return sprintf(buffer, "tcm_loop_proc_info()\n"); } static int tcm_loop_driver_probe(struct device *); static int tcm_loop_driver_remove(struct device *); static int pseudo_lld_bus_match(struct device *dev, struct device_driver *dev_driver) { return 1; } static struct bus_type tcm_loop_lld_bus = { .name = "tcm_loop_bus", .match = pseudo_lld_bus_match, .probe = tcm_loop_driver_probe, .remove = tcm_loop_driver_remove, }; static struct device_driver tcm_loop_driverfs = { .name = "tcm_loop", .bus = &tcm_loop_lld_bus, }; /* * Used with root_device_register() in tcm_loop_alloc_core_bus() below */ struct device *tcm_loop_primary; /* * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and * drivers/scsi/libiscsi.c:iscsi_change_queue_depth() */ static int tcm_loop_change_queue_depth( struct scsi_device *sdev, int depth, int reason) { switch (reason) { case SCSI_QDEPTH_DEFAULT: scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); break; case SCSI_QDEPTH_QFULL: scsi_track_queue_full(sdev, depth); break; case SCSI_QDEPTH_RAMP_UP: scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); break; default: return -EOPNOTSUPP; } return sdev->queue_depth; } /* * Main entry point from struct scsi_host_template for incoming SCSI CDB+Data * from Linux/SCSI subsystem for SCSI low level device drivers (LLDs) */ static int tcm_loop_queuecommand( struct Scsi_Host *sh, struct scsi_cmnd *sc) { struct se_cmd *se_cmd; struct se_portal_group *se_tpg; struct tcm_loop_hba *tl_hba; struct tcm_loop_tpg *tl_tpg; TL_CDB_DEBUG("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x" " scsi_buf_len: %u\n", sc->device->host->host_no, sc->device->id, sc->device->channel, sc->device->lun, sc->cmnd[0], scsi_bufflen(sc)); /* * Locate the tcm_loop_hba_t pointer */ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; se_tpg = &tl_tpg->tl_se_tpg; /* * Determine the SAM Task Attribute and allocate tl_cmd and * tl_cmd->tl_se_cmd from TCM infrastructure */ se_cmd = tcm_loop_allocate_core_cmd(tl_hba, se_tpg, sc); if (!se_cmd) { sc->scsi_done(sc); return 0; } /* * Queue up the newly allocated to be processed in TCM thread context. */ transport_generic_handle_cdb_map(se_cmd); return 0; } /* * Called from SCSI EH process context to issue a LUN_RESET TMR * to struct scsi_device */ static int tcm_loop_device_reset(struct scsi_cmnd *sc) { struct se_cmd *se_cmd = NULL; struct se_portal_group *se_tpg; struct se_session *se_sess; struct tcm_loop_cmd *tl_cmd = NULL; struct tcm_loop_hba *tl_hba; struct tcm_loop_nexus *tl_nexus; struct tcm_loop_tmr *tl_tmr = NULL; struct tcm_loop_tpg *tl_tpg; int ret = FAILED; /* * Locate the tcm_loop_hba_t pointer */ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); /* * Locate the tl_nexus and se_sess pointers */ tl_nexus = tl_hba->tl_nexus; if (!tl_nexus) { printk(KERN_ERR "Unable to perform device reset without" " active I_T Nexus\n"); return FAILED; } se_sess = tl_nexus->se_sess; /* * Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id */ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; se_tpg = &tl_tpg->tl_se_tpg; tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL); if (!tl_cmd) { printk(KERN_ERR "Unable to allocate memory for tl_cmd\n"); return FAILED; } tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL); if (!tl_tmr) { printk(KERN_ERR "Unable to allocate memory for tl_tmr\n"); goto release; } init_waitqueue_head(&tl_tmr->tl_tmr_wait); se_cmd = &tl_cmd->tl_se_cmd; /* * Initialize struct se_cmd descriptor from target_core_mod infrastructure */ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0, DMA_NONE, TASK_ATTR_SIMPLE, &tl_cmd->tl_sense_buf[0]); /* * Allocate the LUN_RESET TMR */ se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, (void *)tl_tmr, TMR_LUN_RESET); if (!se_cmd->se_tmr_req) goto release; /* * Locate the underlying TCM struct se_lun from sc->device->lun */ if (transport_get_lun_for_tmr(se_cmd, sc->device->lun) < 0) goto release; /* * Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp() * to wake us up. */ transport_generic_handle_tmr(se_cmd); wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete)); /* * The TMR LUN_RESET has completed, check the response status and * then release allocations. */ ret = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; release: if (se_cmd) transport_generic_free_cmd(se_cmd, 1, 1, 0); else kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); kfree(tl_tmr); return ret; } static int tcm_loop_slave_alloc(struct scsi_device *sd) { set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags); return 0; } static int tcm_loop_slave_configure(struct scsi_device *sd) { return 0; } static struct scsi_host_template tcm_loop_driver_template = { .proc_info = tcm_loop_proc_info, .proc_name = "tcm_loopback", .name = "TCM_Loopback", .queuecommand = tcm_loop_queuecommand, .change_queue_depth = tcm_loop_change_queue_depth, .eh_device_reset_handler = tcm_loop_device_reset, .can_queue = TL_SCSI_CAN_QUEUE, .this_id = -1, .sg_tablesize = TL_SCSI_SG_TABLESIZE, .cmd_per_lun = TL_SCSI_CMD_PER_LUN, .max_sectors = TL_SCSI_MAX_SECTORS, .use_clustering = DISABLE_CLUSTERING, .slave_alloc = tcm_loop_slave_alloc, .slave_configure = tcm_loop_slave_configure, .module = THIS_MODULE, }; static int tcm_loop_driver_probe(struct device *dev) { struct tcm_loop_hba *tl_hba; struct Scsi_Host *sh; int error; tl_hba = to_tcm_loop_hba(dev); sh = scsi_host_alloc(&tcm_loop_driver_template, sizeof(struct tcm_loop_hba)); if (!sh) { printk(KERN_ERR "Unable to allocate struct scsi_host\n"); return -ENODEV; } tl_hba->sh = sh; /* * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata */ *((struct tcm_loop_hba **)sh->hostdata) = tl_hba; /* * Setup single ID, Channel and LUN for now.. */ sh->max_id = 2; sh->max_lun = 0; sh->max_channel = 0; sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN; error = scsi_add_host(sh, &tl_hba->dev); if (error) { printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); scsi_host_put(sh); return -ENODEV; } return 0; } static int tcm_loop_driver_remove(struct device *dev) { struct tcm_loop_hba *tl_hba; struct Scsi_Host *sh; tl_hba = to_tcm_loop_hba(dev); sh = tl_hba->sh; scsi_remove_host(sh); scsi_host_put(sh); return 0; } static void tcm_loop_release_adapter(struct device *dev) { struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev); kfree(tl_hba);