linux/fs/smbfs/smbiod.c
<<
>>
Prefs
   1/*
   2 *  smbiod.c
   3 *
   4 *  Copyright (C) 2000, Charles Loep / Corel Corp.
   5 *  Copyright (C) 2001, Urban Widmark
   6 */
   7
   8
   9#include <linux/sched.h>
  10#include <linux/kernel.h>
  11#include <linux/mm.h>
  12#include <linux/string.h>
  13#include <linux/stat.h>
  14#include <linux/errno.h>
  15#include <linux/init.h>
  16#include <linux/file.h>
  17#include <linux/dcache.h>
  18#include <linux/module.h>
  19#include <linux/net.h>
  20#include <linux/kthread.h>
  21#include <net/ip.h>
  22
  23#include <linux/smb_fs.h>
  24#include <linux/smbno.h>
  25#include <linux/smb_mount.h>
  26
  27#include <asm/system.h>
  28#include <asm/uaccess.h>
  29
  30#include "smb_debug.h"
  31#include "request.h"
  32#include "proto.h"
  33
  34enum smbiod_state {
  35        SMBIOD_DEAD,
  36        SMBIOD_STARTING,
  37        SMBIOD_RUNNING,
  38};
  39
  40static enum smbiod_state smbiod_state = SMBIOD_DEAD;
  41static struct task_struct *smbiod_thread;
  42static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait);
  43static LIST_HEAD(smb_servers);
  44static DEFINE_SPINLOCK(servers_lock);
  45
  46#define SMBIOD_DATA_READY       (1<<0)
  47static unsigned long smbiod_flags;
  48
  49static int smbiod(void *);
  50static int smbiod_start(void);
  51
  52/*
  53 * called when there's work for us to do
  54 */
  55void smbiod_wake_up(void)
  56{
  57        if (smbiod_state == SMBIOD_DEAD)
  58                return;
  59        set_bit(SMBIOD_DATA_READY, &smbiod_flags);
  60        wake_up_interruptible(&smbiod_wait);
  61}
  62
  63/*
  64 * start smbiod if none is running
  65 */
  66static int smbiod_start(void)
  67{
  68        struct task_struct *tsk;
  69        int err = 0;
  70
  71        if (smbiod_state != SMBIOD_DEAD)
  72                return 0;
  73        smbiod_state = SMBIOD_STARTING;
  74        __module_get(THIS_MODULE);
  75        spin_unlock(&servers_lock);
  76        tsk = kthread_run(smbiod, NULL, "smbiod");
  77        if (IS_ERR(tsk)) {
  78                err = PTR_ERR(tsk);
  79                module_put(THIS_MODULE);
  80        }
  81
  82        spin_lock(&servers_lock);
  83        if (err < 0) {
  84                smbiod_state = SMBIOD_DEAD;
  85                smbiod_thread = NULL;
  86        } else {
  87                smbiod_state = SMBIOD_RUNNING;
  88                smbiod_thread = tsk;
  89        }
  90        return err;
  91}
  92
  93/*
  94 * register a server & start smbiod if necessary
  95 */
  96int smbiod_register_server(struct smb_sb_info *server)
  97{
  98        int ret;
  99        spin_lock(&servers_lock);
 100        list_add(&server->entry, &smb_servers);
 101        VERBOSE("%p\n", server);
 102        ret = smbiod_start();
 103        spin_unlock(&servers_lock);
 104        return ret;
 105}
 106
 107/*
 108 * Unregister a server
 109 * Must be called with the server lock held.
 110 */
 111void smbiod_unregister_server(struct smb_sb_info *server)
 112{
 113        spin_lock(&servers_lock);
 114        list_del_init(&server->entry);
 115        VERBOSE("%p\n", server);
 116        spin_unlock(&servers_lock);
 117
 118        smbiod_wake_up();
 119        smbiod_flush(server);
 120}
 121
 122void smbiod_flush(struct smb_sb_info *server)
 123{
 124        struct list_head *tmp, *n;
 125        struct smb_request *req;
 126
 127        list_for_each_safe(tmp, n, &server->xmitq) {
 128                req = list_entry(tmp, struct smb_request, rq_queue);
 129                req->rq_errno = -EIO;
 130                list_del_init(&req->rq_queue);
 131                smb_rput(req);
 132                wake_up_interruptible(&req->rq_wait);
 133        }
 134        list_for_each_safe(tmp, n, &server->recvq) {
 135                req = list_entry(tmp, struct smb_request, rq_queue);
 136                req->rq_errno = -EIO;
 137                list_del_init(&req->rq_queue);
 138                smb_rput(req);
 139                wake_up_interruptible(&req->rq_wait);
 140        }
 141}
 142
 143/*
 144 * Wake up smbmount and make it reconnect to the server.
 145 * This must be called with the server locked.
 146 *
 147 * FIXME: add smbconnect version to this
 148 */
 149int smbiod_retry(struct smb_sb_info *server)
 150{
 151        struct list_head *head;
 152        struct smb_request *req;
 153        struct pid *pid = get_pid(server->conn_pid);
 154        int result = 0;
 155
 156        VERBOSE("state: %d\n", server->state);
 157        if (server->state == CONN_VALID || server->state == CONN_RETRYING)
 158                goto out;
 159
 160        smb_invalidate_inodes(server);
 161
 162        /*
 163         * Some requests are meaningless after a retry, so we abort them.
 164         * One example are all requests using 'fileid' since the files are
 165         * closed on retry.
 166         */
 167        head = server->xmitq.next;
 168        while (head != &server->xmitq) {
 169                req = list_entry(head, struct smb_request, rq_queue);
 170                head = head->next;
 171
 172                req->rq_bytes_sent = 0;
 173                if (req->rq_flags & SMB_REQ_NORETRY) {
 174                        VERBOSE("aborting request %p on xmitq\n", req);
 175                        req->rq_errno = -EIO;
 176                        list_del_init(&req->rq_queue);
 177                        smb_rput(req);
 178                        wake_up_interruptible(&req->rq_wait);
 179                }
 180        }
 181
 182        /*
 183         * FIXME: test the code for retrying request we already sent
 184         */
 185        head = server->recvq.next;
 186        while (head != &server->recvq) {
 187                req = list_entry(head, struct smb_request, rq_queue);
 188                head = head->next;
 189#if 0
 190                if (req->rq_flags & SMB_REQ_RETRY) {
 191                        /* must move the request to the xmitq */
 192                        VERBOSE("retrying request %p on recvq\n", req);
 193                        list_move(&req->rq_queue, &server->xmitq);
 194                        continue;
 195                }
 196#endif
 197
 198                VERBOSE("aborting request %p on recvq\n", req);
 199                /* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */
 200                req->rq_errno = -EIO;
 201                list_del_init(&req->rq_queue);
 202                smb_rput(req);
 203                wake_up_interruptible(&req->rq_wait);
 204        }
 205
 206        smb_close_socket(server);
 207
 208        if (!pid) {
 209                /* FIXME: this is fatal, umount? */
 210                printk(KERN_ERR "smb_retry: no connection process\n");
 211                server->state = CONN_RETRIED;
 212                goto out;
 213        }
 214
 215        /*
 216         * Change state so that only one retry per server will be started.
 217         */
 218        server->state = CONN_RETRYING;
 219
 220        /*
 221         * Note: use the "priv" flag, as a user process may need to reconnect.
 222         */
 223        result = kill_pid(pid, SIGUSR1, 1);
 224        if (result) {
 225                /* FIXME: this is most likely fatal, umount? */
 226                printk(KERN_ERR "smb_retry: signal failed [%d]\n", result);
 227                goto out;
 228        }
 229        VERBOSE("signalled pid %d\n", pid_nr(pid));
 230
 231        /* FIXME: The retried requests should perhaps get a "time boost". */
 232
 233out:
 234        put_pid(pid);
 235        return result;
 236}
 237
 238/*
 239 * Currently handles lockingX packets.
 240 */
 241static void smbiod_handle_request(struct smb_sb_info *server)
 242{
 243        PARANOIA("smbiod got a request ... and we don't implement oplocks!\n");
 244        server->rstate = SMB_RECV_DROP;
 245}
 246
 247/*
 248 * Do some IO for one server.
 249 */
 250static void smbiod_doio(struct smb_sb_info *server)
 251{
 252        int result;
 253        int maxwork = 7;
 254
 255        if (server->state != CONN_VALID)
 256                goto out;
 257
 258        do {
 259                result = smb_request_recv(server);
 260                if (result < 0) {
 261                        server->state = CONN_INVALID;
 262                        smbiod_retry(server);
 263                        goto out;       /* reconnecting is slow */
 264                } else if (server->rstate == SMB_RECV_REQUEST)
 265                        smbiod_handle_request(server);
 266        } while (result > 0 && maxwork-- > 0);
 267
 268        /*
 269         * If there is more to read then we want to be sure to wake up again.
 270         */
 271        if (server->state != CONN_VALID)
 272                goto out;
 273        if (smb_recv_available(server) > 0)
 274                set_bit(SMBIOD_DATA_READY, &smbiod_flags);
 275
 276        do {
 277                result = smb_request_send_server(server);
 278                if (result < 0) {
 279                        server->state = CONN_INVALID;
 280                        smbiod_retry(server);
 281                        goto out;       /* reconnecting is slow */
 282                }
 283        } while (result > 0);
 284
 285        /*
 286         * If the last request was not sent out we want to wake up again.
 287         */
 288        if (!list_empty(&server->xmitq))
 289                set_bit(SMBIOD_DATA_READY, &smbiod_flags);
 290
 291out:
 292        return;
 293}
 294
 295/*
 296 * smbiod kernel thread
 297 */
 298static int smbiod(void *unused)
 299{
 300        VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid);
 301
 302        for (;;) {
 303                struct smb_sb_info *server;
 304                struct list_head *pos, *n;
 305
 306                /* FIXME: Use poll? */
 307                wait_event_interruptible(smbiod_wait,
 308                         test_bit(SMBIOD_DATA_READY, &smbiod_flags));
 309                if (signal_pending(current)) {
 310                        spin_lock(&servers_lock);
 311                        smbiod_state = SMBIOD_DEAD;
 312                        spin_unlock(&servers_lock);
 313                        break;
 314                }
 315
 316                clear_bit(SMBIOD_DATA_READY, &smbiod_flags);
 317
 318                spin_lock(&servers_lock);
 319                if (list_empty(&smb_servers)) {
 320                        smbiod_state = SMBIOD_DEAD;
 321                        spin_unlock(&servers_lock);
 322                        break;
 323                }
 324
 325                list_for_each_safe(pos, n, &smb_servers) {
 326                        server = list_entry(pos, struct smb_sb_info, entry);
 327                        VERBOSE("checking server %p\n", server);
 328
 329                        if (server->state == CONN_VALID) {
 330                                spin_unlock(&servers_lock);
 331
 332                                smb_lock_server(server);
 333                                smbiod_doio(server);
 334                                smb_unlock_server(server);
 335
 336                                spin_lock(&servers_lock);
 337                        }
 338                }
 339                spin_unlock(&servers_lock);
 340        }
 341
 342        VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid);
 343        module_put_and_exit(0);
 344}
 345
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.