123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- /*
- * smbiod.c
- *
- * Copyright (C) 2000, Charles Loep / Corel Corp.
- * Copyright (C) 2001, Urban Widmark
- */
- #include <linux/config.h>
- #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/smp_lock.h>
- #include <linux/module.h>
- #include <linux/net.h>
- #include <linux/kthread.h>
- #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;
- static struct task_struct *smbiod_thread;
- 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)
- {
- struct task_struct *tsk;
- int err = 0;
- if (smbiod_state != SMBIOD_DEAD)
- return 0;
- smbiod_state = SMBIOD_STARTING;
- __module_get(THIS_MODULE);
- spin_unlock(&servers_lock);
- tsk = kthread_run(smbiod, NULL, "smbiod");
- if (IS_ERR(tsk)) {
- err = PTR_ERR(tsk);
- module_put(THIS_MODULE);
- }
- spin_lock(&servers_lock);
- if (err < 0) {
- smbiod_state = SMBIOD_DEAD;
- smbiod_thread = NULL;
- } else {
- smbiod_state = SMBIOD_RUNNING;
- smbiod_thread = tsk;
- }
- return err;
- }
- /*
- * 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;
- pid_t pid = server->conn_pid;
- 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);
- list_move(&req->rq_queue, &server->xmitq);
- 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.
- */
- result = kill_proc(pid, SIGUSR1, 1);
- 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:
- 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)
- {
- allow_signal(SIGKILL);
- 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);
- }
|