2005-04-17 02:20:36 +04:00
/*
* smbiod . c
*
* Copyright ( C ) 2000 , Charles Loep / Corel Corp .
* Copyright ( C ) 2001 , Urban Widmark
*/
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/string.h>
# include <linux/stat.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/file.h>
# include <linux/dcache.h>
# include <linux/module.h>
# include <linux/net.h>
2006-06-25 16:49:00 +04:00
# include <linux/kthread.h>
2005-04-17 02:20:36 +04:00
# include <net/ip.h>
# include <linux/smb_fs.h>
# include <linux/smbno.h>
# include <linux/smb_mount.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include "smb_debug.h"
# include "request.h"
# include "proto.h"
enum smbiod_state {
SMBIOD_DEAD ,
SMBIOD_STARTING ,
SMBIOD_RUNNING ,
} ;
static enum smbiod_state smbiod_state = SMBIOD_DEAD ;
2006-06-25 16:49:00 +04:00
static struct task_struct * smbiod_thread ;
2005-04-17 02:20:36 +04:00
static DECLARE_WAIT_QUEUE_HEAD ( smbiod_wait ) ;
static LIST_HEAD ( smb_servers ) ;
static DEFINE_SPINLOCK ( servers_lock ) ;
# define SMBIOD_DATA_READY (1<<0)
static long smbiod_flags ;
static int smbiod ( void * ) ;
static int smbiod_start ( void ) ;
/*
* called when there ' s work for us to do
*/
void smbiod_wake_up ( void )
{
if ( smbiod_state = = SMBIOD_DEAD )
return ;
set_bit ( SMBIOD_DATA_READY , & smbiod_flags ) ;
wake_up_interruptible ( & smbiod_wait ) ;
}
/*
* start smbiod if none is running
*/
static int smbiod_start ( void )
{
2006-06-25 16:49:00 +04:00
struct task_struct * tsk ;
int err = 0 ;
2005-04-17 02:20:36 +04:00
if ( smbiod_state ! = SMBIOD_DEAD )
return 0 ;
smbiod_state = SMBIOD_STARTING ;
__module_get ( THIS_MODULE ) ;
spin_unlock ( & servers_lock ) ;
2006-06-25 16:49:00 +04:00
tsk = kthread_run ( smbiod , NULL , " smbiod " ) ;
if ( IS_ERR ( tsk ) ) {
err = PTR_ERR ( tsk ) ;
2005-04-17 02:20:36 +04:00
module_put ( THIS_MODULE ) ;
2006-06-25 16:49:00 +04:00
}
2005-04-17 02:20:36 +04:00
spin_lock ( & servers_lock ) ;
2006-06-25 16:49:00 +04:00
if ( err < 0 ) {
smbiod_state = SMBIOD_DEAD ;
smbiod_thread = NULL ;
} else {
smbiod_state = SMBIOD_RUNNING ;
smbiod_thread = tsk ;
}
return err ;
2005-04-17 02:20:36 +04:00
}
/*
* register a server & start smbiod if necessary
*/
int smbiod_register_server ( struct smb_sb_info * server )
{
int ret ;
spin_lock ( & servers_lock ) ;
list_add ( & server - > entry , & smb_servers ) ;
VERBOSE ( " %p \n " , server ) ;
ret = smbiod_start ( ) ;
spin_unlock ( & servers_lock ) ;
return ret ;
}
/*
* Unregister a server
* Must be called with the server lock held .
*/
void smbiod_unregister_server ( struct smb_sb_info * server )
{
spin_lock ( & servers_lock ) ;
list_del_init ( & server - > entry ) ;
VERBOSE ( " %p \n " , server ) ;
spin_unlock ( & servers_lock ) ;
smbiod_wake_up ( ) ;
smbiod_flush ( server ) ;
}
void smbiod_flush ( struct smb_sb_info * server )
{
struct list_head * tmp , * n ;
struct smb_request * req ;
list_for_each_safe ( tmp , n , & server - > xmitq ) {
req = list_entry ( tmp , struct smb_request , rq_queue ) ;
req - > rq_errno = - EIO ;
list_del_init ( & req - > rq_queue ) ;
smb_rput ( req ) ;
wake_up_interruptible ( & req - > rq_wait ) ;
}
list_for_each_safe ( tmp , n , & server - > recvq ) {
req = list_entry ( tmp , struct smb_request , rq_queue ) ;
req - > rq_errno = - EIO ;
list_del_init ( & req - > rq_queue ) ;
smb_rput ( req ) ;
wake_up_interruptible ( & req - > rq_wait ) ;
}
}
/*
* Wake up smbmount and make it reconnect to the server .
* This must be called with the server locked .
*
* FIXME : add smbconnect version to this
*/
int smbiod_retry ( struct smb_sb_info * server )
{
struct list_head * head ;
struct smb_request * req ;
2006-12-13 11:35:10 +03:00
struct pid * pid = get_pid ( server - > conn_pid ) ;
2005-04-17 02:20:36 +04:00
int result = 0 ;
VERBOSE ( " state: %d \n " , server - > state ) ;
if ( server - > state = = CONN_VALID | | server - > state = = CONN_RETRYING )
goto out ;
smb_invalidate_inodes ( server ) ;
/*
* Some requests are meaningless after a retry , so we abort them .
* One example are all requests using ' fileid ' since the files are
* closed on retry .
*/
head = server - > xmitq . next ;
while ( head ! = & server - > xmitq ) {
req = list_entry ( head , struct smb_request , rq_queue ) ;
head = head - > next ;
req - > rq_bytes_sent = 0 ;
if ( req - > rq_flags & SMB_REQ_NORETRY ) {
VERBOSE ( " aborting request %p on xmitq \n " , req ) ;
req - > rq_errno = - EIO ;
list_del_init ( & req - > rq_queue ) ;
smb_rput ( req ) ;
wake_up_interruptible ( & req - > rq_wait ) ;
}
}
/*
* FIXME : test the code for retrying request we already sent
*/
head = server - > recvq . next ;
while ( head ! = & server - > recvq ) {
req = list_entry ( head , struct smb_request , rq_queue ) ;
head = head - > next ;
#if 0
if ( req - > rq_flags & SMB_REQ_RETRY ) {
/* must move the request to the xmitq */
VERBOSE ( " retrying request %p on recvq \n " , req ) ;
2006-06-26 11:24:46 +04:00
list_move ( & req - > rq_queue , & server - > xmitq ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
# endif
VERBOSE ( " aborting request %p on recvq \n " , req ) ;
/* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */
req - > rq_errno = - EIO ;
list_del_init ( & req - > rq_queue ) ;
smb_rput ( req ) ;
wake_up_interruptible ( & req - > rq_wait ) ;
}
smb_close_socket ( server ) ;
if ( pid = = 0 ) {
/* FIXME: this is fatal, umount? */
printk ( KERN_ERR " smb_retry: no connection process \n " ) ;
server - > state = CONN_RETRIED ;
goto out ;
}
/*
* Change state so that only one retry per server will be started .
*/
server - > state = CONN_RETRYING ;
/*
* Note : use the " priv " flag , as a user process may need to reconnect .
*/
2006-12-13 11:35:10 +03:00
result = kill_pid ( pid , SIGUSR1 , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( result ) {
/* FIXME: this is most likely fatal, umount? */
printk ( KERN_ERR " smb_retry: signal failed [%d] \n " , result ) ;
goto out ;
}
VERBOSE ( " signalled pid %d \n " , pid ) ;
/* FIXME: The retried requests should perhaps get a "time boost". */
out :
2006-12-13 11:35:10 +03:00
put_pid ( pid ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
/*
* Currently handles lockingX packets .
*/
static void smbiod_handle_request ( struct smb_sb_info * server )
{
PARANOIA ( " smbiod got a request ... and we don't implement oplocks! \n " ) ;
server - > rstate = SMB_RECV_DROP ;
}
/*
* Do some IO for one server .
*/
static void smbiod_doio ( struct smb_sb_info * server )
{
int result ;
int maxwork = 7 ;
if ( server - > state ! = CONN_VALID )
goto out ;
do {
result = smb_request_recv ( server ) ;
if ( result < 0 ) {
server - > state = CONN_INVALID ;
smbiod_retry ( server ) ;
goto out ; /* reconnecting is slow */
} else if ( server - > rstate = = SMB_RECV_REQUEST )
smbiod_handle_request ( server ) ;
} while ( result > 0 & & maxwork - - > 0 ) ;
/*
* If there is more to read then we want to be sure to wake up again .
*/
if ( server - > state ! = CONN_VALID )
goto out ;
if ( smb_recv_available ( server ) > 0 )
set_bit ( SMBIOD_DATA_READY , & smbiod_flags ) ;
do {
result = smb_request_send_server ( server ) ;
if ( result < 0 ) {
server - > state = CONN_INVALID ;
smbiod_retry ( server ) ;
goto out ; /* reconnecting is slow */
}
} while ( result > 0 ) ;
/*
* If the last request was not sent out we want to wake up again .
*/
if ( ! list_empty ( & server - > xmitq ) )
set_bit ( SMBIOD_DATA_READY , & smbiod_flags ) ;
out :
return ;
}
/*
* smbiod kernel thread
*/
static int smbiod ( void * unused )
{
VERBOSE ( " SMB Kernel thread starting (%d) ... \n " , current - > pid ) ;
for ( ; ; ) {
struct smb_sb_info * server ;
struct list_head * pos , * n ;
/* FIXME: Use poll? */
wait_event_interruptible ( smbiod_wait ,
test_bit ( SMBIOD_DATA_READY , & smbiod_flags ) ) ;
if ( signal_pending ( current ) ) {
spin_lock ( & servers_lock ) ;
smbiod_state = SMBIOD_DEAD ;
spin_unlock ( & servers_lock ) ;
break ;
}
clear_bit ( SMBIOD_DATA_READY , & smbiod_flags ) ;
spin_lock ( & servers_lock ) ;
if ( list_empty ( & smb_servers ) ) {
smbiod_state = SMBIOD_DEAD ;
spin_unlock ( & servers_lock ) ;
break ;
}
list_for_each_safe ( pos , n , & smb_servers ) {
server = list_entry ( pos , struct smb_sb_info , entry ) ;
VERBOSE ( " checking server %p \n " , server ) ;
if ( server - > state = = CONN_VALID ) {
spin_unlock ( & servers_lock ) ;
smb_lock_server ( server ) ;
smbiod_doio ( server ) ;
smb_unlock_server ( server ) ;
spin_lock ( & servers_lock ) ;
}
}
spin_unlock ( & servers_lock ) ;
}
VERBOSE ( " SMB Kernel thread exiting (%d) ... \n " , current - > pid ) ;
module_put_and_exit ( 0 ) ;
}