2012-02-05 15:16:00 +04:00
/*
* Virtio SCSI HBA driver
*
* Copyright IBM Corp . 2010
* Copyright Red Hat , Inc . 2011
*
* Authors :
* Stefan Hajnoczi < stefanha @ linux . vnet . ibm . com >
* Paolo Bonzini < pbonzini @ redhat . com >
*
* This work is licensed under the terms of the GNU GPL , version 2 or later .
* See the COPYING file in the top - level directory .
*
*/
2013-03-12 09:04:40 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2012-02-05 15:16:00 +04:00
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/mempool.h>
# include <linux/virtio.h>
# include <linux/virtio_ids.h>
# include <linux/virtio_config.h>
# include <linux/virtio_scsi.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_cmnd.h>
# define VIRTIO_SCSI_MEMPOOL_SZ 64
2012-07-05 13:06:43 +04:00
# define VIRTIO_SCSI_EVENT_LEN 8
2012-02-05 15:16:00 +04:00
/* Command queue element */
struct virtio_scsi_cmd {
struct scsi_cmnd * sc ;
struct completion * comp ;
union {
struct virtio_scsi_cmd_req cmd ;
struct virtio_scsi_ctrl_tmf_req tmf ;
struct virtio_scsi_ctrl_an_req an ;
} req ;
union {
struct virtio_scsi_cmd_resp cmd ;
struct virtio_scsi_ctrl_tmf_resp tmf ;
struct virtio_scsi_ctrl_an_resp an ;
struct virtio_scsi_event evt ;
} resp ;
} ____cacheline_aligned_in_smp ;
2012-07-05 13:06:43 +04:00
struct virtio_scsi_event_node {
struct virtio_scsi * vscsi ;
struct virtio_scsi_event event ;
struct work_struct work ;
} ;
2012-06-13 18:56:32 +04:00
struct virtio_scsi_vq {
/* Protects vq */
spinlock_t vq_lock ;
struct virtqueue * vq ;
} ;
2012-06-13 18:56:34 +04:00
/* Per-target queue state */
struct virtio_scsi_target_state {
2013-03-20 09:14:28 +04:00
/* Never held at the same time as vq_lock. */
2012-06-13 18:56:34 +04:00
spinlock_t tgt_lock ;
} ;
2012-02-05 15:16:00 +04:00
/* Driver instance state */
struct virtio_scsi {
struct virtio_device * vdev ;
2012-06-13 18:56:34 +04:00
2012-06-13 18:56:32 +04:00
struct virtio_scsi_vq ctrl_vq ;
struct virtio_scsi_vq event_vq ;
struct virtio_scsi_vq req_vq ;
2012-02-05 15:16:00 +04:00
2012-07-05 13:06:43 +04:00
/* Get some buffers ready for event vq */
struct virtio_scsi_event_node event_list [ VIRTIO_SCSI_EVENT_LEN ] ;
2012-02-05 15:16:00 +04:00
} ;
static struct kmem_cache * virtscsi_cmd_cache ;
static mempool_t * virtscsi_cmd_pool ;
static inline struct Scsi_Host * virtio_scsi_host ( struct virtio_device * vdev )
{
return vdev - > priv ;
}
static void virtscsi_compute_resid ( struct scsi_cmnd * sc , u32 resid )
{
if ( ! resid )
return ;
if ( ! scsi_bidi_cmnd ( sc ) ) {
scsi_set_resid ( sc , resid ) ;
return ;
}
scsi_in ( sc ) - > resid = min ( resid , scsi_in ( sc ) - > length ) ;
scsi_out ( sc ) - > resid = resid - scsi_in ( sc ) - > resid ;
}
/**
* virtscsi_complete_cmd - finish a scsi_cmd and invoke scsi_done
*
* Called with vq_lock held .
*/
2013-04-08 17:31:38 +04:00
static void virtscsi_complete_cmd ( struct virtio_scsi * vscsi , void * buf )
2012-02-05 15:16:00 +04:00
{
struct virtio_scsi_cmd * cmd = buf ;
struct scsi_cmnd * sc = cmd - > sc ;
struct virtio_scsi_cmd_resp * resp = & cmd - > resp . cmd ;
dev_dbg ( & sc - > device - > sdev_gendev ,
" cmd %p response %u status %#02x sense_len %u \n " ,
sc , resp - > response , resp - > status , resp - > sense_len ) ;
sc - > result = resp - > status ;
virtscsi_compute_resid ( sc , resp - > resid ) ;
switch ( resp - > response ) {
case VIRTIO_SCSI_S_OK :
set_host_byte ( sc , DID_OK ) ;
break ;
case VIRTIO_SCSI_S_OVERRUN :
set_host_byte ( sc , DID_ERROR ) ;
break ;
case VIRTIO_SCSI_S_ABORTED :
set_host_byte ( sc , DID_ABORT ) ;
break ;
case VIRTIO_SCSI_S_BAD_TARGET :
set_host_byte ( sc , DID_BAD_TARGET ) ;
break ;
case VIRTIO_SCSI_S_RESET :
set_host_byte ( sc , DID_RESET ) ;
break ;
case VIRTIO_SCSI_S_BUSY :
set_host_byte ( sc , DID_BUS_BUSY ) ;
break ;
case VIRTIO_SCSI_S_TRANSPORT_FAILURE :
set_host_byte ( sc , DID_TRANSPORT_DISRUPTED ) ;
break ;
case VIRTIO_SCSI_S_TARGET_FAILURE :
set_host_byte ( sc , DID_TARGET_FAILURE ) ;
break ;
case VIRTIO_SCSI_S_NEXUS_FAILURE :
set_host_byte ( sc , DID_NEXUS_FAILURE ) ;
break ;
default :
scmd_printk ( KERN_WARNING , sc , " Unknown response %d " ,
resp - > response ) ;
/* fall through */
case VIRTIO_SCSI_S_FAILURE :
set_host_byte ( sc , DID_ERROR ) ;
break ;
}
WARN_ON ( resp - > sense_len > VIRTIO_SCSI_SENSE_SIZE ) ;
if ( sc - > sense_buffer ) {
memcpy ( sc - > sense_buffer , resp - > sense ,
min_t ( u32 , resp - > sense_len , VIRTIO_SCSI_SENSE_SIZE ) ) ;
if ( resp - > sense_len )
set_driver_byte ( sc , DRIVER_SENSE ) ;
}
mempool_free ( cmd , virtscsi_cmd_pool ) ;
sc - > scsi_done ( sc ) ;
}
2013-04-08 17:32:07 +04:00
static void virtscsi_vq_done ( struct virtio_scsi * vscsi ,
struct virtio_scsi_vq * virtscsi_vq ,
2013-04-08 17:31:38 +04:00
void ( * fn ) ( struct virtio_scsi * vscsi , void * buf ) )
2012-02-05 15:16:00 +04:00
{
void * buf ;
unsigned int len ;
2013-04-08 17:32:07 +04:00
unsigned long flags ;
struct virtqueue * vq = virtscsi_vq - > vq ;
2012-02-05 15:16:00 +04:00
2013-04-08 17:32:07 +04:00
spin_lock_irqsave ( & virtscsi_vq - > vq_lock , flags ) ;
2012-02-05 15:16:00 +04:00
do {
virtqueue_disable_cb ( vq ) ;
while ( ( buf = virtqueue_get_buf ( vq , & len ) ) ! = NULL )
2013-04-08 17:31:38 +04:00
fn ( vscsi , buf ) ;
2012-02-05 15:16:00 +04:00
} while ( ! virtqueue_enable_cb ( vq ) ) ;
2013-04-08 17:32:07 +04:00
spin_unlock_irqrestore ( & virtscsi_vq - > vq_lock , flags ) ;
2012-02-05 15:16:00 +04:00
}
static void virtscsi_req_done ( struct virtqueue * vq )
{
2012-06-13 18:56:32 +04:00
struct Scsi_Host * sh = virtio_scsi_host ( vq - > vdev ) ;
struct virtio_scsi * vscsi = shost_priv ( sh ) ;
2013-04-08 17:32:07 +04:00
virtscsi_vq_done ( vscsi , & vscsi - > req_vq , virtscsi_complete_cmd ) ;
2012-02-05 15:16:00 +04:00
} ;
2013-04-08 17:31:38 +04:00
static void virtscsi_complete_free ( struct virtio_scsi * vscsi , void * buf )
2012-02-05 15:16:00 +04:00
{
struct virtio_scsi_cmd * cmd = buf ;
if ( cmd - > comp )
complete_all ( cmd - > comp ) ;
2012-05-04 14:32:04 +04:00
else
mempool_free ( cmd , virtscsi_cmd_pool ) ;
2012-02-05 15:16:00 +04:00
}
static void virtscsi_ctrl_done ( struct virtqueue * vq )
{
2012-06-13 18:56:32 +04:00
struct Scsi_Host * sh = virtio_scsi_host ( vq - > vdev ) ;
struct virtio_scsi * vscsi = shost_priv ( sh ) ;
2013-04-08 17:32:07 +04:00
virtscsi_vq_done ( vscsi , & vscsi - > ctrl_vq , virtscsi_complete_free ) ;
2012-02-05 15:16:00 +04:00
} ;
2012-07-05 13:06:43 +04:00
static int virtscsi_kick_event ( struct virtio_scsi * vscsi ,
struct virtio_scsi_event_node * event_node )
{
2012-10-16 17:26:16 +04:00
int err ;
2012-07-05 13:06:43 +04:00
struct scatterlist sg ;
unsigned long flags ;
2012-10-02 19:25:46 +04:00
sg_init_one ( & sg , & event_node - > event , sizeof ( struct virtio_scsi_event ) ) ;
2012-07-05 13:06:43 +04:00
spin_lock_irqsave ( & vscsi - > event_vq . vq_lock , flags ) ;
2013-03-20 09:14:28 +04:00
err = virtqueue_add_inbuf ( vscsi - > event_vq . vq , & sg , 1 , event_node ,
GFP_ATOMIC ) ;
2012-10-16 17:26:16 +04:00
if ( ! err )
2012-07-05 13:06:43 +04:00
virtqueue_kick ( vscsi - > event_vq . vq ) ;
spin_unlock_irqrestore ( & vscsi - > event_vq . vq_lock , flags ) ;
2012-10-16 17:26:16 +04:00
return err ;
2012-07-05 13:06:43 +04:00
}
static int virtscsi_kick_event_all ( struct virtio_scsi * vscsi )
{
int i ;
for ( i = 0 ; i < VIRTIO_SCSI_EVENT_LEN ; i + + ) {
vscsi - > event_list [ i ] . vscsi = vscsi ;
virtscsi_kick_event ( vscsi , & vscsi - > event_list [ i ] ) ;
}
return 0 ;
}
static void virtscsi_cancel_event_work ( struct virtio_scsi * vscsi )
{
int i ;
for ( i = 0 ; i < VIRTIO_SCSI_EVENT_LEN ; i + + )
cancel_work_sync ( & vscsi - > event_list [ i ] . work ) ;
}
static void virtscsi_handle_transport_reset ( struct virtio_scsi * vscsi ,
struct virtio_scsi_event * event )
{
struct scsi_device * sdev ;
struct Scsi_Host * shost = virtio_scsi_host ( vscsi - > vdev ) ;
unsigned int target = event - > lun [ 1 ] ;
unsigned int lun = ( event - > lun [ 2 ] < < 8 ) | event - > lun [ 3 ] ;
switch ( event - > reason ) {
case VIRTIO_SCSI_EVT_RESET_RESCAN :
scsi_add_device ( shost , 0 , target , lun ) ;
break ;
case VIRTIO_SCSI_EVT_RESET_REMOVED :
sdev = scsi_device_lookup ( shost , 0 , target , lun ) ;
if ( sdev ) {
scsi_remove_device ( sdev ) ;
scsi_device_put ( sdev ) ;
} else {
pr_err ( " SCSI device %d 0 %d %d not found \n " ,
shost - > host_no , target , lun ) ;
}
break ;
default :
pr_info ( " Unsupport virtio scsi event reason %x \n " , event - > reason ) ;
}
}
2012-10-02 19:25:48 +04:00
static void virtscsi_handle_param_change ( struct virtio_scsi * vscsi ,
struct virtio_scsi_event * event )
{
struct scsi_device * sdev ;
struct Scsi_Host * shost = virtio_scsi_host ( vscsi - > vdev ) ;
unsigned int target = event - > lun [ 1 ] ;
unsigned int lun = ( event - > lun [ 2 ] < < 8 ) | event - > lun [ 3 ] ;
u8 asc = event - > reason & 255 ;
u8 ascq = event - > reason > > 8 ;
sdev = scsi_device_lookup ( shost , 0 , target , lun ) ;
if ( ! sdev ) {
pr_err ( " SCSI device %d 0 %d %d not found \n " ,
shost - > host_no , target , lun ) ;
return ;
}
/* Handle "Parameters changed", "Mode parameters changed", and
" Capacity data has changed " . */
if ( asc = = 0x2a & & ( ascq = = 0x00 | | ascq = = 0x01 | | ascq = = 0x09 ) )
scsi_rescan_device ( & sdev - > sdev_gendev ) ;
scsi_device_put ( sdev ) ;
}
2012-07-05 13:06:43 +04:00
static void virtscsi_handle_event ( struct work_struct * work )
{
struct virtio_scsi_event_node * event_node =
container_of ( work , struct virtio_scsi_event_node , work ) ;
struct virtio_scsi * vscsi = event_node - > vscsi ;
struct virtio_scsi_event * event = & event_node - > event ;
if ( event - > event & VIRTIO_SCSI_T_EVENTS_MISSED ) {
event - > event & = ~ VIRTIO_SCSI_T_EVENTS_MISSED ;
scsi_scan_host ( virtio_scsi_host ( vscsi - > vdev ) ) ;
}
switch ( event - > event ) {
case VIRTIO_SCSI_T_NO_EVENT :
break ;
case VIRTIO_SCSI_T_TRANSPORT_RESET :
virtscsi_handle_transport_reset ( vscsi , event ) ;
break ;
2012-10-02 19:25:48 +04:00
case VIRTIO_SCSI_T_PARAM_CHANGE :
virtscsi_handle_param_change ( vscsi , event ) ;
break ;
2012-07-05 13:06:43 +04:00
default :
pr_err ( " Unsupport virtio scsi event %x \n " , event - > event ) ;
}
virtscsi_kick_event ( vscsi , event_node ) ;
}
2013-04-08 17:31:38 +04:00
static void virtscsi_complete_event ( struct virtio_scsi * vscsi , void * buf )
2012-07-05 13:06:43 +04:00
{
struct virtio_scsi_event_node * event_node = buf ;
INIT_WORK ( & event_node - > work , virtscsi_handle_event ) ;
schedule_work ( & event_node - > work ) ;
}
2012-02-05 15:16:00 +04:00
static void virtscsi_event_done ( struct virtqueue * vq )
{
2012-06-13 18:56:32 +04:00
struct Scsi_Host * sh = virtio_scsi_host ( vq - > vdev ) ;
struct virtio_scsi * vscsi = shost_priv ( sh ) ;
2013-04-08 17:32:07 +04:00
virtscsi_vq_done ( vscsi , & vscsi - > event_vq , virtscsi_complete_event ) ;
2012-02-05 15:16:00 +04:00
} ;
/**
2013-03-20 09:14:28 +04:00
* virtscsi_add_cmd - add a virtio_scsi_cmd to a virtqueue
* @ vq : the struct virtqueue we ' re talking about
2012-02-05 15:16:00 +04:00
* @ cmd : command structure
* @ req_size : size of the request buffer
* @ resp_size : size of the response buffer
2013-03-20 09:14:28 +04:00
* @ gfp : flags to use for memory allocations
2012-02-05 15:16:00 +04:00
*/
2013-03-20 09:14:28 +04:00
static int virtscsi_add_cmd ( struct virtqueue * vq ,
struct virtio_scsi_cmd * cmd ,
size_t req_size , size_t resp_size , gfp_t gfp )
2012-02-05 15:16:00 +04:00
{
struct scsi_cmnd * sc = cmd - > sc ;
2013-03-20 09:14:28 +04:00
struct scatterlist * sgs [ 4 ] , req , resp ;
struct sg_table * out , * in ;
unsigned out_num = 0 , in_num = 0 ;
out = in = NULL ;
if ( sc & & sc - > sc_data_direction ! = DMA_NONE ) {
if ( sc - > sc_data_direction ! = DMA_FROM_DEVICE )
out = & scsi_out ( sc ) - > table ;
if ( sc - > sc_data_direction ! = DMA_TO_DEVICE )
in = & scsi_in ( sc ) - > table ;
}
2012-02-05 15:16:00 +04:00
/* Request header. */
2013-03-20 09:14:28 +04:00
sg_init_one ( & req , & cmd - > req , req_size ) ;
sgs [ out_num + + ] = & req ;
2012-02-05 15:16:00 +04:00
/* Data-out buffer. */
2013-03-20 09:14:28 +04:00
if ( out )
sgs [ out_num + + ] = out - > sgl ;
2012-02-05 15:16:00 +04:00
/* Response header. */
2013-03-20 09:14:28 +04:00
sg_init_one ( & resp , & cmd - > resp , resp_size ) ;
sgs [ out_num + in_num + + ] = & resp ;
2012-02-05 15:16:00 +04:00
/* Data-in buffer */
2013-03-20 09:14:28 +04:00
if ( in )
sgs [ out_num + in_num + + ] = in - > sgl ;
2012-02-05 15:16:00 +04:00
2013-03-20 09:14:28 +04:00
return virtqueue_add_sgs ( vq , sgs , out_num , in_num , cmd , gfp ) ;
2012-02-05 15:16:00 +04:00
}
2013-03-20 09:14:28 +04:00
static int virtscsi_kick_cmd ( struct virtio_scsi_vq * vq ,
2012-02-05 15:16:00 +04:00
struct virtio_scsi_cmd * cmd ,
size_t req_size , size_t resp_size , gfp_t gfp )
{
unsigned long flags ;
2012-10-16 17:26:16 +04:00
int err ;
bool needs_kick = false ;
2012-02-05 15:16:00 +04:00
2013-03-20 09:14:28 +04:00
spin_lock_irqsave ( & vq - > vq_lock , flags ) ;
err = virtscsi_add_cmd ( vq - > vq , cmd , req_size , resp_size , gfp ) ;
2012-10-16 17:26:16 +04:00
if ( ! err )
needs_kick = virtqueue_kick_prepare ( vq - > vq ) ;
2012-06-13 18:56:32 +04:00
2012-06-13 18:56:33 +04:00
spin_unlock_irqrestore ( & vq - > vq_lock , flags ) ;
2012-02-05 15:16:00 +04:00
2012-10-16 17:26:16 +04:00
if ( needs_kick )
2012-06-13 18:56:32 +04:00
virtqueue_notify ( vq - > vq ) ;
2012-10-16 17:26:16 +04:00
return err ;
2012-02-05 15:16:00 +04:00
}
static int virtscsi_queuecommand ( struct Scsi_Host * sh , struct scsi_cmnd * sc )
{
struct virtio_scsi * vscsi = shost_priv ( sh ) ;
struct virtio_scsi_cmd * cmd ;
int ret ;
2012-06-13 18:56:34 +04:00
struct Scsi_Host * shost = virtio_scsi_host ( vscsi - > vdev ) ;
BUG_ON ( scsi_sg_count ( sc ) > shost - > sg_tablesize ) ;
/* TODO: check feature bit and fail if unsupported? */
BUG_ON ( sc - > sc_data_direction = = DMA_BIDIRECTIONAL ) ;
2012-02-05 15:16:00 +04:00
dev_dbg ( & sc - > device - > sdev_gendev ,
" cmd %p CDB: %#02x \n " , sc , sc - > cmnd [ 0 ] ) ;
ret = SCSI_MLQUEUE_HOST_BUSY ;
cmd = mempool_alloc ( virtscsi_cmd_pool , GFP_ATOMIC ) ;
if ( ! cmd )
goto out ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > sc = sc ;
cmd - > req . cmd = ( struct virtio_scsi_cmd_req ) {
. lun [ 0 ] = 1 ,
. lun [ 1 ] = sc - > device - > id ,
. lun [ 2 ] = ( sc - > device - > lun > > 8 ) | 0x40 ,
. lun [ 3 ] = sc - > device - > lun & 0xff ,
. tag = ( unsigned long ) sc ,
. task_attr = VIRTIO_SCSI_S_SIMPLE ,
. prio = 0 ,
. crn = 0 ,
} ;
BUG_ON ( sc - > cmd_len > VIRTIO_SCSI_CDB_SIZE ) ;
memcpy ( cmd - > req . cmd . cdb , sc - > cmnd , sc - > cmd_len ) ;
2013-03-20 09:14:28 +04:00
if ( virtscsi_kick_cmd ( & vscsi - > req_vq , cmd ,
2012-02-05 15:16:00 +04:00
sizeof cmd - > req . cmd , sizeof cmd - > resp . cmd ,
2012-10-16 17:26:16 +04:00
GFP_ATOMIC ) = = 0 )
2012-02-05 15:16:00 +04:00
ret = 0 ;
2012-11-08 13:55:50 +04:00
else
mempool_free ( cmd , virtscsi_cmd_pool ) ;
2012-02-05 15:16:00 +04:00
out :
return ret ;
}
static int virtscsi_tmf ( struct virtio_scsi * vscsi , struct virtio_scsi_cmd * cmd )
{
DECLARE_COMPLETION_ONSTACK ( comp ) ;
2012-05-04 14:32:04 +04:00
int ret = FAILED ;
2012-02-05 15:16:00 +04:00
cmd - > comp = & comp ;
2013-03-20 09:14:28 +04:00
if ( virtscsi_kick_cmd ( & vscsi - > ctrl_vq , cmd ,
2012-05-04 14:32:04 +04:00
sizeof cmd - > req . tmf , sizeof cmd - > resp . tmf ,
GFP_NOIO ) < 0 )
goto out ;
2012-02-05 15:16:00 +04:00
wait_for_completion ( & comp ) ;
2012-05-04 14:32:04 +04:00
if ( cmd - > resp . tmf . response = = VIRTIO_SCSI_S_OK | |
cmd - > resp . tmf . response = = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED )
ret = SUCCESS ;
2012-02-05 15:16:00 +04:00
2012-05-04 14:32:04 +04:00
out :
mempool_free ( cmd , virtscsi_cmd_pool ) ;
return ret ;
2012-02-05 15:16:00 +04:00
}
static int virtscsi_device_reset ( struct scsi_cmnd * sc )
{
struct virtio_scsi * vscsi = shost_priv ( sc - > device - > host ) ;
struct virtio_scsi_cmd * cmd ;
sdev_printk ( KERN_INFO , sc - > device , " device reset \n " ) ;
cmd = mempool_alloc ( virtscsi_cmd_pool , GFP_NOIO ) ;
if ( ! cmd )
return FAILED ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > sc = sc ;
cmd - > req . tmf = ( struct virtio_scsi_ctrl_tmf_req ) {
. type = VIRTIO_SCSI_T_TMF ,
. subtype = VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET ,
. lun [ 0 ] = 1 ,
. lun [ 1 ] = sc - > device - > id ,
. lun [ 2 ] = ( sc - > device - > lun > > 8 ) | 0x40 ,
. lun [ 3 ] = sc - > device - > lun & 0xff ,
} ;
return virtscsi_tmf ( vscsi , cmd ) ;
}
static int virtscsi_abort ( struct scsi_cmnd * sc )
{
struct virtio_scsi * vscsi = shost_priv ( sc - > device - > host ) ;
struct virtio_scsi_cmd * cmd ;
scmd_printk ( KERN_INFO , sc , " abort \n " ) ;
cmd = mempool_alloc ( virtscsi_cmd_pool , GFP_NOIO ) ;
if ( ! cmd )
return FAILED ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > sc = sc ;
cmd - > req . tmf = ( struct virtio_scsi_ctrl_tmf_req ) {
. type = VIRTIO_SCSI_T_TMF ,
. subtype = VIRTIO_SCSI_T_TMF_ABORT_TASK ,
. lun [ 0 ] = 1 ,
. lun [ 1 ] = sc - > device - > id ,
. lun [ 2 ] = ( sc - > device - > lun > > 8 ) | 0x40 ,
. lun [ 3 ] = sc - > device - > lun & 0xff ,
. tag = ( unsigned long ) sc ,
} ;
return virtscsi_tmf ( vscsi , cmd ) ;
}
2013-04-08 17:31:16 +04:00
static int virtscsi_target_alloc ( struct scsi_target * starget )
{
struct virtio_scsi_target_state * tgt =
kmalloc ( sizeof ( * tgt ) , GFP_KERNEL ) ;
if ( ! tgt )
return - ENOMEM ;
spin_lock_init ( & tgt - > tgt_lock ) ;
starget - > hostdata = tgt ;
return 0 ;
}
static void virtscsi_target_destroy ( struct scsi_target * starget )
{
struct virtio_scsi_target_state * tgt = starget - > hostdata ;
kfree ( tgt ) ;
}
2012-02-05 15:16:00 +04:00
static struct scsi_host_template virtscsi_host_template = {
. module = THIS_MODULE ,
. name = " Virtio SCSI HBA " ,
. proc_name = " virtio_scsi " ,
. queuecommand = virtscsi_queuecommand ,
. this_id = - 1 ,
. eh_abort_handler = virtscsi_abort ,
. eh_device_reset_handler = virtscsi_device_reset ,
. can_queue = 1024 ,
. dma_boundary = UINT_MAX ,
. use_clustering = ENABLE_CLUSTERING ,
2013-04-08 17:31:16 +04:00
. target_alloc = virtscsi_target_alloc ,
. target_destroy = virtscsi_target_destroy ,
2012-02-05 15:16:00 +04:00
} ;
# define virtscsi_config_get(vdev, fld) \
( { \
typeof ( ( ( struct virtio_scsi_config * ) 0 ) - > fld ) __val ; \
vdev - > config - > get ( vdev , \
offsetof ( struct virtio_scsi_config , fld ) , \
& __val , sizeof ( __val ) ) ; \
__val ; \
} )
# define virtscsi_config_set(vdev, fld, val) \
( void ) ( { \
typeof ( ( ( struct virtio_scsi_config * ) 0 ) - > fld ) __val = ( val ) ; \
vdev - > config - > set ( vdev , \
offsetof ( struct virtio_scsi_config , fld ) , \
& __val , sizeof ( __val ) ) ; \
} )
2012-06-13 18:56:32 +04:00
static void virtscsi_init_vq ( struct virtio_scsi_vq * virtscsi_vq ,
struct virtqueue * vq )
{
spin_lock_init ( & virtscsi_vq - > vq_lock ) ;
virtscsi_vq - > vq = vq ;
}
2012-07-12 01:22:16 +04:00
static void virtscsi_scan ( struct virtio_device * vdev )
{
struct Scsi_Host * shost = ( struct Scsi_Host * ) vdev - > priv ;
scsi_scan_host ( shost ) ;
}
2012-06-13 18:56:34 +04:00
static void virtscsi_remove_vqs ( struct virtio_device * vdev )
{
/* Stop all the virtqueues. */
vdev - > config - > reset ( vdev ) ;
vdev - > config - > del_vqs ( vdev ) ;
}
2012-02-05 15:16:00 +04:00
static int virtscsi_init ( struct virtio_device * vdev ,
2013-04-08 17:31:16 +04:00
struct virtio_scsi * vscsi )
2012-02-05 15:16:00 +04:00
{
int err ;
struct virtqueue * vqs [ 3 ] ;
2012-06-13 18:56:34 +04:00
2012-02-05 15:16:00 +04:00
vq_callback_t * callbacks [ ] = {
virtscsi_ctrl_done ,
virtscsi_event_done ,
virtscsi_req_done
} ;
const char * names [ ] = {
" control " ,
" event " ,
" request "
} ;
/* Discover virtqueues and write information to configuration. */
err = vdev - > config - > find_vqs ( vdev , 3 , vqs , callbacks , names ) ;
if ( err )
return err ;
2012-06-13 18:56:32 +04:00
virtscsi_init_vq ( & vscsi - > ctrl_vq , vqs [ 0 ] ) ;
virtscsi_init_vq ( & vscsi - > event_vq , vqs [ 1 ] ) ;
virtscsi_init_vq ( & vscsi - > req_vq , vqs [ 2 ] ) ;
2012-02-05 15:16:00 +04:00
virtscsi_config_set ( vdev , cdb_size , VIRTIO_SCSI_CDB_SIZE ) ;
virtscsi_config_set ( vdev , sense_size , VIRTIO_SCSI_SENSE_SIZE ) ;
2012-06-13 18:56:34 +04:00
2012-07-05 13:06:43 +04:00
if ( virtio_has_feature ( vdev , VIRTIO_SCSI_F_HOTPLUG ) )
virtscsi_kick_event_all ( vscsi ) ;
2012-06-13 18:56:34 +04:00
return err ;
2012-02-05 15:16:00 +04:00
}
2012-12-22 01:08:55 +04:00
static int virtscsi_probe ( struct virtio_device * vdev )
2012-02-05 15:16:00 +04:00
{
struct Scsi_Host * shost ;
struct virtio_scsi * vscsi ;
int err ;
2012-06-13 18:56:34 +04:00
u32 sg_elems , num_targets ;
2012-02-05 15:16:00 +04:00
u32 cmd_per_lun ;
2012-06-13 18:56:34 +04:00
num_targets = virtscsi_config_get ( vdev , max_target ) + 1 ;
2012-02-05 15:16:00 +04:00
2013-04-08 17:31:16 +04:00
shost = scsi_host_alloc ( & virtscsi_host_template , sizeof ( * vscsi ) ) ;
2012-02-05 15:16:00 +04:00
if ( ! shost )
return - ENOMEM ;
2012-06-13 18:56:34 +04:00
sg_elems = virtscsi_config_get ( vdev , seg_max ) ? : 1 ;
2012-02-05 15:16:00 +04:00
shost - > sg_tablesize = sg_elems ;
vscsi = shost_priv ( shost ) ;
vscsi - > vdev = vdev ;
vdev - > priv = shost ;
2013-04-08 17:31:16 +04:00
err = virtscsi_init ( vdev , vscsi ) ;
2012-02-05 15:16:00 +04:00
if ( err )
goto virtscsi_init_failed ;
cmd_per_lun = virtscsi_config_get ( vdev , cmd_per_lun ) ? : 1 ;
shost - > cmd_per_lun = min_t ( u32 , cmd_per_lun , shost - > can_queue ) ;
shost - > max_sectors = virtscsi_config_get ( vdev , max_sectors ) ? : 0xFFFF ;
2012-10-02 19:25:47 +04:00
/* LUNs > 256 are reported with format 1, so they go in the range
* 16640 - 32767.
*/
shost - > max_lun = virtscsi_config_get ( vdev , max_lun ) + 1 + 0x4000 ;
2012-06-13 18:56:34 +04:00
shost - > max_id = num_targets ;
2012-02-05 15:16:00 +04:00
shost - > max_channel = 0 ;
shost - > max_cmd_len = VIRTIO_SCSI_CDB_SIZE ;
err = scsi_add_host ( shost , & vdev - > dev ) ;
if ( err )
goto scsi_add_host_failed ;
2012-07-12 01:22:16 +04:00
/*
* scsi_scan_host ( ) happens in virtscsi_scan ( ) via virtio_driver - > scan ( )
* after VIRTIO_CONFIG_S_DRIVER_OK has been set . .
*/
2012-02-05 15:16:00 +04:00
return 0 ;
scsi_add_host_failed :
vdev - > config - > del_vqs ( vdev ) ;
virtscsi_init_failed :
scsi_host_put ( shost ) ;
return err ;
}
2012-12-22 01:08:55 +04:00
static void virtscsi_remove ( struct virtio_device * vdev )
2012-02-05 15:16:00 +04:00
{
struct Scsi_Host * shost = virtio_scsi_host ( vdev ) ;
2012-07-05 13:06:43 +04:00
struct virtio_scsi * vscsi = shost_priv ( shost ) ;
if ( virtio_has_feature ( vdev , VIRTIO_SCSI_F_HOTPLUG ) )
virtscsi_cancel_event_work ( vscsi ) ;
2012-02-05 15:16:00 +04:00
scsi_remove_host ( shost ) ;
virtscsi_remove_vqs ( vdev ) ;
scsi_host_put ( shost ) ;
}
# ifdef CONFIG_PM
static int virtscsi_freeze ( struct virtio_device * vdev )
{
virtscsi_remove_vqs ( vdev ) ;
return 0 ;
}
static int virtscsi_restore ( struct virtio_device * vdev )
{
struct Scsi_Host * sh = virtio_scsi_host ( vdev ) ;
struct virtio_scsi * vscsi = shost_priv ( sh ) ;
2013-04-08 17:31:16 +04:00
return virtscsi_init ( vdev , vscsi ) ;
2012-02-05 15:16:00 +04:00
}
# endif
static struct virtio_device_id id_table [ ] = {
{ VIRTIO_ID_SCSI , VIRTIO_DEV_ANY_ID } ,
{ 0 } ,
} ;
2012-07-05 13:06:43 +04:00
static unsigned int features [ ] = {
2012-10-02 19:25:48 +04:00
VIRTIO_SCSI_F_HOTPLUG ,
VIRTIO_SCSI_F_CHANGE ,
2012-07-05 13:06:43 +04:00
} ;
2012-02-05 15:16:00 +04:00
static struct virtio_driver virtio_scsi_driver = {
2012-07-05 13:06:43 +04:00
. feature_table = features ,
. feature_table_size = ARRAY_SIZE ( features ) ,
2012-02-05 15:16:00 +04:00
. driver . name = KBUILD_MODNAME ,
. driver . owner = THIS_MODULE ,
. id_table = id_table ,
. probe = virtscsi_probe ,
2012-07-12 01:22:16 +04:00
. scan = virtscsi_scan ,
2012-02-05 15:16:00 +04:00
# ifdef CONFIG_PM
. freeze = virtscsi_freeze ,
. restore = virtscsi_restore ,
# endif
2012-12-22 01:08:55 +04:00
. remove = virtscsi_remove ,
2012-02-05 15:16:00 +04:00
} ;
static int __init init ( void )
{
int ret = - ENOMEM ;
virtscsi_cmd_cache = KMEM_CACHE ( virtio_scsi_cmd , 0 ) ;
if ( ! virtscsi_cmd_cache ) {
2013-03-12 09:04:40 +04:00
pr_err ( " kmem_cache_create() for virtscsi_cmd_cache failed \n " ) ;
2012-02-05 15:16:00 +04:00
goto error ;
}
virtscsi_cmd_pool =
mempool_create_slab_pool ( VIRTIO_SCSI_MEMPOOL_SZ ,
virtscsi_cmd_cache ) ;
if ( ! virtscsi_cmd_pool ) {
2013-03-12 09:04:40 +04:00
pr_err ( " mempool_create() for virtscsi_cmd_pool failed \n " ) ;
2012-02-05 15:16:00 +04:00
goto error ;
}
ret = register_virtio_driver ( & virtio_scsi_driver ) ;
if ( ret < 0 )
goto error ;
return 0 ;
error :
if ( virtscsi_cmd_pool ) {
mempool_destroy ( virtscsi_cmd_pool ) ;
virtscsi_cmd_pool = NULL ;
}
if ( virtscsi_cmd_cache ) {
kmem_cache_destroy ( virtscsi_cmd_cache ) ;
virtscsi_cmd_cache = NULL ;
}
return ret ;
}
static void __exit fini ( void )
{
unregister_virtio_driver ( & virtio_scsi_driver ) ;
mempool_destroy ( virtscsi_cmd_pool ) ;
kmem_cache_destroy ( virtscsi_cmd_cache ) ;
}
module_init ( init ) ;
module_exit ( fini ) ;
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_DESCRIPTION ( " Virtio SCSI HBA driver " ) ;
MODULE_LICENSE ( " GPL " ) ;