2007-10-23 13:47:31 -05:00
/*
* The Guest 9 p transport driver
*
2008-02-06 19:25:58 -06:00
* This is a block based transport driver based on the lguest block driver
* code .
2007-10-23 13:47:31 -05:00
*
*/
/*
* Copyright ( C ) 2007 Eric Van Hensbergen , IBM Corporation
*
* 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>
# include <net/9p/transport.h>
# include <linux/scatterlist.h>
# include <linux/virtio.h>
# include <linux/virtio_9p.h>
2008-02-06 19:25:58 -06:00
# define VIRTQUEUE_NUM 128
2007-10-23 13:47:31 -05:00
/* a single mutex to manage channel initialization and attachment */
2008-03-07 11:39:13 -06:00
static DEFINE_MUTEX ( virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
/* global which tracks highest initialized channel */
static int chan_index ;
2008-02-06 19:25:58 -06:00
# define P9_INIT_MAXTAG 16
2008-03-05 07:08:09 -06:00
/**
* enum p9_req_status_t - virtio request status
* @ REQ_STATUS_IDLE : request slot unused
* @ REQ_STATUS_SENT : request sent to server
* @ REQ_STATUS_RCVD : response received from server
* @ REQ_STATUS_FLSH : request has been flushed
*
* The @ REQ_STATUS_IDLE state is used to mark a request slot as unused
* but use is actually tracked by the idpool structure which handles tag
* id allocation .
*
*/
enum p9_req_status_t {
REQ_STATUS_IDLE ,
REQ_STATUS_SENT ,
REQ_STATUS_RCVD ,
REQ_STATUS_FLSH ,
} ;
/**
* struct p9_req_t - virtio request slots
* @ status : status of this request slot
* @ wq : wait_queue for the client to block on for this request
*
* The virtio transport uses an array to track outstanding requests
* instead of a list . While this may incurr overhead during initial
* allocation or expansion , it makes request lookup much easier as the
* tag id is a index into an array . ( We use tag + 1 so that we can accomodate
* the - 1 tag for the T_VERSION request ) .
* This also has the nice effect of only having to allocate wait_queues
* once , instead of constantly allocating and freeing them . Its possible
* other resources could benefit from this scheme as well .
*
*/
2008-02-06 19:25:58 -06:00
struct p9_req_t {
int status ;
wait_queue_head_t * wq ;
} ;
2008-03-05 07:08:09 -06: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
* @ vdev : virtio dev associated with this channel
* @ vq : virtio queue associated with this channel
* @ tagpool : accounting for tag ids ( and request slots )
* @ reqs : array of request slots
* @ max_tag : current number of request_slots allocated
* @ sg : scatter gather list which is used to pack a request ( protected ? )
*
* We keep all per - channel information in a structure .
2007-10-23 13:47:31 -05: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 07:08:09 -06:00
*
2007-10-23 13:47:31 -05:00
*/
2008-03-05 07:08:09 -06:00
2007-10-23 13:47:31 -05:00
static struct virtio_chan {
2008-03-05 07:08:09 -06:00
bool initialized ;
bool inuse ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
spinlock_t lock ;
2007-10-23 13:47:31 -05:00
struct virtio_device * vdev ;
2008-02-06 19:25:58 -06:00
struct virtqueue * vq ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
struct p9_idpool * tagpool ;
struct p9_req_t * reqs ;
int max_tag ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
/* Scatterlist: can be too big for stack. */
struct scatterlist sg [ VIRTQUEUE_NUM ] ;
2007-10-23 13:47:31 -05:00
} channels [ MAX_9P_CHAN ] ;
2008-03-05 07:08:09 -06:00
/**
* p9_lookup_tag - Lookup requests by tag
* @ c : virtio channel to lookup tag within
* @ tag : numeric id for transaction
*
* this is a simple array lookup , but will grow the
* request_slots as necessary to accomodate transaction
* ids which did not previously have a slot .
*
* Bugs : there is currently no upper limit on request slots set
* here , but that should be constrained by the id accounting .
*/
2008-02-06 19:25:58 -06:00
static struct p9_req_t * p9_lookup_tag ( struct virtio_chan * c , u16 tag )
{
/* This looks up the original request by tag so we know which
* buffer to read the data into */
tag + + ;
while ( tag > = c - > max_tag ) {
int old_max = c - > max_tag ;
int count ;
if ( c - > max_tag )
c - > max_tag * = 2 ;
else
c - > max_tag = P9_INIT_MAXTAG ;
c - > reqs = krealloc ( c - > reqs , sizeof ( struct p9_req_t ) * c - > max_tag ,
GFP_ATOMIC ) ;
if ( ! c - > reqs ) {
printk ( KERN_ERR " Couldn't grow tag array \n " ) ;
BUG ( ) ;
}
for ( count = old_max ; count < c - > max_tag ; count + + ) {
c - > reqs [ count ] . status = REQ_STATUS_IDLE ;
2008-02-19 16:25:30 -08:00
c - > reqs [ count ] . wq = kmalloc ( sizeof ( wait_queue_head_t ) ,
2008-02-06 19:25:58 -06:00
GFP_ATOMIC ) ;
if ( ! c - > reqs [ count ] . wq ) {
printk ( KERN_ERR " Couldn't grow tag array \n " ) ;
BUG ( ) ;
}
init_waitqueue_head ( c - > reqs [ count ] . wq ) ;
}
}
return & c - > reqs [ tag ] ;
}
2007-10-23 13:47:31 -05: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 07:08:09 -06:00
/**
* p9_virtio_close - reclaim resources of a channel
* @ trans : transport state
*
* This reclaims a channel by freeing its resources and
* reseting its inuse flag .
*
*/
2008-02-06 19:25:58 -06:00
static void p9_virtio_close ( struct p9_trans * trans )
{
struct virtio_chan * chan = trans - > priv ;
int count ;
2008-05-03 17:29:50 -05:00
unsigned long flags ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
spin_lock_irqsave ( & chan - > lock , flags ) ;
p9_idpool_destroy ( chan - > tagpool ) ;
for ( count = 0 ; count < chan - > max_tag ; count + + )
kfree ( chan - > reqs [ count ] . wq ) ;
kfree ( chan - > reqs ) ;
chan - > max_tag = 0 ;
spin_unlock_irqrestore ( & chan - > lock , flags ) ;
2007-10-23 13:47:31 -05:00
2008-03-07 11:39:13 -06:00
mutex_lock ( & virtio_9p_lock ) ;
2008-02-06 19:25:58 -06:00
chan - > inuse = false ;
2008-03-07 11:39:13 -06:00
mutex_unlock ( & virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
kfree ( trans ) ;
2007-10-23 13:47:31 -05:00
}
2008-03-05 07:08:09 -06: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-06 19:25:58 -06:00
static void req_done ( struct virtqueue * vq )
2007-10-23 13:47:31 -05:00
{
2008-02-06 19:25:58 -06:00
struct virtio_chan * chan = vq - > vdev - > priv ;
struct p9_fcall * rc ;
unsigned int len ;
unsigned long flags ;
struct p9_req_t * req ;
spin_lock_irqsave ( & chan - > lock , flags ) ;
while ( ( rc = chan - > vq - > vq_ops - > get_buf ( chan - > vq , & len ) ) ! = NULL ) {
req = p9_lookup_tag ( chan , rc - > tag ) ;
req - > status = REQ_STATUS_RCVD ;
wake_up ( req - > wq ) ;
}
/* In case queue is stopped waiting for more buffers. */
spin_unlock_irqrestore ( & chan - > lock , flags ) ;
}
2007-10-23 13:47:31 -05:00
2008-03-05 07:08:09 -06: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-06 19:25:58 -06: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-17 18:42:53 -08:00
BUG_ON ( index > limit ) ;
2008-02-06 19:25:58 -06:00
}
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
return index - start ;
2007-10-23 13:47:31 -05:00
}
2008-03-05 07:08:09 -06:00
/**
* p9_virtio_rpc - issue a request and wait for a response
* @ t : transport state
* @ tc : & p9_fcall request to transmit
* @ rc : & p9_fcall to put reponse into
*
*/
2008-02-06 19:25:58 -06:00
static int
2008-02-06 19:25:03 -06:00
p9_virtio_rpc ( struct p9_trans * t , struct p9_fcall * tc , struct p9_fcall * * rc )
2007-10-23 13:47:31 -05:00
{
2008-02-06 19:25:58 -06:00
int in , out ;
int n , err , size ;
struct virtio_chan * chan = t - > priv ;
char * rdata ;
struct p9_req_t * req ;
unsigned long flags ;
if ( * rc = = NULL ) {
2008-02-06 19:25:03 -06:00
* rc = kmalloc ( sizeof ( struct p9_fcall ) + t - > msize , GFP_KERNEL ) ;
2008-02-06 19:25:58 -06:00
if ( ! * rc )
return - ENOMEM ;
}
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
rdata = ( char * ) * rc + sizeof ( struct p9_fcall ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
n = P9_NOTAG ;
if ( tc - > id ! = P9_TVERSION ) {
n = p9_idpool_get ( chan - > tagpool ) ;
if ( n < 0 )
return - ENOMEM ;
2007-10-23 13:47:31 -05:00
}
2008-02-06 19:25:07 -06:00
spin_lock_irqsave ( & chan - > lock , flags ) ;
2008-02-06 19:25:58 -06:00
req = p9_lookup_tag ( chan , n ) ;
spin_unlock_irqrestore ( & chan - > lock , flags ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
p9_set_tag ( tc , n ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
P9_DPRINTK ( P9_DEBUG_TRANS , " 9p debug: virtio rpc tag %d \n " , n ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
out = pack_sg_list ( chan - > sg , 0 , VIRTQUEUE_NUM , tc - > sdata , tc - > size ) ;
2008-02-06 19:25:03 -06:00
in = pack_sg_list ( chan - > sg , out , VIRTQUEUE_NUM - out , rdata , t - > msize ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
req - > status = REQ_STATUS_SENT ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
if ( chan - > vq - > vq_ops - > add_buf ( chan - > vq , chan - > sg , out , in , tc ) ) {
P9_DPRINTK ( P9_DEBUG_TRANS ,
" 9p debug: virtio rpc add_buf returned failure " ) ;
return - EIO ;
}
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
chan - > vq - > vq_ops - > kick ( chan - > vq ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
wait_event ( * req - > wq , req - > status = = REQ_STATUS_RCVD ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
size = le32_to_cpu ( * ( __le32 * ) rdata ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:03 -06:00
err = p9_deserialize_fcall ( rdata , size , * rc , t - > extended ) ;
2008-02-06 19:25:58 -06:00
if ( err < 0 ) {
P9_DPRINTK ( P9_DEBUG_TRANS ,
" 9p debug: virtio rpc deserialize returned %d \n " , err ) ;
return err ;
}
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
# ifdef CONFIG_NET_9P_DEBUG
if ( ( p9_debug_level & P9_DEBUG_FCALL ) = = P9_DEBUG_FCALL ) {
char buf [ 150 ] ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:03 -06:00
p9_printfcall ( buf , sizeof ( buf ) , * rc , t - > extended ) ;
2008-02-06 19:25:58 -06:00
printk ( KERN_NOTICE " >>> %p %s \n " , t , buf ) ;
}
# endif
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
if ( n ! = P9_NOTAG & & p9_idpool_check ( n , chan - > tagpool ) )
p9_idpool_put ( n , chan - > tagpool ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
req - > status = REQ_STATUS_IDLE ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
return 0 ;
2007-10-23 13:47:31 -05:00
}
2008-03-05 07:08:09 -06:00
/**
* p9_virtio_probe - probe for existence of 9 P virtio channels
* @ vdev : virtio device to probe
*
* This probes for existing virtio channels . At present only
* a single channel is in use , so in the future more work may need
* to be done here .
*
*/
2008-02-06 19:25:58 -06:00
static int p9_virtio_probe ( struct virtio_device * vdev )
2007-10-23 13:47:31 -05:00
{
int err ;
struct virtio_chan * chan ;
int index ;
2008-03-07 11:39:13 -06:00
mutex_lock ( & virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
index = chan_index + + ;
chan = & channels [ index ] ;
2008-03-07 11:39:13 -06:00
mutex_unlock ( & virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
if ( chan_index > MAX_9P_CHAN ) {
printk ( KERN_ERR " 9p: virtio: Maximum channels exceeded \n " ) ;
BUG ( ) ;
err = - ENOMEM ;
goto fail ;
}
2008-02-06 19:25:58 -06:00
chan - > vdev = vdev ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
/* We expect one virtqueue, for requests. */
chan - > vq = vdev - > config - > find_vq ( vdev , 0 , req_done ) ;
if ( IS_ERR ( chan - > vq ) ) {
err = PTR_ERR ( chan - > vq ) ;
goto out_free_vq ;
2007-10-23 13:47:31 -05:00
}
2008-02-06 19:25:58 -06:00
chan - > vq - > vdev - > priv = chan ;
spin_lock_init ( & chan - > lock ) ;
2007-10-23 13:47:31 -05:00
2008-02-06 19:25:58 -06:00
sg_init_table ( chan - > sg , VIRTQUEUE_NUM ) ;
2007-10-23 13:47:31 -05:00
chan - > inuse = false ;
chan - > initialized = true ;
return 0 ;
2008-02-06 19:25:58 -06:00
out_free_vq :
vdev - > config - > del_vq ( chan - > vq ) ;
2007-10-23 13:47:31 -05:00
fail :
2008-03-07 11:39:13 -06:00
mutex_lock ( & virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
chan_index - - ;
2008-03-07 11:39:13 -06:00
mutex_unlock ( & virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
return err ;
}
2008-03-05 07:08:09 -06:00
/**
* p9_virtio_create - allocate a new virtio channel
* @ devname : string identifying the channel to connect to ( unused )
* @ args : args passed from sys_mount ( ) for per - transport options ( unused )
* @ msize : requested maximum packet size
* @ extended : 9 p2000 . u enabled flag
*
* This sets up a transport channel for 9 p communication . Right now
2007-10-23 13:47:31 -05: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 07:08:09 -06:00
* mount has a channel open at a time .
*
* Bugs : doesn ' t allow identification of a specific channel
* to allocate , channels are allocated sequentially . This was
* a pragmatic decision to get things rolling , but ideally some
* way of identifying the channel to attach to would be nice
* if we are going to support multiple channels .
*
*/
2008-02-06 19:25:03 -06:00
static struct p9_trans *
p9_virtio_create ( const char * devname , char * args , int msize ,
unsigned char extended )
2007-10-23 13:47:31 -05:00
{
struct p9_trans * trans ;
struct virtio_chan * chan = channels ;
2008-02-06 19:25:58 -06:00
int index = 0 ;
2007-10-23 13:47:31 -05:00
2008-03-07 11:39:13 -06:00
mutex_lock ( & virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
while ( index < MAX_9P_CHAN ) {
if ( chan - > initialized & & ! chan - > inuse ) {
chan - > inuse = true ;
break ;
} else {
index + + ;
chan = & channels [ index ] ;
}
}
2008-03-07 11:39:13 -06:00
mutex_unlock ( & virtio_9p_lock ) ;
2007-10-23 13:47:31 -05:00
if ( index > = MAX_9P_CHAN ) {
2008-02-06 19:25:58 -06:00
printk ( KERN_ERR " 9p: no channels available \n " ) ;
return ERR_PTR ( - ENODEV ) ;
}
chan - > tagpool = p9_idpool_create ( ) ;
if ( IS_ERR ( chan - > tagpool ) ) {
printk ( KERN_ERR " 9p: couldn't allocate tagpool \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
2007-10-23 13:47:31 -05:00
}
2008-02-06 19:25:58 -06:00
p9_idpool_get ( chan - > tagpool ) ; /* reserve tag 0 */
chan - > max_tag = 0 ;
chan - > reqs = NULL ;
2007-10-23 13:47:31 -05:00
trans = kmalloc ( sizeof ( struct p9_trans ) , GFP_KERNEL ) ;
if ( ! trans ) {
printk ( KERN_ERR " 9p: couldn't allocate transport \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2008-02-06 19:25:03 -06:00
trans - > extended = extended ;
trans - > msize = msize ;
2007-10-23 13:47:31 -05:00
trans - > close = p9_virtio_close ;
2008-02-06 19:25:58 -06:00
trans - > rpc = p9_virtio_rpc ;
2007-10-23 13:47:31 -05:00
trans - > priv = chan ;
return trans ;
}
2008-03-05 07:08:09 -06:00
/**
* p9_virtio_remove - clean up resources associated with a virtio device
* @ vdev : virtio device to remove
*
*/
2008-02-06 19:25:04 -06:00
static void p9_virtio_remove ( struct virtio_device * vdev )
{
struct virtio_chan * chan = vdev - > priv ;
BUG_ON ( chan - > inuse ) ;
if ( chan - > initialized ) {
vdev - > config - > del_vq ( chan - > vq ) ;
chan - > initialized = false ;
}
}
2007-10-23 13:47:31 -05:00
# define VIRTIO_ID_9P 9
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-06 19:25:04 -06:00
. remove = p9_virtio_remove ,
2007-10-23 13:47:31 -05:00
} ;
static struct p9_trans_module p9_virtio_trans = {
. name = " virtio " ,
. create = p9_virtio_create ,
2008-02-06 19:25:58 -06:00
. maxsize = PAGE_SIZE * 16 ,
2007-10-23 13:47:31 -05:00
. def = 0 ,
} ;
/* The standard init function */
static int __init p9_virtio_init ( void )
{
int count ;
for ( count = 0 ; count < MAX_9P_CHAN ; count + + )
channels [ count ] . initialized = false ;
v9fs_register_trans ( & p9_virtio_trans ) ;
return register_virtio_driver ( & p9_virtio_drv ) ;
}
2008-02-06 19:25:04 -06:00
static void __exit p9_virtio_cleanup ( void )
{
unregister_virtio_driver ( & p9_virtio_drv ) ;
}
2007-10-23 13:47:31 -05:00
module_init ( p9_virtio_init ) ;
2008-02-06 19:25:04 -06:00
module_exit ( p9_virtio_cleanup ) ;
2007-10-23 13:47:31 -05:00
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_AUTHOR ( " Eric Van Hensbergen <ericvh@gmail.com> " ) ;
MODULE_DESCRIPTION ( " Virtio 9p Transport " ) ;
MODULE_LICENSE ( " GPL " ) ;