2006-11-16 13:24:16 +03:00
/*
* SCSI target kernel / user interface functions
*
* Copyright ( C ) 2005 FUJITA Tomonori < tomof @ acm . org >
* Copyright ( C ) 2005 Mike Christie < michaelc @ cs . wisc . edu >
*
* 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 <linux/miscdevice.h>
# include <linux/file.h>
2008-05-20 21:16:42 +04:00
# include <linux/smp_lock.h>
2006-11-16 13:24:16 +03:00
# include <net/tcp.h>
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_tgt.h>
# include <scsi/scsi_tgt_if.h>
2006-12-05 10:06:48 +03:00
# include <asm/cacheflush.h>
2006-11-16 13:24:16 +03:00
# include "scsi_tgt_priv.h"
2007-02-13 17:21:52 +03:00
# 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)
2006-11-16 13:24:16 +03:00
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 ;
mb ( ) ;
ev - > hdr . status = 1 ;
flush_dcache_page ( virt_to_page ( ev ) ) ;
wake_up_interruptible ( & tgt_poll_wait ) ;
return 0 ;
}
2007-07-11 10:08:17 +04:00
int scsi_tgt_uspace_send_cmd ( struct scsi_cmnd * cmd , u64 itn_id ,
struct scsi_lun * lun , u64 tag )
2006-11-16 13:24:16 +03:00
{
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 ;
2007-07-11 10:08:17 +04:00
ev . p . cmd_req . itn_id = itn_id ;
2007-10-24 20:21:30 +04:00
ev . p . cmd_req . data_len = scsi_bufflen ( cmd ) ;
2006-11-16 13:24:16 +03:00
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 ;
dprintk ( " %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 )
eprintk ( " tx buf is full, could not send \n " ) ;
return err ;
}
2007-07-11 10:08:17 +04:00
int scsi_tgt_uspace_send_status ( struct scsi_cmnd * cmd , u64 itn_id , u64 tag )
2006-11-16 13:24:16 +03:00
{
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 ;
2007-07-11 10:08:17 +04:00
ev . p . cmd_done . itn_id = itn_id ;
2006-11-16 13:24:16 +03:00
ev . p . cmd_done . tag = tag ;
ev . p . cmd_done . result = cmd - > result ;
dprintk ( " %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 )
eprintk ( " tx buf is full, could not send \n " ) ;
return err ;
}
2007-07-11 10:08:17 +04:00
int scsi_tgt_uspace_send_tsk_mgmt ( int host_no , u64 itn_id , int function ,
u64 tag , struct scsi_lun * scsilun , void * data )
2006-11-16 13:24:16 +03:00
{
struct tgt_event ev ;
int err ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
ev . p . tsk_mgmt_req . host_no = host_no ;
2007-07-11 10:08:17 +04:00
ev . p . tsk_mgmt_req . itn_id = itn_id ;
2006-11-16 13:24:16 +03:00
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 ;
dprintk ( " %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 )
eprintk ( " tx buf is full, could not send \n " ) ;
return err ;
}
2007-07-11 10:08:17 +04:00
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 ) ) ;
dprintk ( " %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 )
eprintk ( " tx buf is full, could not send \n " ) ;
return err ;
}
2006-11-16 13:24:16 +03:00
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 ,
2007-07-11 10:08:17 +04:00
ev - > p . cmd_rsp . itn_id ,
2006-11-16 13:24:16 +03:00
ev - > p . cmd_rsp . result ,
2007-03-03 03:55:54 +03:00
ev - > p . cmd_rsp . tag ,
2006-11-16 13:24:16 +03:00
ev - > p . cmd_rsp . uaddr ,
2007-03-03 03:55:54 +03:00
ev - > p . cmd_rsp . len ,
ev - > p . cmd_rsp . sense_uaddr ,
ev - > p . cmd_rsp . sense_len ,
2006-11-16 13:24:16 +03:00
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 ,
2007-07-11 10:08:17 +04:00
ev - > p . tsk_mgmt_rsp . itn_id ,
2006-11-16 13:24:16 +03:00
ev - > p . tsk_mgmt_rsp . mid ,
ev - > p . tsk_mgmt_rsp . result ) ;
break ;
2007-07-11 10:08:17 +04:00
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 ;
2006-11-16 13:24:16 +03:00
default :
eprintk ( " 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 ;
for ( i = 0 ; i < TGT_RING_PAGES ; i + + ) {
struct page * 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 ) {
eprintk ( " 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 ;
2008-05-20 21:16:42 +04:00
cycle_kernel_lock ( ) ;
2006-11-16 13:24:16 +03:00
return 0 ;
}
2007-02-12 11:55:34 +03:00
static const struct file_operations tgt_fops = {
2006-11-16 13:24:16 +03:00
. owner = THIS_MODULE ,
. open = tgt_open ,
. poll = tgt_poll ,
. write = tgt_write ,
. mmap = tgt_mmap ,
} ;
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 ] ) {
eprintk ( " 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 ;
}