/*******************************************************************************
*
* 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 <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) {
pr_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 = MSG_HEAD_TAG;
break;
case ORDERED_QUEUE_TAG:
sam_task_attr = MSG_ORDERED_TAG;
break;
default:
sam_task_attr = MSG_SIMPLE_TAG;
break;
}
} else
sam_task_attr = MSG_SIMPLE_TAG;
/*
* 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]);
if (scsi_bidi_cmnd(sc))
se_cmd->se_cmd_flags |= SCF_BIDI;
/*
* Locate the struct se_lun pointer and attach it to struct se_cmd
*/
if (transport_lookup_cmd_lun(se_cmd, tl_cmd->sc->device->lun) < 0) {
kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
set_host_byte(sc, DID_NO_CONNECT);
return NULL;
}
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;
struct scatterlist *sgl_bidi = NULL;
u32 sgl_bidi_count = 0;
int ret;
/*
* Allocate the necessary tasks to complete the received CDB+data
*/
ret = transport_generic_allocate_tasks(se_cmd, sc->cmnd);
if (ret != 0)
return ret;
/*
* For BIDI commands, pass in the extra READ buffer
* to transport_generic_map_mem_to_cmd() below..
*/
if (se_cmd->se_cmd_flags & SCF_BIDI) {
struct scsi_data_buffer *sdb = scsi_in(sc);
sgl_bidi = sdb->table.sgl;
sgl_bidi_count = sdb->table.nents;
}
/*
* Because some userspace code via scsi-generic do not memset their
* associated read buffers, go ahead and do that here for type
* SCF_SCSI_CONTROL_SG_IO_CDB. Also note that this is currently
* guaranteed to be a single SGL for SCF_SCSI_CONTROL_SG_IO_CDB
* by target core in transport_generic_allocate_tasks() ->
* transport_generic_cmd_sequencer().
*/
if (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB &&
se_cmd->data_direction == DMA_FROM_DEVICE) {
struct scatterlist *sg = scsi_sglist(sc);
unsigned char *buf = kmap(sg_page(sg)) + sg->offset;
if (buf != NULL) {
memset(buf, 0, sg->length);
kunmap(sg_page(sg));
}
}
/* Tell the core about our preallocated memory */
return transport_generic_map_mem_to_cmd(se_cmd, scsi_sglist(sc),
scsi_sg_count(sc), sgl_bidi, sgl_bidi_count);
}
/*
* Called from struct target_core_fabric_ops->check_stop_free()
*/
static int 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 0;
/*
* 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);
return 1;
}
static void tcm_loop_release_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)
{
|