linux-bk/fs/nfsd/nfssvc.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfsd/nfssvc.c
   3 *
   4 * Central processing for nfsd.
   5 *
   6 * Authors:     Olaf Kirch (okir@monad.swb.de)
   7 *
   8 * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
   9 */
  10
  11#include <linux/config.h>
  12#include <linux/module.h>
  13
  14#include <linux/time.h>
  15#include <linux/errno.h>
  16#include <linux/nfs.h>
  17#include <linux/in.h>
  18#include <linux/uio.h>
  19#include <linux/unistd.h>
  20#include <linux/slab.h>
  21#include <linux/smp.h>
  22#include <linux/smp_lock.h>
  23#include <linux/fs_struct.h>
  24
  25#include <linux/sunrpc/types.h>
  26#include <linux/sunrpc/stats.h>
  27#include <linux/sunrpc/svc.h>
  28#include <linux/sunrpc/svcsock.h>
  29#include <linux/sunrpc/cache.h>
  30#include <linux/nfsd/nfsd.h>
  31#include <linux/nfsd/stats.h>
  32#include <linux/nfsd/cache.h>
  33#include <linux/lockd/bind.h>
  34
  35#define NFSDDBG_FACILITY        NFSDDBG_SVC
  36
  37/* these signals will be delivered to an nfsd thread 
  38 * when handling a request
  39 */
  40#define ALLOWED_SIGS    (sigmask(SIGKILL))
  41/* these signals will be delivered to an nfsd thread
  42 * when not handling a request. i.e. when waiting
  43 */
  44#define SHUTDOWN_SIGS   (sigmask(SIGKILL) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT))
  45/* if the last thread dies with SIGHUP, then the exports table is
  46 * left unchanged ( like 2.4-{0-9} ).  Any other signal will clear
  47 * the exports table (like 2.2).
  48 */
  49#define SIG_NOCLEAN     SIGHUP
  50
  51extern struct svc_program       nfsd_program;
  52static void                     nfsd(struct svc_rqst *rqstp);
  53struct timeval                  nfssvc_boot;
  54static struct svc_serv          *nfsd_serv;
  55static atomic_t                 nfsd_busy;
  56static unsigned long            nfsd_last_call;
  57static spinlock_t               nfsd_call_lock = SPIN_LOCK_UNLOCKED;
  58
  59struct nfsd_list {
  60        struct list_head        list;
  61        struct task_struct      *task;
  62};
  63struct list_head nfsd_list = LIST_HEAD_INIT(nfsd_list);
  64
  65/*
  66 * Maximum number of nfsd processes
  67 */
  68#define NFSD_MAXSERVS           8192
  69
  70int nfsd_nrthreads(void)
  71{
  72        if (nfsd_serv == NULL)
  73                return 0;
  74        else
  75                return nfsd_serv->sv_nrthreads;
  76}
  77
  78int
  79nfsd_svc(unsigned short port, int nrservs)
  80{
  81        int     error;
  82        int     none_left;      
  83        struct list_head *victim;
  84        
  85        lock_kernel();
  86        dprintk("nfsd: creating service\n");
  87        error = -EINVAL;
  88        if (nrservs <= 0)
  89                nrservs = 0;
  90        if (nrservs > NFSD_MAXSERVS)
  91                nrservs = NFSD_MAXSERVS;
  92        
  93        /* Readahead param cache - will no-op if it already exists */
  94        error = nfsd_racache_init(2*nrservs);
  95        nfs4_state_init();
  96        if (error<0)
  97                goto out;
  98        if (!nfsd_serv) {
  99                atomic_set(&nfsd_busy, 0);
 100                error = -ENOMEM;
 101                nfsd_serv = svc_create(&nfsd_program, NFSD_BUFSIZE);
 102                if (nfsd_serv == NULL)
 103                        goto out;
 104                error = svc_makesock(nfsd_serv, IPPROTO_UDP, port);
 105                if (error < 0)
 106                        goto failure;
 107
 108#ifdef CONFIG_NFSD_TCP
 109                error = svc_makesock(nfsd_serv, IPPROTO_TCP, port);
 110                if (error < 0)
 111                        goto failure;
 112#endif
 113                do_gettimeofday(&nfssvc_boot);          /* record boot time */
 114        } else
 115                nfsd_serv->sv_nrthreads++;
 116        nrservs -= (nfsd_serv->sv_nrthreads-1);
 117        while (nrservs > 0) {
 118                nrservs--;
 119                __module_get(THIS_MODULE);
 120                error = svc_create_thread(nfsd, nfsd_serv);
 121                if (error < 0) {
 122                        module_put(THIS_MODULE);
 123                        break;
 124                }
 125        }
 126        victim = nfsd_list.next;
 127        while (nrservs < 0 && victim != &nfsd_list) {
 128                struct nfsd_list *nl =
 129                        list_entry(victim,struct nfsd_list, list);
 130                victim = victim->next;
 131                send_sig(SIG_NOCLEAN, nl->task, 1);
 132                nrservs++;
 133        }
 134 failure:
 135        none_left = (nfsd_serv->sv_nrthreads == 1);
 136        svc_destroy(nfsd_serv);         /* Release server */
 137        if (none_left) {
 138                nfsd_serv = NULL;
 139                nfsd_racache_shutdown();
 140                nfs4_state_shutdown();
 141        }
 142 out:
 143        unlock_kernel();
 144        return error;
 145}
 146
 147static inline void
 148update_thread_usage(int busy_threads)
 149{
 150        unsigned long prev_call;
 151        unsigned long diff;
 152        int decile;
 153
 154        spin_lock(&nfsd_call_lock);
 155        prev_call = nfsd_last_call;
 156        nfsd_last_call = jiffies;
 157        decile = busy_threads*10/nfsdstats.th_cnt;
 158        if (decile>0 && decile <= 10) {
 159                diff = nfsd_last_call - prev_call;
 160                if ( (nfsdstats.th_usage[decile-1] += diff) >= NFSD_USAGE_WRAP)
 161                        nfsdstats.th_usage[decile-1] -= NFSD_USAGE_WRAP;
 162                if (decile == 10)
 163                        nfsdstats.th_fullcnt++;
 164        }
 165        spin_unlock(&nfsd_call_lock);
 166}
 167
 168/*
 169 * This is the NFS server kernel thread
 170 */
 171static void
 172nfsd(struct svc_rqst *rqstp)
 173{
 174        struct svc_serv *serv = rqstp->rq_server;
 175        struct fs_struct *fsp;
 176        int             err;
 177        struct nfsd_list me;
 178        sigset_t shutdown_mask, allowed_mask;
 179
 180        /* Lock module and set up kernel thread */
 181        lock_kernel();
 182        daemonize("nfsd");
 183
 184        /* After daemonize() this kernel thread shares current->fs
 185         * with the init process. We need to create files with a
 186         * umask of 0 instead of init's umask. */
 187        fsp = copy_fs_struct(current->fs);
 188        if (!fsp) {
 189                printk("Unable to start nfsd thread: out of memory\n");
 190                goto out;
 191        }
 192        exit_fs(current);
 193        current->fs = fsp;
 194        current->fs->umask = 0;
 195
 196        siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS);
 197        siginitsetinv(&allowed_mask, ALLOWED_SIGS);
 198
 199        nfsdstats.th_cnt++;
 200
 201        lockd_up();                             /* start lockd */
 202
 203        me.task = current;
 204        list_add(&me.list, &nfsd_list);
 205
 206        unlock_kernel();
 207
 208        /*
 209         * We want less throttling in balance_dirty_pages() so that nfs to
 210         * localhost doesn't cause nfsd to lock up due to all the client's
 211         * dirty pages.
 212         */
 213        current->flags |= PF_LESS_THROTTLE;
 214
 215        /*
 216         * The main request loop
 217         */
 218        for (;;) {
 219                /* Block all but the shutdown signals */
 220                sigprocmask(SIG_SETMASK, &shutdown_mask, NULL);
 221
 222                /*
 223                 * Find a socket with data available and call its
 224                 * recvfrom routine.
 225                 */
 226                while ((err = svc_recv(serv, rqstp,
 227                                       60*60*HZ)) == -EAGAIN)
 228                        ;
 229                if (err < 0)
 230                        break;
 231                update_thread_usage(atomic_read(&nfsd_busy));
 232                atomic_inc(&nfsd_busy);
 233
 234                /* Lock the export hash tables for reading. */
 235                exp_readlock();
 236
 237                /* Process request with signals blocked.  */
 238                sigprocmask(SIG_SETMASK, &allowed_mask, NULL);
 239
 240                svc_process(serv, rqstp);
 241
 242                /* Unlock export hash tables */
 243                exp_readunlock();
 244                update_thread_usage(atomic_read(&nfsd_busy));
 245                atomic_dec(&nfsd_busy);
 246        }
 247
 248        if (err != -EINTR) {
 249                printk(KERN_WARNING "nfsd: terminating on error %d\n", -err);
 250        } else {
 251                unsigned int    signo;
 252
 253                for (signo = 1; signo <= _NSIG; signo++)
 254                        if (sigismember(&current->pending.signal, signo) &&
 255                            !sigismember(&current->blocked, signo))
 256                                break;
 257                err = signo;
 258        }
 259
 260        lock_kernel();
 261
 262        /* Release lockd */
 263        lockd_down();
 264
 265        /* Check if this is last thread */
 266        if (serv->sv_nrthreads==1) {
 267                
 268                printk(KERN_WARNING "nfsd: last server has exited\n");
 269                if (err != SIG_NOCLEAN) {
 270                        printk(KERN_WARNING "nfsd: unexporting all filesystems\n");
 271                        nfsd_export_flush();
 272                }
 273                nfsd_serv = NULL;
 274                nfsd_racache_shutdown();        /* release read-ahead cache */
 275                nfs4_state_shutdown();
 276        }
 277        list_del(&me.list);
 278        nfsdstats.th_cnt --;
 279
 280out:
 281        /* Release the thread */
 282        svc_exit_thread(rqstp);
 283
 284        /* Release module */
 285        module_put_and_exit(0);
 286}
 287
 288int
 289nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp)
 290{
 291        struct svc_procedure    *proc;
 292        kxdrproc_t              xdr;
 293        u32                     nfserr;
 294        u32                     *nfserrp;
 295
 296        dprintk("nfsd_dispatch: vers %d proc %d\n",
 297                                rqstp->rq_vers, rqstp->rq_proc);
 298        proc = rqstp->rq_procinfo;
 299
 300        /* Check whether we have this call in the cache. */
 301        switch (nfsd_cache_lookup(rqstp, proc->pc_cachetype)) {
 302        case RC_INTR:
 303        case RC_DROPIT:
 304                return 0;
 305        case RC_REPLY:
 306                return 1;
 307        case RC_DOIT:;
 308                /* do it */
 309        }
 310
 311        /* Decode arguments */
 312        xdr = proc->pc_decode;
 313        if (xdr && !xdr(rqstp, (u32*)rqstp->rq_arg.head[0].iov_base,
 314                        rqstp->rq_argp)) {
 315                dprintk("nfsd: failed to decode arguments!\n");
 316                nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
 317                *statp = rpc_garbage_args;
 318                return 1;
 319        }
 320
 321        /* need to grab the location to store the status, as
 322         * nfsv4 does some encoding while processing 
 323         */
 324        nfserrp = rqstp->rq_res.head[0].iov_base
 325                + rqstp->rq_res.head[0].iov_len;
 326        rqstp->rq_res.head[0].iov_len += sizeof(u32);
 327
 328        /* Now call the procedure handler, and encode NFS status. */
 329        nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
 330        if (nfserr == nfserr_jukebox && rqstp->rq_vers == 2)
 331                nfserr = nfserr_dropit;
 332        if (nfserr == nfserr_dropit) {
 333                dprintk("nfsd: Dropping request due to malloc failure!\n");
 334                nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
 335                return 0;
 336        }
 337
 338        if (rqstp->rq_proc != 0)
 339                *nfserrp++ = nfserr;
 340
 341        /* Encode result.
 342         * For NFSv2, additional info is never returned in case of an error.
 343         */
 344        if (!(nfserr && rqstp->rq_vers == 2)) {
 345                xdr = proc->pc_encode;
 346                if (xdr && !xdr(rqstp, nfserrp,
 347                                rqstp->rq_resp)) {
 348                        /* Failed to encode result. Release cache entry */
 349                        dprintk("nfsd: failed to encode result!\n");
 350                        nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
 351                        *statp = rpc_system_err;
 352                        return 1;
 353                }
 354        }
 355
 356        /* Store reply in cache. */
 357        nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1);
 358        return 1;
 359}
 360
 361extern struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
 362
 363static struct svc_version *     nfsd_version[] = {
 364        [2] = &nfsd_version2,
 365#if defined(CONFIG_NFSD_V3)
 366        [3] = &nfsd_version3,
 367#endif
 368#if defined(CONFIG_NFSD_V4)
 369        [4] = &nfsd_version4,
 370#endif
 371};
 372
 373#define NFSD_NRVERS             (sizeof(nfsd_version)/sizeof(nfsd_version[0]))
 374struct svc_program              nfsd_program = {
 375        .pg_prog                = NFS_PROGRAM,          /* program number */
 376        .pg_nvers               = NFSD_NRVERS,          /* nr of entries in nfsd_version */
 377        .pg_vers                = nfsd_version,         /* version table */
 378        .pg_name                = "nfsd",               /* program name */
 379        .pg_class               = "nfsd",               /* authentication class */
 380        .pg_stats               = &nfsd_svcstats,       /* version table */
 381};
 382
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.