aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/smbfs/smbiod.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2010-10-04 22:55:57 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2010-10-05 09:08:21 -0700
commit2116b7a473bf1c8d26998b477c294e7fe294921f (patch)
tree5f06aca6b425916f763d83fa4516bca51c8f9a60 /drivers/staging/smbfs/smbiod.c
parent5af74aa5e97fcc0cc3955bc2a7ff6f3a13fa41cb (diff)
downloadkernel_samsung_smdk4412-2116b7a473bf1c8d26998b477c294e7fe294921f.zip
kernel_samsung_smdk4412-2116b7a473bf1c8d26998b477c294e7fe294921f.tar.gz
kernel_samsung_smdk4412-2116b7a473bf1c8d26998b477c294e7fe294921f.tar.bz2
smbfs: move to drivers/staging
smbfs has been scheduled for removal in 2.6.27, so maybe we can now move it to drivers/staging on the way out. smbfs still uses the big kernel lock and nobody is going to fix that, so we should be getting rid of it soon. This removes the 32 bit compat mount and ioctl handling code, which is implemented in common fs code, and moves all smbfs related files into drivers/staging/smbfs. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/smbfs/smbiod.c')
-rw-r--r--drivers/staging/smbfs/smbiod.c343
1 files changed, 343 insertions, 0 deletions
diff --git a/drivers/staging/smbfs/smbiod.c b/drivers/staging/smbfs/smbiod.c
new file mode 100644
index 0000000..ec99892
--- /dev/null
+++ b/drivers/staging/smbfs/smbiod.c
@@ -0,0 +1,343 @@
+/*
+ * 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/init.h>
+#include <linux/file.h>
+#include <linux/dcache.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/kthread.h>
+#include <net/ip.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "smb_fs.h"
+#include "smbno.h"
+#include "smb_mount.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 unsigned 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;
+ struct pid *pid = get_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) {
+ /* 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_pid(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_nr(pid));
+
+ /* FIXME: The retried requests should perhaps get a "time boost". */
+
+out:
+ put_pid(pid);
+ 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);
+}