linux/fs/nfs/callback.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfs/callback.c
   3 *
   4 * Copyright (C) 2004 Trond Myklebust
   5 *
   6 * NFSv4 callback handling
   7 */
   8
   9#include <linux/completion.h>
  10#include <linux/ip.h>
  11#include <linux/module.h>
  12#include <linux/sunrpc/svc.h>
  13#include <linux/sunrpc/svcsock.h>
  14#include <linux/nfs_fs.h>
  15#include <linux/errno.h>
  16#include <linux/mutex.h>
  17#include <linux/freezer.h>
  18#include <linux/kthread.h>
  19#include <linux/sunrpc/svcauth_gss.h>
  20#include <linux/sunrpc/bc_xprt.h>
  21
  22#include <net/inet_sock.h>
  23
  24#include "nfs4_fs.h"
  25#include "callback.h"
  26#include "internal.h"
  27#include "netns.h"
  28
  29#define NFSDBG_FACILITY NFSDBG_CALLBACK
  30
  31struct nfs_callback_data {
  32        unsigned int users;
  33        struct svc_serv *serv;
  34        struct svc_rqst *rqst;
  35        struct task_struct *task;
  36};
  37
  38static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
  39static DEFINE_MUTEX(nfs_callback_mutex);
  40static struct svc_program nfs4_callback_program;
  41
  42static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
  43{
  44        int ret;
  45        struct nfs_net *nn = net_generic(net, nfs_net_id);
  46
  47        ret = svc_create_xprt(serv, "tcp", net, PF_INET,
  48                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
  49        if (ret <= 0)
  50                goto out_err;
  51        nn->nfs_callback_tcpport = ret;
  52        dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
  53                        nn->nfs_callback_tcpport, PF_INET, net);
  54
  55        ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
  56                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
  57        if (ret > 0) {
  58                nn->nfs_callback_tcpport6 = ret;
  59                dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
  60                                nn->nfs_callback_tcpport6, PF_INET6, net);
  61        } else if (ret != -EAFNOSUPPORT)
  62                goto out_err;
  63        return 0;
  64
  65out_err:
  66        return (ret) ? ret : -ENOMEM;
  67}
  68
  69/*
  70 * This is the NFSv4 callback kernel thread.
  71 */
  72static int
  73nfs4_callback_svc(void *vrqstp)
  74{
  75        int err;
  76        struct svc_rqst *rqstp = vrqstp;
  77
  78        set_freezable();
  79
  80        while (!kthread_should_stop()) {
  81                /*
  82                 * Listen for a request on the socket
  83                 */
  84                err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
  85                if (err == -EAGAIN || err == -EINTR)
  86                        continue;
  87                svc_process(rqstp);
  88        }
  89        return 0;
  90}
  91
  92/*
  93 * Prepare to bring up the NFSv4 callback service
  94 */
  95static struct svc_rqst *
  96nfs4_callback_up(struct svc_serv *serv)
  97{
  98        return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
  99}
 100
 101#if defined(CONFIG_NFS_V4_1)
 102static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
 103{
 104        /*
 105         * Create an svc_sock for the back channel service that shares the
 106         * fore channel connection.
 107         * Returns the input port (0) and sets the svc_serv bc_xprt on success
 108         */
 109        return svc_create_xprt(serv, "tcp-bc", net, PF_INET, 0,
 110                              SVC_SOCK_ANONYMOUS);
 111}
 112
 113/*
 114 * The callback service for NFSv4.1 callbacks
 115 */
 116static int
 117nfs41_callback_svc(void *vrqstp)
 118{
 119        struct svc_rqst *rqstp = vrqstp;
 120        struct svc_serv *serv = rqstp->rq_server;
 121        struct rpc_rqst *req;
 122        int error;
 123        DEFINE_WAIT(wq);
 124
 125        set_freezable();
 126
 127        while (!kthread_should_stop()) {
 128                if (try_to_freeze())
 129                        continue;
 130
 131                prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
 132                spin_lock_bh(&serv->sv_cb_lock);
 133                if (!list_empty(&serv->sv_cb_list)) {
 134                        req = list_first_entry(&serv->sv_cb_list,
 135                                        struct rpc_rqst, rq_bc_list);
 136                        list_del(&req->rq_bc_list);
 137                        spin_unlock_bh(&serv->sv_cb_lock);
 138                        dprintk("Invoking bc_svc_process()\n");
 139                        error = bc_svc_process(serv, req, rqstp);
 140                        dprintk("bc_svc_process() returned w/ error code= %d\n",
 141                                error);
 142                } else {
 143                        spin_unlock_bh(&serv->sv_cb_lock);
 144                        schedule();
 145                }
 146                finish_wait(&serv->sv_cb_waitq, &wq);
 147        }
 148        return 0;
 149}
 150
 151/*
 152 * Bring up the NFSv4.1 callback service
 153 */
 154static struct svc_rqst *
 155nfs41_callback_up(struct svc_serv *serv)
 156{
 157        struct svc_rqst *rqstp;
 158
 159        INIT_LIST_HEAD(&serv->sv_cb_list);
 160        spin_lock_init(&serv->sv_cb_lock);
 161        init_waitqueue_head(&serv->sv_cb_waitq);
 162        rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
 163        if (IS_ERR(rqstp)) {
 164                svc_xprt_put(serv->sv_bc_xprt);
 165                serv->sv_bc_xprt = NULL;
 166        }
 167        dprintk("--> %s return %d\n", __func__, PTR_ERR_OR_ZERO(rqstp));
 168        return rqstp;
 169}
 170
 171static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
 172                struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 173{
 174        *rqstpp = nfs41_callback_up(serv);
 175        *callback_svc = nfs41_callback_svc;
 176}
 177
 178static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 179                struct svc_serv *serv)
 180{
 181        if (minorversion)
 182                /*
 183                 * Save the svc_serv in the transport so that it can
 184                 * be referenced when the session backchannel is initialized
 185                 */
 186                xprt->bc_serv = serv;
 187}
 188#else
 189static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
 190{
 191        return 0;
 192}
 193
 194static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
 195                struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 196{
 197        *rqstpp = ERR_PTR(-ENOTSUPP);
 198        *callback_svc = ERR_PTR(-ENOTSUPP);
 199}
 200
 201static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 202                struct svc_serv *serv)
 203{
 204}
 205#endif /* CONFIG_NFS_V4_1 */
 206
 207static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
 208                                  struct svc_serv *serv)
 209{
 210        struct svc_rqst *rqstp;
 211        int (*callback_svc)(void *vrqstp);
 212        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 213        int ret;
 214
 215        nfs_callback_bc_serv(minorversion, xprt, serv);
 216
 217        if (cb_info->task)
 218                return 0;
 219
 220        switch (minorversion) {
 221        case 0:
 222                /* v4.0 callback setup */
 223                rqstp = nfs4_callback_up(serv);
 224                callback_svc = nfs4_callback_svc;
 225                break;
 226        default:
 227                nfs_minorversion_callback_svc_setup(serv,
 228                                &rqstp, &callback_svc);
 229        }
 230
 231        if (IS_ERR(rqstp))
 232                return PTR_ERR(rqstp);
 233
 234        svc_sock_update_bufs(serv);
 235
 236        cb_info->serv = serv;
 237        cb_info->rqst = rqstp;
 238        cb_info->task = kthread_run(callback_svc, cb_info->rqst,
 239                                    "nfsv4.%u-svc", minorversion);
 240        if (IS_ERR(cb_info->task)) {
 241                ret = PTR_ERR(cb_info->task);
 242                svc_exit_thread(cb_info->rqst);
 243                cb_info->rqst = NULL;
 244                cb_info->task = NULL;
 245                return ret;
 246        }
 247        dprintk("nfs_callback_up: service started\n");
 248        return 0;
 249}
 250
 251static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
 252{
 253        struct nfs_net *nn = net_generic(net, nfs_net_id);
 254
 255        if (--nn->cb_users[minorversion])
 256                return;
 257
 258        dprintk("NFS: destroy per-net callback data; net=%p\n", net);
 259        svc_shutdown_net(serv, net);
 260}
 261
 262static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct net *net)
 263{
 264        struct nfs_net *nn = net_generic(net, nfs_net_id);
 265        int ret;
 266
 267        if (nn->cb_users[minorversion]++)
 268                return 0;
 269
 270        dprintk("NFS: create per-net callback data; net=%p\n", net);
 271
 272        ret = svc_bind(serv, net);
 273        if (ret < 0) {
 274                printk(KERN_WARNING "NFS: bind callback service failed\n");
 275                goto err_bind;
 276        }
 277
 278        switch (minorversion) {
 279                case 0:
 280                        ret = nfs4_callback_up_net(serv, net);
 281                        break;
 282                case 1:
 283                case 2:
 284                        ret = nfs41_callback_up_net(serv, net);
 285                        break;
 286                default:
 287                        printk(KERN_ERR "NFS: unknown callback version: %d\n",
 288                                        minorversion);
 289                        ret = -EINVAL;
 290                        break;
 291        }
 292
 293        if (ret < 0) {
 294                printk(KERN_ERR "NFS: callback service start failed\n");
 295                goto err_socks;
 296        }
 297        return 0;
 298
 299err_socks:
 300        svc_rpcb_cleanup(serv, net);
 301err_bind:
 302        dprintk("NFS: Couldn't create callback socket: err = %d; "
 303                        "net = %p\n", ret, net);
 304        return ret;
 305}
 306
 307static struct svc_serv *nfs_callback_create_svc(int minorversion)
 308{
 309        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 310        struct svc_serv *serv;
 311
 312        /*
 313         * Check whether we're already up and running.
 314         */
 315        if (cb_info->task) {
 316                /*
 317                 * Note: increase service usage, because later in case of error
 318                 * svc_destroy() will be called.
 319                 */
 320                svc_get(cb_info->serv);
 321                return cb_info->serv;
 322        }
 323
 324        /*
 325         * Sanity check: if there's no task,
 326         * we should be the first user ...
 327         */
 328        if (cb_info->users)
 329                printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
 330                        cb_info->users);
 331
 332        serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
 333        if (!serv) {
 334                printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
 335                return ERR_PTR(-ENOMEM);
 336        }
 337        /* As there is only one thread we need to over-ride the
 338         * default maximum of 80 connections
 339         */
 340        serv->sv_maxconn = 1024;
 341        dprintk("nfs_callback_create_svc: service created\n");
 342        return serv;
 343}
 344
 345/*
 346 * Bring up the callback thread if it is not already up.
 347 */
 348int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 349{
 350        struct svc_serv *serv;
 351        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 352        int ret;
 353        struct net *net = xprt->xprt_net;
 354
 355        mutex_lock(&nfs_callback_mutex);
 356
 357        serv = nfs_callback_create_svc(minorversion);
 358        if (IS_ERR(serv)) {
 359                ret = PTR_ERR(serv);
 360                goto err_create;
 361        }
 362
 363        ret = nfs_callback_up_net(minorversion, serv, net);
 364        if (ret < 0)
 365                goto err_net;
 366
 367        ret = nfs_callback_start_svc(minorversion, xprt, serv);
 368        if (ret < 0)
 369                goto err_start;
 370
 371        cb_info->users++;
 372        /*
 373         * svc_create creates the svc_serv with sv_nrthreads == 1, and then
 374         * svc_prepare_thread increments that. So we need to call svc_destroy
 375         * on both success and failure so that the refcount is 1 when the
 376         * thread exits.
 377         */
 378err_net:
 379        svc_destroy(serv);
 380err_create:
 381        mutex_unlock(&nfs_callback_mutex);
 382        return ret;
 383
 384err_start:
 385        nfs_callback_down_net(minorversion, serv, net);
 386        dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
 387        goto err_net;
 388}
 389
 390/*
 391 * Kill the callback thread if it's no longer being used.
 392 */
 393void nfs_callback_down(int minorversion, struct net *net)
 394{
 395        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 396
 397        mutex_lock(&nfs_callback_mutex);
 398        nfs_callback_down_net(minorversion, cb_info->serv, net);
 399        cb_info->users--;
 400        if (cb_info->users == 0 && cb_info->task != NULL) {
 401                kthread_stop(cb_info->task);
 402                dprintk("nfs_callback_down: service stopped\n");
 403                svc_exit_thread(cb_info->rqst);
 404                dprintk("nfs_callback_down: service destroyed\n");
 405                cb_info->serv = NULL;
 406                cb_info->rqst = NULL;
 407                cb_info->task = NULL;
 408        }
 409        mutex_unlock(&nfs_callback_mutex);
 410}
 411
 412/* Boolean check of RPC_AUTH_GSS principal */
 413int
 414check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
 415{
 416        char *p = rqstp->rq_cred.cr_principal;
 417
 418        if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
 419                return 1;
 420
 421        /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
 422        if (clp->cl_minorversion != 0)
 423                return 0;
 424        /*
 425         * It might just be a normal user principal, in which case
 426         * userspace won't bother to tell us the name at all.
 427         */
 428        if (p == NULL)
 429                return 0;
 430
 431        /*
 432         * Did we get the acceptor from userland during the SETCLIENID
 433         * negotiation?
 434         */
 435        if (clp->cl_acceptor)
 436                return !strcmp(p, clp->cl_acceptor);
 437
 438        /*
 439         * Otherwise try to verify it using the cl_hostname. Note that this
 440         * doesn't work if a non-canonical hostname was used in the devname.
 441         */
 442
 443        /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
 444
 445        if (memcmp(p, "nfs@", 4) != 0)
 446                return 0;
 447        p += 4;
 448        if (strcmp(p, clp->cl_hostname) != 0)
 449                return 0;
 450        return 1;
 451}
 452
 453/*
 454 * pg_authenticate method for nfsv4 callback threads.
 455 *
 456 * The authflavor has been negotiated, so an incorrect flavor is a server
 457 * bug. Drop packets with incorrect authflavor.
 458 *
 459 * All other checking done after NFS decoding where the nfs_client can be
 460 * found in nfs4_callback_compound
 461 */
 462static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 463{
 464        switch (rqstp->rq_authop->flavour) {
 465        case RPC_AUTH_NULL:
 466                if (rqstp->rq_proc != CB_NULL)
 467                        return SVC_DROP;
 468                break;
 469        case RPC_AUTH_GSS:
 470                /* No RPC_AUTH_GSS support yet in NFSv4.1 */
 471                 if (svc_is_backchannel(rqstp))
 472                        return SVC_DROP;
 473        }
 474        return SVC_OK;
 475}
 476
 477/*
 478 * Define NFS4 callback program
 479 */
 480static struct svc_version *nfs4_callback_version[] = {
 481        [1] = &nfs4_callback_version1,
 482        [4] = &nfs4_callback_version4,
 483};
 484
 485static struct svc_stat nfs4_callback_stats;
 486
 487static struct svc_program nfs4_callback_program = {
 488        .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
 489        .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
 490        .pg_vers = nfs4_callback_version,               /* version table */
 491        .pg_name = "NFSv4 callback",                    /* service name */
 492        .pg_class = "nfs",                              /* authentication class */
 493        .pg_stats = &nfs4_callback_stats,
 494        .pg_authenticate = nfs_callback_authenticate,
 495};
 496