2005-09-10 00:04:22 +04:00
/*
2007-07-11 02:57:28 +04:00
* net / 9 p / mux . c
2005-09-10 00:04:22 +04:00
*
* Protocol Multiplexer
*
* Copyright ( C ) 2004 by Eric Van Hensbergen < ericvh @ gmail . com >
2006-01-08 12:04:58 +03:00
* Copyright ( C ) 2004 - 2005 by Latchesar Ionkov < lucho @ ionkov . net >
2005-09-10 00:04:22 +04:00
*
* This program is free software ; you can redistribute it and / or modify
2006-03-25 14:07:28 +03:00
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
2005-09-10 00:04:22 +04:00
*
* 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/module.h>
# include <linux/errno.h>
# include <linux/fs.h>
2006-01-08 12:04:58 +03:00
# include <linux/poll.h>
2005-09-10 00:04:22 +04:00
# include <linux/kthread.h>
# include <linux/idr.h>
2006-03-23 14:00:19 +03:00
# include <linux/mutex.h>
2007-07-11 02:57:28 +04:00
# include <net/9p/9p.h>
2007-10-17 23:31:07 +04:00
# include <linux/parser.h>
2007-07-11 02:57:28 +04:00
# include <net/9p/transport.h>
# include <net/9p/conn.h>
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
# define ERREQFLUSH 1
# define SCHED_TIMEOUT 10
# define MAXPOLLWADDR 2
enum {
Rworksched = 1 , /* read work scheduled or running */
Rpending = 2 , /* can read */
Wworksched = 4 , /* write work scheduled or running */
Wpending = 8 , /* can write */
} ;
2006-05-15 20:44:21 +04:00
enum {
None ,
Flushing ,
Flushed ,
} ;
2007-07-11 02:57:28 +04:00
struct p9_mux_poll_task ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
struct p9_req {
spinlock_t lock ; /* protect request structure */
2006-01-08 12:04:58 +03:00
int tag ;
2007-07-11 02:57:28 +04:00
struct p9_fcall * tcall ;
struct p9_fcall * rcall ;
2006-01-08 12:04:58 +03:00
int err ;
2007-07-11 02:57:28 +04:00
p9_conn_req_callback cb ;
2006-01-08 12:04:58 +03:00
void * cba ;
2006-05-15 20:44:21 +04:00
int flush ;
2006-01-08 12:04:58 +03:00
struct list_head req_list ;
} ;
2007-07-11 02:57:28 +04:00
struct p9_conn {
spinlock_t lock ; /* protect lock structure */
2006-01-08 12:04:58 +03:00
struct list_head mux_list ;
2007-07-11 02:57:28 +04:00
struct p9_mux_poll_task * poll_task ;
2006-01-08 12:04:58 +03:00
int msize ;
unsigned char * extended ;
2007-10-17 23:31:07 +04:00
struct p9_trans * trans ;
2007-07-11 02:57:28 +04:00
struct p9_idpool * tagpool ;
2006-01-08 12:04:58 +03:00
int err ;
wait_queue_head_t equeue ;
struct list_head req_list ;
struct list_head unsent_req_list ;
2007-07-11 02:57:28 +04:00
struct p9_fcall * rcall ;
2006-01-08 12:04:58 +03:00
int rpos ;
char * rbuf ;
int wpos ;
int wsize ;
char * wbuf ;
wait_queue_t poll_wait [ MAXPOLLWADDR ] ;
wait_queue_head_t * poll_waddr [ MAXPOLLWADDR ] ;
poll_table pt ;
struct work_struct rq ;
struct work_struct wq ;
unsigned long wsched ;
} ;
2007-07-11 02:57:28 +04:00
struct p9_mux_poll_task {
2006-01-08 12:04:58 +03:00
struct task_struct * task ;
struct list_head mux_list ;
int muxnum ;
} ;
2007-07-11 02:57:28 +04:00
struct p9_mux_rpc {
struct p9_conn * m ;
2006-01-08 12:04:58 +03:00
int err ;
2007-07-11 02:57:28 +04:00
struct p9_fcall * tcall ;
struct p9_fcall * rcall ;
2006-01-08 12:04:58 +03:00
wait_queue_head_t wqueue ;
} ;
2007-07-11 02:57:28 +04:00
static int p9_poll_proc ( void * ) ;
static void p9_read_work ( struct work_struct * work ) ;
static void p9_write_work ( struct work_struct * work ) ;
static void p9_pollwait ( struct file * filp , wait_queue_head_t * wait_address ,
2006-01-08 12:04:58 +03:00
poll_table * p ) ;
2007-07-11 02:57:28 +04:00
static u16 p9_mux_get_tag ( struct p9_conn * ) ;
static void p9_mux_put_tag ( struct p9_conn * , u16 ) ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
static DEFINE_MUTEX ( p9_mux_task_lock ) ;
static struct workqueue_struct * p9_mux_wq ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
static int p9_mux_num ;
static int p9_mux_poll_task_num ;
static struct p9_mux_poll_task p9_mux_poll_tasks [ 100 ] ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
int p9_mux_global_init ( void )
2006-01-08 12:04:58 +03:00
{
int i ;
2007-07-11 02:57:28 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( p9_mux_poll_tasks ) ; i + + )
p9_mux_poll_tasks [ i ] . task = NULL ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
p9_mux_wq = create_workqueue ( " v9fs " ) ;
if ( ! p9_mux_wq ) {
2007-01-26 11:57:04 +03:00
printk ( KERN_WARNING " v9fs: mux: creating workqueue failed \n " ) ;
2006-01-08 12:05:02 +03:00
return - ENOMEM ;
2007-01-26 11:57:04 +03:00
}
2006-01-08 12:05:02 +03:00
return 0 ;
2006-01-08 12:04:58 +03:00
}
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
void p9_mux_global_exit ( void )
2005-09-10 00:04:22 +04:00
{
2007-07-11 02:57:28 +04:00
destroy_workqueue ( p9_mux_wq ) ;
2005-09-10 00:04:22 +04:00
}
/**
2007-07-11 02:57:28 +04:00
* p9_mux_calc_poll_procs - calculates the number of polling procs
2006-01-08 12:04:58 +03:00
* based on the number of mounted v9fs filesystems .
2005-09-10 00:04:22 +04:00
*
2006-01-08 12:04:58 +03:00
* The current implementation returns sqrt of the number of mounts .
2005-09-10 00:04:22 +04:00
*/
2007-07-11 02:57:28 +04:00
static int p9_mux_calc_poll_procs ( int muxnum )
2006-01-08 12:04:58 +03:00
{
int n ;
2007-07-11 02:57:28 +04:00
if ( p9_mux_poll_task_num )
n = muxnum / p9_mux_poll_task_num +
( muxnum % p9_mux_poll_task_num ? 1 : 0 ) ;
2006-01-08 12:04:58 +03:00
else
n = 1 ;
2007-07-11 02:57:28 +04:00
if ( n > ARRAY_SIZE ( p9_mux_poll_tasks ) )
n = ARRAY_SIZE ( p9_mux_poll_tasks ) ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
return n ;
}
2007-07-11 02:57:28 +04:00
static int p9_mux_poll_start ( struct p9_conn * m )
2005-09-10 00:04:22 +04:00
{
2006-01-08 12:04:58 +03:00
int i , n ;
2007-07-11 02:57:28 +04:00
struct p9_mux_poll_task * vpt , * vptlast ;
2006-01-08 12:05:02 +03:00
struct task_struct * pproc ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p muxnum %d procnum %d \n " , m , p9_mux_num ,
p9_mux_poll_task_num ) ;
mutex_lock ( & p9_mux_task_lock ) ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
n = p9_mux_calc_poll_procs ( p9_mux_num + 1 ) ;
if ( n > p9_mux_poll_task_num ) {
for ( i = 0 ; i < ARRAY_SIZE ( p9_mux_poll_tasks ) ; i + + ) {
if ( p9_mux_poll_tasks [ i ] . task = = NULL ) {
vpt = & p9_mux_poll_tasks [ i ] ;
P9_DPRINTK ( P9_DEBUG_MUX , " create proc %p \n " ,
vpt ) ;
pproc = kthread_create ( p9_poll_proc , vpt ,
" v9fs-poll " ) ;
2006-01-08 12:05:02 +03:00
if ( ! IS_ERR ( pproc ) ) {
vpt - > task = pproc ;
INIT_LIST_HEAD ( & vpt - > mux_list ) ;
vpt - > muxnum = 0 ;
2007-07-11 02:57:28 +04:00
p9_mux_poll_task_num + + ;
2006-01-08 12:05:02 +03:00
wake_up_process ( vpt - > task ) ;
}
2006-01-08 12:04:58 +03:00
break ;
}
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
if ( i > = ARRAY_SIZE ( p9_mux_poll_tasks ) )
P9_DPRINTK ( P9_DEBUG_ERROR ,
" warning: no free poll slots \n " ) ;
2006-01-08 12:04:58 +03:00
}
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
n = ( p9_mux_num + 1 ) / p9_mux_poll_task_num +
( ( p9_mux_num + 1 ) % p9_mux_poll_task_num ? 1 : 0 ) ;
2006-01-08 12:04:58 +03:00
vptlast = NULL ;
2007-07-11 02:57:28 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( p9_mux_poll_tasks ) ; i + + ) {
vpt = & p9_mux_poll_tasks [ i ] ;
2006-01-08 12:04:58 +03:00
if ( vpt - > task ! = NULL ) {
vptlast = vpt ;
if ( vpt - > muxnum < n ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " put in proc %d \n " , i ) ;
2006-01-08 12:04:58 +03:00
list_add ( & m - > mux_list , & vpt - > mux_list ) ;
vpt - > muxnum + + ;
m - > poll_task = vpt ;
2007-07-11 02:57:28 +04:00
memset ( & m - > poll_waddr , 0 ,
sizeof ( m - > poll_waddr ) ) ;
init_poll_funcptr ( & m - > pt , p9_pollwait ) ;
2006-01-08 12:04:58 +03:00
break ;
}
}
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
if ( i > = ARRAY_SIZE ( p9_mux_poll_tasks ) ) {
2007-10-24 07:52:48 +04:00
if ( vptlast = = NULL ) {
mutex_unlock ( & p9_mux_task_lock ) ;
2006-01-08 12:05:02 +03:00
return - ENOMEM ;
2007-10-24 07:52:48 +04:00
}
2006-01-08 12:05:02 +03:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " put in proc %d \n " , i ) ;
2006-01-08 12:04:58 +03:00
list_add ( & m - > mux_list , & vptlast - > mux_list ) ;
vptlast - > muxnum + + ;
2006-01-08 12:05:02 +03:00
m - > poll_task = vptlast ;
2006-01-08 12:04:58 +03:00
memset ( & m - > poll_waddr , 0 , sizeof ( m - > poll_waddr ) ) ;
2007-07-11 02:57:28 +04:00
init_poll_funcptr ( & m - > pt , p9_pollwait ) ;
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
p9_mux_num + + ;
mutex_unlock ( & p9_mux_task_lock ) ;
2006-01-08 12:05:02 +03:00
return 0 ;
2006-01-08 12:04:58 +03:00
}
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
static void p9_mux_poll_stop ( struct p9_conn * m )
2006-01-08 12:04:58 +03:00
{
int i ;
2007-07-11 02:57:28 +04:00
struct p9_mux_poll_task * vpt ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
mutex_lock ( & p9_mux_task_lock ) ;
2006-01-08 12:04:58 +03:00
vpt = m - > poll_task ;
list_del ( & m - > mux_list ) ;
2007-07-11 02:57:28 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( m - > poll_waddr ) ; i + + ) {
2006-01-08 12:04:58 +03:00
if ( m - > poll_waddr [ i ] ! = NULL ) {
remove_wait_queue ( m - > poll_waddr [ i ] , & m - > poll_wait [ i ] ) ;
m - > poll_waddr [ i ] = NULL ;
}
}
vpt - > muxnum - - ;
if ( ! vpt - > muxnum ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " destroy proc %p \n " , vpt ) ;
2006-12-13 01:26:07 +03:00
kthread_stop ( vpt - > task ) ;
2006-01-08 12:04:58 +03:00
vpt - > task = NULL ;
2007-07-11 02:57:28 +04:00
p9_mux_poll_task_num - - ;
2006-01-08 12:04:58 +03:00
}
2007-07-11 02:57:28 +04:00
p9_mux_num - - ;
mutex_unlock ( & p9_mux_task_lock ) ;
2006-01-08 12:04:58 +03:00
}
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
/**
2007-07-11 02:57:28 +04:00
* p9_conn_create - allocate and initialize the per - session mux data
2006-01-08 12:04:58 +03:00
* Creates the polling task if this is the first session .
*
* @ trans - transport structure
* @ msize - maximum message size
* @ extended - pointer to the extended flag
*/
2007-10-17 23:31:07 +04:00
struct p9_conn * p9_conn_create ( struct p9_trans * trans , int msize ,
2006-01-08 12:04:58 +03:00
unsigned char * extended )
{
int i , n ;
2007-07-11 02:57:28 +04:00
struct p9_conn * m , * mtmp ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " transport %p msize %d \n " , trans , msize ) ;
m = kmalloc ( sizeof ( struct p9_conn ) , GFP_KERNEL ) ;
2006-01-08 12:04:58 +03:00
if ( ! m )
return ERR_PTR ( - ENOMEM ) ;
spin_lock_init ( & m - > lock ) ;
INIT_LIST_HEAD ( & m - > mux_list ) ;
m - > msize = msize ;
m - > extended = extended ;
m - > trans = trans ;
2007-07-11 02:57:28 +04:00
m - > tagpool = p9_idpool_create ( ) ;
2007-07-26 23:04:54 +04:00
if ( IS_ERR ( m - > tagpool ) ) {
mtmp = ERR_PTR ( - ENOMEM ) ;
2007-07-11 02:57:28 +04:00
kfree ( m ) ;
2007-07-26 23:04:54 +04:00
return mtmp ;
2007-07-11 02:57:28 +04:00
}
2006-01-08 12:04:58 +03:00
m - > err = 0 ;
init_waitqueue_head ( & m - > equeue ) ;
INIT_LIST_HEAD ( & m - > req_list ) ;
INIT_LIST_HEAD ( & m - > unsent_req_list ) ;
2006-01-08 12:05:00 +03:00
m - > rcall = NULL ;
2006-01-08 12:04:58 +03:00
m - > rpos = 0 ;
2006-01-08 12:05:00 +03:00
m - > rbuf = NULL ;
2006-01-08 12:04:58 +03:00
m - > wpos = m - > wsize = 0 ;
2006-01-08 12:05:00 +03:00
m - > wbuf = NULL ;
2007-07-11 02:57:28 +04:00
INIT_WORK ( & m - > rq , p9_read_work ) ;
INIT_WORK ( & m - > wq , p9_write_work ) ;
2006-01-08 12:04:58 +03:00
m - > wsched = 0 ;
memset ( & m - > poll_waddr , 0 , sizeof ( m - > poll_waddr ) ) ;
2006-01-08 12:05:02 +03:00
m - > poll_task = NULL ;
2007-07-11 02:57:28 +04:00
n = p9_mux_poll_start ( m ) ;
2007-07-26 23:04:54 +04:00
if ( n ) {
kfree ( m ) ;
2006-01-08 12:05:02 +03:00
return ERR_PTR ( n ) ;
2007-07-26 23:04:54 +04:00
}
2006-01-08 12:04:58 +03:00
n = trans - > poll ( trans , & m - > pt ) ;
if ( n & POLLIN ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p can read \n " , m ) ;
2006-01-08 12:04:58 +03:00
set_bit ( Rpending , & m - > wsched ) ;
2005-09-10 00:04:22 +04:00
}
2006-01-08 12:04:58 +03:00
if ( n & POLLOUT ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p can write \n " , m ) ;
2006-01-08 12:04:58 +03:00
set_bit ( Wpending , & m - > wsched ) ;
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( m - > poll_waddr ) ; i + + ) {
2006-01-08 12:04:58 +03:00
if ( IS_ERR ( m - > poll_waddr [ i ] ) ) {
2007-07-11 02:57:28 +04:00
p9_mux_poll_stop ( m ) ;
2006-01-08 12:04:58 +03:00
mtmp = ( void * ) m - > poll_waddr ; /* the error code */
kfree ( m ) ;
m = mtmp ;
break ;
}
2005-09-10 00:04:22 +04:00
}
2006-01-08 12:04:58 +03:00
return m ;
}
2007-07-11 02:57:28 +04:00
EXPORT_SYMBOL ( p9_conn_create ) ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
/**
2007-07-11 02:57:28 +04:00
* p9_mux_destroy - cancels all pending requests and frees mux resources
2006-01-08 12:04:58 +03:00
*/
2007-07-11 02:57:28 +04:00
void p9_conn_destroy ( struct p9_conn * m )
2006-01-08 12:04:58 +03:00
{
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p prev %p next %p \n " , m ,
2006-01-08 12:04:58 +03:00
m - > mux_list . prev , m - > mux_list . next ) ;
2007-07-11 02:57:28 +04:00
p9_conn_cancel ( m , - ECONNRESET ) ;
2006-01-08 12:04:58 +03:00
if ( ! list_empty ( & m - > req_list ) ) {
/* wait until all processes waiting on this session exit */
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX ,
" mux %p waiting for empty request queue \n " , m ) ;
2006-01-08 12:04:58 +03:00
wait_event_timeout ( m - > equeue , ( list_empty ( & m - > req_list ) ) , 5000 ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p request queue empty: %d \n " , m ,
2006-01-08 12:04:58 +03:00
list_empty ( & m - > req_list ) ) ;
}
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
p9_mux_poll_stop ( m ) ;
2006-01-08 12:04:58 +03:00
m - > trans = NULL ;
2007-07-11 02:57:28 +04:00
p9_idpool_destroy ( m - > tagpool ) ;
2006-01-08 12:04:58 +03:00
kfree ( m ) ;
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
EXPORT_SYMBOL ( p9_conn_destroy ) ;
2005-09-10 00:04:22 +04:00
/**
2007-07-11 02:57:28 +04:00
* p9_pollwait - called by files poll operation to add v9fs - poll task
2006-01-08 12:04:58 +03:00
* to files wait queue
2005-09-10 00:04:22 +04:00
*/
2006-01-08 12:04:58 +03:00
static void
2007-07-11 02:57:28 +04:00
p9_pollwait ( struct file * filp , wait_queue_head_t * wait_address ,
2006-01-08 12:04:58 +03:00
poll_table * p )
2005-09-10 00:04:22 +04:00
{
2006-01-08 12:04:58 +03:00
int i ;
2007-07-11 02:57:28 +04:00
struct p9_conn * m ;
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
m = container_of ( p , struct p9_conn , pt ) ;
for ( i = 0 ; i < ARRAY_SIZE ( m - > poll_waddr ) ; i + + )
2006-01-08 12:04:58 +03:00
if ( m - > poll_waddr [ i ] = = NULL )
break ;
2005-09-10 00:04:28 +04:00
2006-01-08 12:04:58 +03:00
if ( i > = ARRAY_SIZE ( m - > poll_waddr ) ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_ERROR , " not enough wait_address slots \n " ) ;
2006-01-08 12:04:58 +03:00
return ;
}
2005-09-10 00:04:28 +04:00
2006-01-08 12:04:58 +03:00
m - > poll_waddr [ i ] = wait_address ;
2005-09-10 00:04:28 +04:00
2006-01-08 12:04:58 +03:00
if ( ! wait_address ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_ERROR , " no wait_address \n " ) ;
2006-01-08 12:04:58 +03:00
m - > poll_waddr [ i ] = ERR_PTR ( - EIO ) ;
return ;
}
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
init_waitqueue_entry ( & m - > poll_wait [ i ] , m - > poll_task - > task ) ;
add_wait_queue ( wait_address , & m - > poll_wait [ i ] ) ;
2005-09-10 00:04:22 +04:00
}
/**
2007-07-11 02:57:28 +04:00
* p9_poll_mux - polls a mux and schedules read or write works if necessary
2005-09-10 00:04:22 +04:00
*/
2007-07-11 02:57:28 +04:00
static void p9_poll_mux ( struct p9_conn * m )
2005-09-10 00:04:22 +04:00
{
2006-01-08 12:04:58 +03:00
int n ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
if ( m - > err < 0 )
return ;
n = m - > trans - > poll ( m - > trans , NULL ) ;
if ( n < 0 | | n & ( POLLERR | POLLHUP | POLLNVAL ) ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " error mux %p err %d \n " , m , n ) ;
2006-01-08 12:04:58 +03:00
if ( n > = 0 )
n = - ECONNRESET ;
2007-07-11 02:57:28 +04:00
p9_conn_cancel ( m , n ) ;
2006-01-08 12:04:58 +03:00
}
if ( n & POLLIN ) {
set_bit ( Rpending , & m - > wsched ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p can read \n " , m ) ;
2006-01-08 12:04:58 +03:00
if ( ! test_and_set_bit ( Rworksched , & m - > wsched ) ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " schedule read work %p \n " , m ) ;
queue_work ( p9_mux_wq , & m - > rq ) ;
2006-01-08 12:04:58 +03:00
}
}
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
if ( n & POLLOUT ) {
set_bit ( Wpending , & m - > wsched ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p can write \n " , m ) ;
2006-01-08 12:04:58 +03:00
if ( ( m - > wsize | | ! list_empty ( & m - > unsent_req_list ) )
& & ! test_and_set_bit ( Wworksched , & m - > wsched ) ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " schedule write work %p \n " , m ) ;
queue_work ( p9_mux_wq , & m - > wq ) ;
2006-01-08 12:04:58 +03:00
}
}
2005-09-10 00:04:22 +04:00
}
/**
2007-07-11 02:57:28 +04:00
* p9_poll_proc - polls all v9fs transports for new events and queues
2006-01-08 12:04:58 +03:00
* the appropriate work to the work queue
2005-09-10 00:04:22 +04:00
*/
2007-07-11 02:57:28 +04:00
static int p9_poll_proc ( void * a )
2005-09-10 00:04:22 +04:00
{
2007-07-11 02:57:28 +04:00
struct p9_conn * m , * mtmp ;
struct p9_mux_poll_task * vpt ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
vpt = a ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " start %p %p \n " , current , vpt ) ;
2006-01-08 12:04:58 +03:00
while ( ! kthread_should_stop ( ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
list_for_each_entry_safe ( m , mtmp , & vpt - > mux_list , mux_list ) {
2007-07-11 02:57:28 +04:00
p9_poll_mux ( m ) ;
2006-01-08 12:04:58 +03:00
}
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " sleeping... \n " ) ;
2006-01-08 12:04:58 +03:00
schedule_timeout ( SCHED_TIMEOUT * HZ ) ;
}
2005-09-10 00:04:28 +04:00
2006-01-08 12:04:58 +03:00
__set_current_state ( TASK_RUNNING ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " finish \n " ) ;
2006-01-08 12:04:58 +03:00
return 0 ;
}
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
/**
2007-07-11 02:57:28 +04:00
* p9_write_work - called when a transport can send some data
2006-01-08 12:04:58 +03:00
*/
2007-07-11 02:57:28 +04:00
static void p9_write_work ( struct work_struct * work )
2006-01-08 12:04:58 +03:00
{
int n , err ;
2007-07-11 02:57:28 +04:00
struct p9_conn * m ;
struct p9_req * req ;
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
m = container_of ( work , struct p9_conn , wq ) ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
if ( m - > err < 0 ) {
clear_bit ( Wworksched , & m - > wsched ) ;
return ;
2005-09-10 00:04:22 +04:00
}
2006-01-08 12:04:58 +03:00
if ( ! m - > wsize ) {
if ( list_empty ( & m - > unsent_req_list ) ) {
clear_bit ( Wworksched , & m - > wsched ) ;
return ;
2005-09-10 00:04:22 +04:00
}
2006-01-08 12:04:58 +03:00
spin_lock ( & m - > lock ) ;
2006-02-03 14:04:20 +03:00
again :
2007-07-11 02:57:28 +04:00
req = list_entry ( m - > unsent_req_list . next , struct p9_req ,
2006-01-08 12:05:00 +03:00
req_list ) ;
list_move_tail ( & req - > req_list , & m - > req_list ) ;
2006-02-03 14:04:20 +03:00
if ( req - > err = = ERREQFLUSH )
goto again ;
2006-01-08 12:05:00 +03:00
m - > wbuf = req - > tcall - > sdata ;
m - > wsize = req - > tcall - > size ;
2006-01-08 12:04:58 +03:00
m - > wpos = 0 ;
spin_unlock ( & m - > lock ) ;
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p pos %d size %d \n " , m , m - > wpos ,
m - > wsize ) ;
2006-01-08 12:04:58 +03:00
clear_bit ( Wpending , & m - > wsched ) ;
err = m - > trans - > write ( m - > trans , m - > wbuf + m - > wpos , m - > wsize - m - > wpos ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p sent %d bytes \n " , m , err ) ;
2006-01-08 12:04:58 +03:00
if ( err = = - EAGAIN ) {
clear_bit ( Wworksched , & m - > wsched ) ;
return ;
}
2007-07-12 01:14:46 +04:00
if ( err < 0 )
2006-01-08 12:04:58 +03:00
goto error ;
2007-07-12 01:14:46 +04:00
else if ( err = = 0 ) {
err = - EREMOTEIO ;
goto error ;
}
2006-01-08 12:04:58 +03:00
m - > wpos + = err ;
if ( m - > wpos = = m - > wsize )
m - > wpos = m - > wsize = 0 ;
if ( m - > wsize = = 0 & & ! list_empty ( & m - > unsent_req_list ) ) {
if ( test_and_clear_bit ( Wpending , & m - > wsched ) )
n = POLLOUT ;
else
n = m - > trans - > poll ( m - > trans , NULL ) ;
if ( n & POLLOUT ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " schedule write work %p \n " , m ) ;
queue_work ( p9_mux_wq , & m - > wq ) ;
2006-01-08 12:04:58 +03:00
} else
clear_bit ( Wworksched , & m - > wsched ) ;
} else
clear_bit ( Wworksched , & m - > wsched ) ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
return ;
2007-07-11 02:57:28 +04:00
error :
p9_conn_cancel ( m , err ) ;
2006-01-08 12:04:58 +03:00
clear_bit ( Wworksched , & m - > wsched ) ;
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
static void process_request ( struct p9_conn * m , struct p9_req * req )
2005-09-10 00:04:23 +04:00
{
2006-05-15 20:44:21 +04:00
int ecode ;
2007-07-11 02:57:28 +04:00
struct p9_str * ename ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
if ( ! req - > err & & req - > rcall - > id = = P9_RERROR ) {
2006-01-08 12:04:58 +03:00
ecode = req - > rcall - > params . rerror . errno ;
2006-01-08 12:05:00 +03:00
ename = & req - > rcall - > params . rerror . error ;
2005-09-10 00:04:23 +04:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " Rerror %.*s \n " , ename - > len ,
ename - > str ) ;
2006-01-08 12:04:58 +03:00
if ( * m - > extended )
req - > err = - ecode ;
if ( ! req - > err ) {
2007-07-11 02:57:28 +04:00
req - > err = p9_errstr2errno ( ename - > str , ename - > len ) ;
2006-01-08 12:04:58 +03:00
if ( ! req - > err ) { /* string match failed */
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " unknown error " , req - > rcall ) ;
2006-01-08 12:04:58 +03:00
}
if ( ! req - > err )
req - > err = - ESERVERFAULT ;
}
} else if ( req - > tcall & & req - > rcall - > id ! = req - > tcall - > id + 1 ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_ERROR ,
" fcall mismatch: expected %d, got %d \n " ,
req - > tcall - > id + 1 , req - > rcall - > id ) ;
2006-01-08 12:04:58 +03:00
if ( ! req - > err )
req - > err = - EIO ;
2005-09-10 00:04:23 +04:00
}
}
2005-09-10 00:04:22 +04:00
/**
2007-07-11 02:57:28 +04:00
* p9_read_work - called when there is some data to be read from a transport
2005-09-10 00:04:22 +04:00
*/
2007-07-11 02:57:28 +04:00
static void p9_read_work ( struct work_struct * work )
2005-09-10 00:04:22 +04:00
{
2006-01-08 12:05:00 +03:00
int n , err ;
2007-07-11 02:57:28 +04:00
struct p9_conn * m ;
struct p9_req * req , * rptr , * rreq ;
struct p9_fcall * rcall ;
2006-01-08 12:05:00 +03:00
char * rbuf ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
m = container_of ( work , struct p9_conn , rq ) ;
2006-01-08 12:04:58 +03:00
if ( m - > err < 0 )
return ;
rcall = NULL ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " start mux %p pos %d \n " , m , m - > rpos ) ;
2006-01-08 12:05:00 +03:00
if ( ! m - > rcall ) {
m - > rcall =
2007-07-11 02:57:28 +04:00
kmalloc ( sizeof ( struct p9_fcall ) + m - > msize , GFP_KERNEL ) ;
2006-01-08 12:05:00 +03:00
if ( ! m - > rcall ) {
err = - ENOMEM ;
goto error ;
}
2007-07-11 02:57:28 +04:00
m - > rbuf = ( char * ) m - > rcall + sizeof ( struct p9_fcall ) ;
2006-01-08 12:05:00 +03:00
m - > rpos = 0 ;
}
2006-01-08 12:04:58 +03:00
clear_bit ( Rpending , & m - > wsched ) ;
err = m - > trans - > read ( m - > trans , m - > rbuf + m - > rpos , m - > msize - m - > rpos ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p got %d bytes \n " , m , err ) ;
2006-01-08 12:04:58 +03:00
if ( err = = - EAGAIN ) {
clear_bit ( Rworksched , & m - > wsched ) ;
return ;
}
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
if ( err < = 0 )
goto error ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
m - > rpos + = err ;
while ( m - > rpos > 4 ) {
n = le32_to_cpu ( * ( __le32 * ) m - > rbuf ) ;
if ( n > = m - > msize ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_ERROR ,
2006-01-08 12:04:58 +03:00
" requested packet size too big: %d \n " , n ) ;
err = - EIO ;
goto error ;
}
if ( m - > rpos < n )
2005-09-10 00:04:22 +04:00
break ;
2006-01-08 12:04:58 +03:00
2006-01-08 12:05:00 +03:00
err =
2007-07-11 02:57:28 +04:00
p9_deserialize_fcall ( m - > rbuf , n , m - > rcall , * m - > extended ) ;
2005-09-10 00:04:28 +04:00
if ( err < 0 ) {
2006-01-08 12:04:58 +03:00
goto error ;
2005-09-10 00:04:22 +04:00
}
2007-07-11 02:57:28 +04:00
# ifdef CONFIG_NET_9P_DEBUG
if ( ( p9_debug_level & P9_DEBUG_FCALL ) = = P9_DEBUG_FCALL ) {
2006-03-25 14:07:25 +03:00
char buf [ 150 ] ;
2007-07-11 02:57:28 +04:00
p9_printfcall ( buf , sizeof ( buf ) , m - > rcall ,
2006-03-25 14:07:25 +03:00
* m - > extended ) ;
printk ( KERN_NOTICE " >>> %p %s \n " , m , buf ) ;
}
2007-07-11 02:57:28 +04:00
# endif
2006-03-25 14:07:25 +03:00
2006-01-08 12:05:00 +03:00
rcall = m - > rcall ;
rbuf = m - > rbuf ;
if ( m - > rpos > n ) {
2007-07-11 02:57:28 +04:00
m - > rcall = kmalloc ( sizeof ( struct p9_fcall ) + m - > msize ,
2006-01-08 12:05:00 +03:00
GFP_KERNEL ) ;
if ( ! m - > rcall ) {
err = - ENOMEM ;
goto error ;
}
2007-07-11 02:57:28 +04:00
m - > rbuf = ( char * ) m - > rcall + sizeof ( struct p9_fcall ) ;
2006-01-08 12:05:00 +03:00
memmove ( m - > rbuf , rbuf + n , m - > rpos - n ) ;
m - > rpos - = n ;
} else {
m - > rcall = NULL ;
m - > rbuf = NULL ;
m - > rpos = 0 ;
}
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p fcall id %d tag %d \n " , m ,
rcall - > id , rcall - > tag ) ;
2006-01-08 12:04:58 +03:00
req = NULL ;
spin_lock ( & m - > lock ) ;
list_for_each_entry_safe ( rreq , rptr , & m - > req_list , req_list ) {
if ( rreq - > tag = = rcall - > tag ) {
req = rreq ;
2006-05-15 20:44:21 +04:00
if ( req - > flush ! = Flushing )
list_del ( & req - > req_list ) ;
2006-01-08 12:04:58 +03:00
break ;
2005-09-10 00:04:22 +04:00
}
}
2006-05-15 20:44:21 +04:00
spin_unlock ( & m - > lock ) ;
2005-09-10 00:04:22 +04:00
2006-05-15 20:44:21 +04:00
if ( req ) {
req - > rcall = rcall ;
process_request ( m , req ) ;
if ( req - > flush ! = Flushing ) {
if ( req - > cb )
( * req - > cb ) ( req , req - > cba ) ;
else
kfree ( req - > rcall ) ;
wake_up ( & m - > equeue ) ;
}
} else {
2007-07-11 02:57:28 +04:00
if ( err > = 0 & & rcall - > id ! = P9_RFLUSH )
P9_DPRINTK ( P9_DEBUG_ERROR ,
" unexpected response mux %p id %d tag %d \n " ,
m , rcall - > id , rcall - > tag ) ;
2005-09-10 00:04:22 +04:00
kfree ( rcall ) ;
}
}
2006-01-08 12:04:58 +03:00
if ( ! list_empty ( & m - > req_list ) ) {
if ( test_and_clear_bit ( Rpending , & m - > wsched ) )
n = POLLIN ;
else
n = m - > trans - > poll ( m - > trans , NULL ) ;
if ( n & POLLIN ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " schedule read work %p \n " , m ) ;
queue_work ( p9_mux_wq , & m - > rq ) ;
2006-01-08 12:04:58 +03:00
} else
clear_bit ( Rworksched , & m - > wsched ) ;
} else
clear_bit ( Rworksched , & m - > wsched ) ;
2005-09-10 00:04:22 +04:00
2006-01-08 12:04:58 +03:00
return ;
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
error :
p9_conn_cancel ( m , err ) ;
2006-01-08 12:04:58 +03:00
clear_bit ( Rworksched , & m - > wsched ) ;
2005-09-10 00:04:22 +04:00
}
/**
2007-07-11 02:57:28 +04:00
* p9_send_request - send 9 P request
2006-01-08 12:04:58 +03:00
* The function can sleep until the request is scheduled for sending .
* The function can be interrupted . Return from the function is not
2006-06-26 20:35:02 +04:00
* a guarantee that the request is sent successfully . Can return errors
2006-01-08 12:04:58 +03:00
* that can be retrieved by PTR_ERR macros .
2005-09-10 00:04:22 +04:00
*
2006-01-08 12:04:58 +03:00
* @ m : mux data
* @ tc : request to be sent
* @ cb : callback function to call when response is received
* @ cba : parameter to pass to the callback function
2005-09-10 00:04:22 +04:00
*/
2007-07-11 02:57:28 +04:00
static struct p9_req * p9_send_request ( struct p9_conn * m ,
struct p9_fcall * tc ,
p9_conn_req_callback cb , void * cba )
2006-01-08 12:04:58 +03:00
{
int n ;
2007-07-11 02:57:28 +04:00
struct p9_req * req ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p task %p tcall %p id %d \n " , m , current ,
2006-01-08 12:04:58 +03:00
tc , tc - > id ) ;
if ( m - > err < 0 )
return ERR_PTR ( m - > err ) ;
2007-07-11 02:57:28 +04:00
req = kmalloc ( sizeof ( struct p9_req ) , GFP_KERNEL ) ;
2006-01-08 12:04:58 +03:00
if ( ! req )
return ERR_PTR ( - ENOMEM ) ;
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
if ( tc - > id = = P9_TVERSION )
n = P9_NOTAG ;
2006-01-08 12:04:58 +03:00
else
2007-07-11 02:57:28 +04:00
n = p9_mux_get_tag ( m ) ;
2006-01-08 12:04:58 +03:00
if ( n < 0 )
return ERR_PTR ( - ENOMEM ) ;
2007-07-11 02:57:28 +04:00
p9_set_tag ( tc , n ) ;
# ifdef CONFIG_NET_9P_DEBUG
if ( ( p9_debug_level & P9_DEBUG_FCALL ) = = P9_DEBUG_FCALL ) {
2006-03-25 14:07:25 +03:00
char buf [ 150 ] ;
2007-07-11 02:57:28 +04:00
p9_printfcall ( buf , sizeof ( buf ) , tc , * m - > extended ) ;
2006-03-25 14:07:25 +03:00
printk ( KERN_NOTICE " <<< %p %s \n " , m , buf ) ;
}
2007-07-11 02:57:28 +04:00
# endif
2006-03-25 14:07:25 +03:00
2006-05-15 20:44:21 +04:00
spin_lock_init ( & req - > lock ) ;
2006-01-08 12:04:58 +03:00
req - > tag = n ;
req - > tcall = tc ;
req - > rcall = NULL ;
req - > err = 0 ;
req - > cb = cb ;
req - > cba = cba ;
2006-05-15 20:44:21 +04:00
req - > flush = None ;
2006-01-08 12:04:58 +03:00
spin_lock ( & m - > lock ) ;
list_add_tail ( & req - > req_list , & m - > unsent_req_list ) ;
spin_unlock ( & m - > lock ) ;
if ( test_and_clear_bit ( Wpending , & m - > wsched ) )
n = POLLOUT ;
else
n = m - > trans - > poll ( m - > trans , NULL ) ;
if ( n & POLLOUT & & ! test_and_set_bit ( Wworksched , & m - > wsched ) )
2007-07-11 02:57:28 +04:00
queue_work ( p9_mux_wq , & m - > wq ) ;
2006-01-08 12:04:58 +03:00
return req ;
}
2007-07-11 02:57:28 +04:00
static void p9_mux_free_request ( struct p9_conn * m , struct p9_req * req )
2006-05-15 20:44:21 +04:00
{
2007-07-11 02:57:28 +04:00
p9_mux_put_tag ( m , req - > tag ) ;
2006-05-15 20:44:21 +04:00
kfree ( req ) ;
}
2007-07-11 02:57:28 +04:00
static void p9_mux_flush_cb ( struct p9_req * freq , void * a )
2005-09-10 00:04:22 +04:00
{
2007-07-11 02:57:28 +04:00
p9_conn_req_callback cb ;
2006-01-08 12:04:58 +03:00
int tag ;
2007-07-11 02:57:28 +04:00
struct p9_conn * m ;
struct p9_req * req , * rreq , * rptr ;
2006-01-08 12:04:58 +03:00
m = a ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p tc %p rc %p err %d oldtag %d \n " , m ,
2006-05-15 20:44:21 +04:00
freq - > tcall , freq - > rcall , freq - > err ,
freq - > tcall - > params . tflush . oldtag ) ;
2006-01-08 12:04:58 +03:00
spin_lock ( & m - > lock ) ;
cb = NULL ;
2006-05-15 20:44:21 +04:00
tag = freq - > tcall - > params . tflush . oldtag ;
req = NULL ;
list_for_each_entry_safe ( rreq , rptr , & m - > req_list , req_list ) {
if ( rreq - > tag = = tag ) {
req = rreq ;
2006-01-08 12:04:58 +03:00
list_del ( & req - > req_list ) ;
break ;
}
}
2006-05-15 20:44:21 +04:00
spin_unlock ( & m - > lock ) ;
2006-01-08 12:04:58 +03:00
2006-05-15 20:44:21 +04:00
if ( req ) {
spin_lock ( & req - > lock ) ;
req - > flush = Flushed ;
spin_unlock ( & req - > lock ) ;
if ( req - > cb )
( * req - > cb ) ( req , req - > cba ) ;
else
kfree ( req - > rcall ) ;
wake_up ( & m - > equeue ) ;
}
2006-01-08 12:04:58 +03:00
2006-05-15 20:44:21 +04:00
kfree ( freq - > tcall ) ;
kfree ( freq - > rcall ) ;
2007-07-11 02:57:28 +04:00
p9_mux_free_request ( m , freq ) ;
2006-01-08 12:04:58 +03:00
}
2006-05-15 20:44:21 +04:00
static int
2007-07-11 02:57:28 +04:00
p9_mux_flush_request ( struct p9_conn * m , struct p9_req * req )
2006-01-08 12:04:58 +03:00
{
2007-07-11 02:57:28 +04:00
struct p9_fcall * fc ;
struct p9_req * rreq , * rptr ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p req %p tag %d \n " , m , req , req - > tag ) ;
2006-01-08 12:04:58 +03:00
2006-05-15 20:44:21 +04:00
/* if a response was received for a request, do nothing */
spin_lock ( & req - > lock ) ;
if ( req - > rcall | | req - > err ) {
spin_unlock ( & req - > lock ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX ,
" mux %p req %p response already received \n " , m , req ) ;
2006-05-15 20:44:21 +04:00
return 0 ;
}
req - > flush = Flushing ;
spin_unlock ( & req - > lock ) ;
spin_lock ( & m - > lock ) ;
/* if the request is not sent yet, just remove it from the list */
list_for_each_entry_safe ( rreq , rptr , & m - > unsent_req_list , req_list ) {
if ( rreq - > tag = = req - > tag ) {
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX ,
" mux %p req %p request is not sent yet \n " , m , req ) ;
2006-05-15 20:44:21 +04:00
list_del ( & rreq - > req_list ) ;
req - > flush = Flushed ;
spin_unlock ( & m - > lock ) ;
if ( req - > cb )
( * req - > cb ) ( req , req - > cba ) ;
return 0 ;
}
}
spin_unlock ( & m - > lock ) ;
clear_thread_flag ( TIF_SIGPENDING ) ;
2007-07-11 02:57:28 +04:00
fc = p9_create_tflush ( req - > tag ) ;
p9_send_request ( m , fc , p9_mux_flush_cb , m ) ;
2006-05-15 20:44:21 +04:00
return 1 ;
2006-01-08 12:04:58 +03:00
}
static void
2007-07-11 02:57:28 +04:00
p9_conn_rpc_cb ( struct p9_req * req , void * a )
2006-01-08 12:04:58 +03:00
{
2007-07-11 02:57:28 +04:00
struct p9_mux_rpc * r ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " req %p r %p \n " , req , a ) ;
2006-01-08 12:04:58 +03:00
r = a ;
2006-05-15 20:44:21 +04:00
r - > rcall = req - > rcall ;
r - > err = req - > err ;
2007-07-11 02:57:28 +04:00
if ( req - > flush ! = None & & ! req - > err )
2006-05-15 20:44:21 +04:00
r - > err = - ERESTARTSYS ;
2006-01-08 12:04:58 +03:00
wake_up ( & r - > wqueue ) ;
}
/**
2007-07-11 02:57:28 +04:00
* p9_mux_rpc - sends 9 P request and waits until a response is available .
2006-01-08 12:04:58 +03:00
* The function can be interrupted .
* @ m : mux data
* @ tc : request to be sent
* @ rc : pointer where a pointer to the response is stored
*/
int
2007-07-11 02:57:28 +04:00
p9_conn_rpc ( struct p9_conn * m , struct p9_fcall * tc ,
struct p9_fcall * * rc )
2006-01-08 12:04:58 +03:00
{
2006-05-15 20:44:21 +04:00
int err , sigpending ;
2006-01-08 12:04:58 +03:00
unsigned long flags ;
2007-07-11 02:57:28 +04:00
struct p9_req * req ;
struct p9_mux_rpc r ;
2006-01-08 12:04:58 +03:00
r . err = 0 ;
2006-05-15 20:44:21 +04:00
r . tcall = tc ;
2006-01-08 12:04:58 +03:00
r . rcall = NULL ;
r . m = m ;
init_waitqueue_head ( & r . wqueue ) ;
if ( rc )
* rc = NULL ;
2006-05-15 20:44:21 +04:00
sigpending = 0 ;
if ( signal_pending ( current ) ) {
sigpending = 1 ;
clear_thread_flag ( TIF_SIGPENDING ) ;
}
2007-07-11 02:57:28 +04:00
req = p9_send_request ( m , tc , p9_conn_rpc_cb , & r ) ;
2006-01-08 12:04:58 +03:00
if ( IS_ERR ( req ) ) {
err = PTR_ERR ( req ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " error %d \n " , err ) ;
2006-05-15 20:44:21 +04:00
return err ;
2006-01-08 12:04:58 +03:00
}
err = wait_event_interruptible ( r . wqueue , r . rcall ! = NULL | | r . err < 0 ) ;
if ( r . err < 0 )
err = r . err ;
2007-07-11 02:57:28 +04:00
if ( err = = - ERESTARTSYS & & m - > trans - > status = = Connected
& & m - > err = = 0 ) {
if ( p9_mux_flush_request ( m , req ) ) {
2006-05-15 20:44:21 +04:00
/* wait until we get response of the flush message */
do {
clear_thread_flag ( TIF_SIGPENDING ) ;
err = wait_event_interruptible ( r . wqueue ,
r . rcall | | r . err ) ;
2007-07-11 02:57:28 +04:00
} while ( ! r . rcall & & ! r . err & & err = = - ERESTARTSYS & &
m - > trans - > status = = Connected & & ! m - > err ) ;
2006-06-28 15:26:50 +04:00
err = - ERESTARTSYS ;
2006-05-15 20:44:21 +04:00
}
sigpending = 1 ;
}
2006-01-08 12:04:58 +03:00
2006-05-15 20:44:21 +04:00
if ( sigpending ) {
2006-01-08 12:04:58 +03:00
spin_lock_irqsave ( & current - > sighand - > siglock , flags ) ;
recalc_sigpending ( ) ;
spin_unlock_irqrestore ( & current - > sighand - > siglock , flags ) ;
2005-09-10 00:04:22 +04:00
}
2006-05-15 20:44:21 +04:00
if ( rc )
* rc = r . rcall ;
else
2006-01-08 12:04:58 +03:00
kfree ( r . rcall ) ;
2006-05-15 20:44:21 +04:00
2007-07-11 02:57:28 +04:00
p9_mux_free_request ( m , req ) ;
2006-05-15 20:44:21 +04:00
if ( err > 0 )
err = - EIO ;
2006-01-08 12:04:58 +03:00
return err ;
}
2007-07-11 02:57:28 +04:00
EXPORT_SYMBOL ( p9_conn_rpc ) ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
# ifdef P9_NONBLOCK
2006-01-08 12:04:58 +03:00
/**
2007-07-11 02:57:28 +04:00
* p9_conn_rpcnb - sends 9 P request without waiting for response .
2006-01-08 12:04:58 +03:00
* @ m : mux data
* @ tc : request to be sent
* @ cb : callback function to be called when response arrives
* @ cba : value to pass to the callback function
*/
2007-07-11 02:57:28 +04:00
int p9_conn_rpcnb ( struct p9_conn * m , struct p9_fcall * tc ,
p9_conn_req_callback cb , void * a )
2006-01-08 12:04:58 +03:00
{
int err ;
2007-07-11 02:57:28 +04:00
struct p9_req * req ;
2006-01-08 12:04:58 +03:00
2007-07-11 02:57:28 +04:00
req = p9_send_request ( m , tc , cb , a ) ;
2006-01-08 12:04:58 +03:00
if ( IS_ERR ( req ) ) {
err = PTR_ERR ( req ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " error %d \n " , err ) ;
2006-01-08 12:04:58 +03:00
return PTR_ERR ( req ) ;
}
2005-09-10 00:04:22 +04:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_MUX , " mux %p tc %p tag %d \n " , m , tc , req - > tag ) ;
2005-09-10 00:04:22 +04:00
return 0 ;
}
2007-07-11 02:57:28 +04:00
EXPORT_SYMBOL ( p9_conn_rpcnb ) ;
# endif /* P9_NONBLOCK */
2006-01-08 12:04:58 +03:00
/**
2007-07-11 02:57:28 +04:00
* p9_conn_cancel - cancel all pending requests with error
2006-01-08 12:04:58 +03:00
* @ m : mux data
* @ err : error code
*/
2007-07-11 02:57:28 +04:00
void p9_conn_cancel ( struct p9_conn * m , int err )
2006-01-08 12:04:58 +03:00
{
2007-07-11 02:57:28 +04:00
struct p9_req * req , * rtmp ;
2006-01-08 12:04:58 +03:00
LIST_HEAD ( cancel_list ) ;
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_ERROR , " mux %p err %d \n " , m , err ) ;
2006-01-08 12:04:58 +03:00
m - > err = err ;
spin_lock ( & m - > lock ) ;
list_for_each_entry_safe ( req , rtmp , & m - > req_list , req_list ) {
list_move ( & req - > req_list , & cancel_list ) ;
}
2006-05-15 20:44:21 +04:00
list_for_each_entry_safe ( req , rtmp , & m - > unsent_req_list , req_list ) {
list_move ( & req - > req_list , & cancel_list ) ;
}
2006-01-08 12:04:58 +03:00
spin_unlock ( & m - > lock ) ;
list_for_each_entry_safe ( req , rtmp , & cancel_list , req_list ) {
list_del ( & req - > req_list ) ;
if ( ! req - > err )
req - > err = err ;
if ( req - > cb )
2006-05-15 20:44:21 +04:00
( * req - > cb ) ( req , req - > cba ) ;
2006-01-08 12:04:58 +03:00
else
kfree ( req - > rcall ) ;
}
wake_up ( & m - > equeue ) ;
}
2007-07-11 02:57:28 +04:00
EXPORT_SYMBOL ( p9_conn_cancel ) ;
2006-01-08 12:05:00 +03:00
2007-07-11 02:57:28 +04:00
static u16 p9_mux_get_tag ( struct p9_conn * m )
2006-01-08 12:05:00 +03:00
{
int tag ;
2007-07-11 02:57:28 +04:00
tag = p9_idpool_get ( m - > tagpool ) ;
2006-01-08 12:05:00 +03:00
if ( tag < 0 )
2007-07-11 02:57:28 +04:00
return P9_NOTAG ;
2006-01-08 12:05:00 +03:00
else
return ( u16 ) tag ;
}
2007-07-11 02:57:28 +04:00
static void p9_mux_put_tag ( struct p9_conn * m , u16 tag )
2006-01-08 12:05:00 +03:00
{
2007-07-11 02:57:28 +04:00
if ( tag ! = P9_NOTAG & & p9_idpool_check ( tag , m - > tagpool ) )
p9_idpool_put ( tag , m - > tagpool ) ;
2006-01-08 12:05:00 +03:00
}