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