2007-10-23 22:47:31 +04:00
/*
2008-10-14 03:45:23 +04:00
* The Virtio 9 p transport driver
2007-10-23 22:47:31 +04:00
*
2008-02-07 04:25:58 +03:00
* This is a block based transport driver based on the lguest block driver
* code .
2007-10-23 22:47:31 +04:00
*
2008-10-14 03:45:23 +04:00
* Copyright ( C ) 2007 , 2008 Eric Van Hensbergen , IBM Corporation
2007-10-23 22:47:31 +04:00
*
* Based on virtio console driver
* Copyright ( C ) 2006 , 2007 Rusty Russell , IBM Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* 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 :
* Free Software Foundation
* 51 Franklin Street , Fifth Floor
* Boston , MA 02111 - 1301 USA
*
*/
# include <linux/in.h>
# include <linux/module.h>
# include <linux/net.h>
# include <linux/ipv6.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/un.h>
# include <linux/uaccess.h>
# include <linux/inet.h>
# include <linux/idr.h>
# include <linux/file.h>
# include <net/9p/9p.h>
# include <linux/parser.h>
2008-10-14 03:45:25 +04:00
# include <net/9p/client.h>
2007-10-23 22:47:31 +04:00
# include <net/9p/transport.h>
# include <linux/scatterlist.h>
# include <linux/virtio.h>
# include <linux/virtio_9p.h>
2008-02-07 04:25:58 +03:00
# define VIRTQUEUE_NUM 128
2007-10-23 22:47:31 +04:00
/* a single mutex to manage channel initialization and attachment */
2008-03-07 20:39:13 +03:00
static DEFINE_MUTEX ( virtio_9p_lock ) ;
2007-10-23 22:47:31 +04:00
2008-03-05 16:08:09 +03:00
/**
* struct virtio_chan - per - instance transport information
* @ initialized : whether the channel is initialized
* @ inuse : whether the channel is in use
* @ lock : protects multiple elements within this structure
2009-07-19 23:41:55 +04:00
* @ client : client instance
2008-03-05 16:08:09 +03:00
* @ vdev : virtio dev associated with this channel
* @ vq : virtio queue associated with this channel
* @ sg : scatter gather list which is used to pack a request ( protected ? )
*
* We keep all per - channel information in a structure .
2007-10-23 22:47:31 +04:00
* This structure is allocated within the devices dev - > mem space .
* A pointer to the structure will get put in the transport private .
2008-03-05 16:08:09 +03:00
*
2007-10-23 22:47:31 +04:00
*/
2008-03-05 16:08:09 +03:00
2010-02-15 20:27:01 +03:00
struct virtio_chan {
2008-03-05 16:08:09 +03:00
bool inuse ;
2007-10-23 22:47:31 +04:00
2008-02-07 04:25:58 +03:00
spinlock_t lock ;
2008-10-14 03:45:23 +04:00
struct p9_client * client ;
2007-10-23 22:47:31 +04:00
struct virtio_device * vdev ;
2008-02-07 04:25:58 +03:00
struct virtqueue * vq ;
2007-10-23 22:47:31 +04:00
2008-02-07 04:25:58 +03:00
/* Scatterlist: can be too big for stack. */
struct scatterlist sg [ VIRTQUEUE_NUM ] ;
2010-02-15 20:27:01 +03:00
struct list_head chan_list ;
} ;
static struct list_head virtio_chan_list ;
2007-10-23 22:47:31 +04:00
/* How many bytes left in this page. */
static unsigned int rest_of_page ( void * data )
{
return PAGE_SIZE - ( ( unsigned long ) data % PAGE_SIZE ) ;
}
2008-03-05 16:08:09 +03:00
/**
* p9_virtio_close - reclaim resources of a channel
2009-07-19 23:41:55 +04:00
* @ client : client instance
2008-03-05 16:08:09 +03:00
*
* This reclaims a channel by freeing its resources and
* reseting its inuse flag .
*
*/
2008-10-14 03:45:25 +04:00
static void p9_virtio_close ( struct p9_client * client )
2008-02-07 04:25:58 +03:00
{
2008-10-14 03:45:25 +04:00
struct virtio_chan * chan = client - > trans ;
2007-10-23 22:47:31 +04:00
2008-03-07 20:39:13 +03:00
mutex_lock ( & virtio_9p_lock ) ;
2010-02-08 14:50:32 +03:00
if ( chan )
chan - > inuse = false ;
2008-03-07 20:39:13 +03:00
mutex_unlock ( & virtio_9p_lock ) ;
2007-10-23 22:47:31 +04:00
}
2008-03-05 16:08:09 +03:00
/**
* req_done - callback which signals activity from the server
* @ vq : virtio queue activity was received on
*
* This notifies us that the server has triggered some activity
* on the virtio channel - most likely a response to request we
* sent . Figure out which requests now have responses and wake up
* those threads .
*
* Bugs : could do with some additional sanity checking , but appears to work .
*
*/
2008-02-07 04:25:58 +03:00
static void req_done ( struct virtqueue * vq )
2007-10-23 22:47:31 +04:00
{
2008-02-07 04:25:58 +03:00
struct virtio_chan * chan = vq - > vdev - > priv ;
struct p9_fcall * rc ;
unsigned int len ;
struct p9_req_t * req ;
2008-10-14 03:45:21 +04:00
P9_DPRINTK ( P9_DEBUG_TRANS , " : request done \n " ) ;
2008-02-07 04:25:58 +03:00
while ( ( rc = chan - > vq - > vq_ops - > get_buf ( chan - > vq , & len ) ) ! = NULL ) {
2008-10-14 03:45:21 +04:00
P9_DPRINTK ( P9_DEBUG_TRANS , " : rc %p \n " , rc ) ;
P9_DPRINTK ( P9_DEBUG_TRANS , " : lookup tag %d \n " , rc - > tag ) ;
2008-10-14 03:45:23 +04:00
req = p9_tag_lookup ( chan - > client , rc - > tag ) ;
2009-04-06 01:28:59 +04:00
req - > status = REQ_STATUS_RCVD ;
2008-10-14 03:45:21 +04:00
p9_client_cb ( chan - > client , req ) ;
2008-02-07 04:25:58 +03:00
}
}
2007-10-23 22:47:31 +04:00
2008-03-05 16:08:09 +03:00
/**
* pack_sg_list - pack a scatter gather list from a linear buffer
* @ sg : scatter / gather list to pack into
* @ start : which segment of the sg_list to start at
* @ limit : maximum segment to pack data to
* @ data : data to pack into scatter / gather list
* @ count : amount of data to pack into the scatter / gather list
*
* sg_lists have multiple segments of various sizes . This will pack
* arbitrary data into an existing scatter gather list , segmenting the
* data as necessary within constraints .
*
*/
2008-02-07 04:25:58 +03:00
static int
pack_sg_list ( struct scatterlist * sg , int start , int limit , char * data ,
int count )
{
int s ;
int index = start ;
while ( count ) {
s = rest_of_page ( data ) ;
if ( s > count )
s = count ;
sg_set_buf ( & sg [ index + + ] , data , s ) ;
count - = s ;
data + = s ;
2008-02-18 05:42:53 +03:00
BUG_ON ( index > limit ) ;
2008-02-07 04:25:58 +03:00
}
2007-10-23 22:47:31 +04:00
2008-02-07 04:25:58 +03:00
return index - start ;
2007-10-23 22:47:31 +04:00
}
2008-10-14 03:45:21 +04:00
/* We don't currently allow canceling of virtio requests */
static int p9_virtio_cancel ( struct p9_client * client , struct p9_req_t * req )
{
return 1 ;
}
2008-03-05 16:08:09 +03:00
/**
2008-10-14 03:45:21 +04:00
* p9_virtio_request - issue a request
2009-07-19 23:41:55 +04:00
* @ client : client instance issuing the request
* @ req : request to be issued
2008-03-05 16:08:09 +03:00
*
*/
2008-02-07 04:25:58 +03:00
static int
2008-10-14 03:45:21 +04:00
p9_virtio_request ( struct p9_client * client , struct p9_req_t * req )
2007-10-23 22:47:31 +04:00
{
2008-02-07 04:25:58 +03:00
int in , out ;
2008-10-14 03:45:21 +04:00
struct virtio_chan * chan = client - > trans ;
char * rdata = ( char * ) req - > rc + sizeof ( struct p9_fcall ) ;
2007-10-23 22:47:31 +04:00
2008-10-14 03:45:21 +04:00
P9_DPRINTK ( P9_DEBUG_TRANS , " 9p debug: virtio request \n " ) ;
2007-10-23 22:47:31 +04:00
2008-10-14 03:45:21 +04:00
out = pack_sg_list ( chan - > sg , 0 , VIRTQUEUE_NUM , req - > tc - > sdata ,
req - > tc - > size ) ;
in = pack_sg_list ( chan - > sg , out , VIRTQUEUE_NUM - out , rdata ,
client - > msize ) ;
2007-10-23 22:47:31 +04:00
2008-02-07 04:25:58 +03:00
req - > status = REQ_STATUS_SENT ;
2007-10-23 22:47:31 +04:00
2009-09-24 08:26:31 +04:00
if ( chan - > vq - > vq_ops - > add_buf ( chan - > vq , chan - > sg , out , in , req - > tc ) < 0 ) {
2008-02-07 04:25:58 +03:00
P9_DPRINTK ( P9_DEBUG_TRANS ,
" 9p debug: virtio rpc add_buf returned failure " ) ;
return - EIO ;
}
2007-10-23 22:47:31 +04:00
2008-02-07 04:25:58 +03:00
chan - > vq - > vq_ops - > kick ( chan - > vq ) ;
2007-10-23 22:47:31 +04:00
2008-10-14 03:45:21 +04:00
P9_DPRINTK ( P9_DEBUG_TRANS , " 9p debug: virtio request kicked \n " ) ;
2008-02-07 04:25:58 +03:00
return 0 ;
2007-10-23 22:47:31 +04:00
}
2008-03-05 16:08:09 +03:00
/**
* p9_virtio_probe - probe for existence of 9 P virtio channels
* @ vdev : virtio device to probe
*
2010-02-15 20:27:01 +03:00
* This probes for existing virtio channels .
2008-03-05 16:08:09 +03:00
*
*/
2008-02-07 04:25:58 +03:00
static int p9_virtio_probe ( struct virtio_device * vdev )
2007-10-23 22:47:31 +04:00
{
int err ;
struct virtio_chan * chan ;
2010-02-15 20:27:01 +03:00
chan = kmalloc ( sizeof ( struct virtio_chan ) , GFP_KERNEL ) ;
if ( ! chan ) {
printk ( KERN_ERR " 9p: Failed to allocate virtio 9P channel \n " ) ;
2007-10-23 22:47:31 +04:00
err = - ENOMEM ;
goto fail ;
}
2008-02-07 04:25:58 +03:00
chan - > vdev = vdev ;
2007-10-23 22:47:31 +04:00
2008-02-07 04:25:58 +03:00
/* We expect one virtqueue, for requests. */
2009-06-13 08:16:36 +04:00
chan - > vq = virtio_find_single_vq ( vdev , req_done , " requests " ) ;
2008-02-07 04:25:58 +03:00
if ( IS_ERR ( chan - > vq ) ) {
err = PTR_ERR ( chan - > vq ) ;
goto out_free_vq ;
2007-10-23 22:47:31 +04:00
}
2008-02-07 04:25:58 +03:00
chan - > vq - > vdev - > priv = chan ;
spin_lock_init ( & chan - > lock ) ;
2007-10-23 22:47:31 +04:00
2008-02-07 04:25:58 +03:00
sg_init_table ( chan - > sg , VIRTQUEUE_NUM ) ;
2007-10-23 22:47:31 +04:00
chan - > inuse = false ;
2010-02-15 20:27:01 +03:00
mutex_lock ( & virtio_9p_lock ) ;
list_add_tail ( & chan - > chan_list , & virtio_chan_list ) ;
mutex_unlock ( & virtio_9p_lock ) ;
2007-10-23 22:47:31 +04:00
return 0 ;
2008-02-07 04:25:58 +03:00
out_free_vq :
2009-06-13 08:16:36 +04:00
vdev - > config - > del_vqs ( vdev ) ;
2010-02-15 20:27:01 +03:00
kfree ( chan ) ;
2007-10-23 22:47:31 +04:00
fail :
return err ;
}
2008-03-05 16:08:09 +03:00
/**
* p9_virtio_create - allocate a new virtio channel
2008-10-14 03:45:25 +04:00
* @ client : client instance invoking this transport
2008-03-05 16:08:09 +03:00
* @ devname : string identifying the channel to connect to ( unused )
* @ args : args passed from sys_mount ( ) for per - transport options ( unused )
*
* This sets up a transport channel for 9 p communication . Right now
2007-10-23 22:47:31 +04:00
* we only match the first available channel , but eventually we couldlook up
* alternate channels by matching devname versus a virtio_config entry .
* We use a simple reference count mechanism to ensure that only a single
2008-03-05 16:08:09 +03:00
* mount has a channel open at a time .
*
*/
2008-10-14 03:45:25 +04:00
static int
p9_virtio_create ( struct p9_client * client , const char * devname , char * args )
2007-10-23 22:47:31 +04:00
{
2010-02-15 20:27:01 +03:00
struct virtio_chan * chan ;
int found = 0 ;
2007-10-23 22:47:31 +04:00
2008-03-07 20:39:13 +03:00
mutex_lock ( & virtio_9p_lock ) ;
2010-02-15 20:27:01 +03:00
list_for_each_entry ( chan , & virtio_chan_list , chan_list ) {
if ( ! strcmp ( devname , dev_name ( & chan - > vdev - > dev ) ) ) {
2010-02-15 20:27:00 +03:00
if ( ! chan - > inuse ) {
chan - > inuse = true ;
2010-02-15 20:27:01 +03:00
found = 1 ;
2010-02-15 20:27:00 +03:00
break ;
}
2007-10-23 22:47:31 +04:00
}
}
2008-03-07 20:39:13 +03:00
mutex_unlock ( & virtio_9p_lock ) ;
2007-10-23 22:47:31 +04:00
2010-02-15 20:27:01 +03:00
if ( ! found ) {
2008-02-07 04:25:58 +03:00
printk ( KERN_ERR " 9p: no channels available \n " ) ;
2008-10-14 03:45:25 +04:00
return - ENODEV ;
2008-02-07 04:25:58 +03:00
}
2008-10-14 03:45:25 +04:00
client - > trans = ( void * ) chan ;
2010-01-16 03:54:03 +03:00
client - > status = Connected ;
2008-10-14 03:45:23 +04:00
chan - > client = client ;
2007-10-23 22:47:31 +04:00
2008-10-14 03:45:25 +04:00
return 0 ;
2007-10-23 22:47:31 +04:00
}
2008-03-05 16:08:09 +03:00
/**
* p9_virtio_remove - clean up resources associated with a virtio device
* @ vdev : virtio device to remove
*
*/
2008-02-07 04:25:04 +03:00
static void p9_virtio_remove ( struct virtio_device * vdev )
{
struct virtio_chan * chan = vdev - > priv ;
BUG_ON ( chan - > inuse ) ;
2010-02-15 20:27:01 +03:00
vdev - > config - > del_vqs ( vdev ) ;
mutex_lock ( & virtio_9p_lock ) ;
list_del ( & chan - > chan_list ) ;
mutex_unlock ( & virtio_9p_lock ) ;
kfree ( chan ) ;
2008-02-07 04:25:04 +03:00
}
2007-10-23 22:47:31 +04:00
static struct virtio_device_id id_table [ ] = {
{ VIRTIO_ID_9P , VIRTIO_DEV_ANY_ID } ,
{ 0 } ,
} ;
/* The standard "struct lguest_driver": */
static struct virtio_driver p9_virtio_drv = {
. driver . name = KBUILD_MODNAME ,
. driver . owner = THIS_MODULE ,
. id_table = id_table ,
. probe = p9_virtio_probe ,
2008-02-07 04:25:04 +03:00
. remove = p9_virtio_remove ,
2007-10-23 22:47:31 +04:00
} ;
static struct p9_trans_module p9_virtio_trans = {
. name = " virtio " ,
. create = p9_virtio_create ,
2008-10-14 03:45:25 +04:00
. close = p9_virtio_close ,
2008-10-14 03:45:21 +04:00
. request = p9_virtio_request ,
. cancel = p9_virtio_cancel ,
2008-02-07 04:25:58 +03:00
. maxsize = PAGE_SIZE * 16 ,
2007-10-23 22:47:31 +04:00
. def = 0 ,
2008-09-25 01:22:23 +04:00
. owner = THIS_MODULE ,
2007-10-23 22:47:31 +04:00
} ;
/* The standard init function */
static int __init p9_virtio_init ( void )
{
2010-02-15 20:27:01 +03:00
INIT_LIST_HEAD ( & virtio_chan_list ) ;
2007-10-23 22:47:31 +04:00
v9fs_register_trans ( & p9_virtio_trans ) ;
return register_virtio_driver ( & p9_virtio_drv ) ;
}
2008-02-07 04:25:04 +03:00
static void __exit p9_virtio_cleanup ( void )
{
unregister_virtio_driver ( & p9_virtio_drv ) ;
2008-09-25 01:22:23 +04:00
v9fs_unregister_trans ( & p9_virtio_trans ) ;
2008-02-07 04:25:04 +03:00
}
2007-10-23 22:47:31 +04:00
module_init ( p9_virtio_init ) ;
2008-02-07 04:25:04 +03:00
module_exit ( p9_virtio_cleanup ) ;
2007-10-23 22:47:31 +04:00
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_AUTHOR ( " Eric Van Hensbergen <ericvh@gmail.com> " ) ;
MODULE_DESCRIPTION ( " Virtio 9p Transport " ) ;
MODULE_LICENSE ( " GPL " ) ;