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/smp_lock.h>
  13#include <linux/sunrpc/svc.h>
  14#include <linux/sunrpc/svcsock.h>
  15#include <linux/nfs_fs.h>
  16#include <linux/mutex.h>
  17#include <linux/freezer.h>
  18#include <linux/kthread.h>
  19#include <linux/sunrpc/svcauth_gss.h>
  20#if defined(CONFIG_NFS_V4_1)
  21#include <linux/sunrpc/bc_xprt.h>
  22#endif
  23
  24#include <net/inet_sock.h>
  25
  26#include "nfs4_fs.h"
  27#include "callback.h"
  28#include "internal.h"
  29
  30#define NFSDBG_FACILITY NFSDBG_CALLBACK
  31
  32struct nfs_callback_data {
  33        unsigned int users;
  34        struct svc_serv *serv;
  35        struct svc_rqst *rqst;
  36        struct task_struct *task;
  37};
  38
  39static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
  40static DEFINE_MUTEX(nfs_callback_mutex);
  41static struct svc_program nfs4_callback_program;
  42
  43unsigned int nfs_callback_set_tcpport;
  44unsigned short nfs_callback_tcpport;
  45unsigned short nfs_callback_tcpport6;
  46#define NFS_CALLBACK_MAXPORTNR (65535U)
  47
  48static int param_set_portnr(const char *val, struct kernel_param *kp)
  49{
  50        unsigned long num;
  51        int ret;
  52
  53        if (!val)
  54                return -EINVAL;
  55        ret = strict_strtoul(val, 0, &num);
  56        if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
  57                return -EINVAL;
  58        *((unsigned int *)kp->arg) = num;
  59        return 0;
  60}
  61
  62static int param_get_portnr(char *buffer, struct kernel_param *kp)
  63{
  64        return param_get_uint(buffer, kp);
  65}
  66#define param_check_portnr(name, p) __param_check(name, p, unsigned int);
  67
  68module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
  69
  70/*
  71 * This is the NFSv4 callback kernel thread.
  72 */
  73static int
  74nfs4_callback_svc(void *vrqstp)
  75{
  76        int err, preverr = 0;
  77        struct svc_rqst *rqstp = vrqstp;
  78
  79        set_freezable();
  80
  81        while (!kthread_should_stop()) {
  82                /*
  83                 * Listen for a request on the socket
  84                 */
  85                err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
  86                if (err == -EAGAIN || err == -EINTR) {
  87                        preverr = err;
  88                        continue;
  89                }
  90                if (err < 0) {
  91                        if (err != preverr) {
  92                                printk(KERN_WARNING "%s: unexpected error "
  93                                        "from svc_recv (%d)\n", __func__, err);
  94                                preverr = err;
  95                        }
  96                        schedule_timeout_uninterruptible(HZ);
  97                        continue;
  98                }
  99                preverr = err;
 100                svc_process(rqstp);
 101        }
 102        return 0;
 103}
 104
 105/*
 106 * Prepare to bring up the NFSv4 callback service
 107 */
 108struct svc_rqst *
 109nfs4_callback_up(struct svc_serv *serv)
 110{
 111        int ret;
 112
 113        ret = svc_create_xprt(serv, "tcp", PF_INET,
 114                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
 115        if (ret <= 0)
 116                goto out_err;
 117        nfs_callback_tcpport = ret;
 118        dprintk("NFS: Callback listener port = %u (af %u)\n",
 119                        nfs_callback_tcpport, PF_INET);
 120
 121        ret = svc_create_xprt(serv, "tcp", PF_INET6,
 122                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
 123        if (ret > 0) {
 124                nfs_callback_tcpport6 = ret;
 125                dprintk("NFS: Callback listener port = %u (af %u)\n",
 126                                nfs_callback_tcpport6, PF_INET6);
 127        } else if (ret == -EAFNOSUPPORT)
 128                ret = 0;
 129        else
 130                goto out_err;
 131
 132        return svc_prepare_thread(serv, &serv->sv_pools[0]);
 133
 134out_err:
 135        if (ret == 0)
 136                ret = -ENOMEM;
 137        return ERR_PTR(ret);
 138}
 139
 140#if defined(CONFIG_NFS_V4_1)
 141/*
 142 * The callback service for NFSv4.1 callbacks
 143 */
 144static int
 145nfs41_callback_svc(void *vrqstp)
 146{
 147        struct svc_rqst *rqstp = vrqstp;
 148        struct svc_serv *serv = rqstp->rq_server;
 149        struct rpc_rqst *req;
 150        int error;
 151        DEFINE_WAIT(wq);
 152
 153        set_freezable();
 154
 155        while (!kthread_should_stop()) {
 156                prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
 157                spin_lock_bh(&serv->sv_cb_lock);
 158                if (!list_empty(&serv->sv_cb_list)) {
 159                        req = list_first_entry(&serv->sv_cb_list,
 160                                        struct rpc_rqst, rq_bc_list);
 161                        list_del(&req->rq_bc_list);
 162                        spin_unlock_bh(&serv->sv_cb_lock);
 163                        dprintk("Invoking bc_svc_process()\n");
 164                        error = bc_svc_process(serv, req, rqstp);
 165                        dprintk("bc_svc_process() returned w/ error code= %d\n",
 166                                error);
 167                } else {
 168                        spin_unlock_bh(&serv->sv_cb_lock);
 169                        schedule();
 170                }
 171                finish_wait(&serv->sv_cb_waitq, &wq);
 172        }
 173        return 0;
 174}
 175
 176/*
 177 * Bring up the NFSv4.1 callback service
 178 */
 179struct svc_rqst *
 180nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
 181{
 182        struct svc_xprt *bc_xprt;
 183        struct svc_rqst *rqstp = ERR_PTR(-ENOMEM);
 184
 185        dprintk("--> %s\n", __func__);
 186        /* Create a svc_sock for the service */
 187        bc_xprt = svc_sock_create(serv, xprt->prot);
 188        if (!bc_xprt)
 189                goto out;
 190
 191        /*
 192         * Save the svc_serv in the transport so that it can
 193         * be referenced when the session backchannel is initialized
 194         */
 195        serv->bc_xprt = bc_xprt;
 196        xprt->bc_serv = serv;
 197
 198        INIT_LIST_HEAD(&serv->sv_cb_list);
 199        spin_lock_init(&serv->sv_cb_lock);
 200        init_waitqueue_head(&serv->sv_cb_waitq);
 201        rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
 202        if (IS_ERR(rqstp))
 203                svc_sock_destroy(bc_xprt);
 204out:
 205        dprintk("--> %s return %p\n", __func__, rqstp);
 206        return rqstp;
 207}
 208
 209static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
 210                struct svc_serv *serv, struct rpc_xprt *xprt,
 211                struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 212{
 213        if (minorversion) {
 214                *rqstpp = nfs41_callback_up(serv, xprt);
 215                *callback_svc = nfs41_callback_svc;
 216        }
 217        return minorversion;
 218}
 219
 220static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 221                struct nfs_callback_data *cb_info)
 222{
 223        if (minorversion)
 224                xprt->bc_serv = cb_info->serv;
 225}
 226#else
 227static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
 228                struct svc_serv *serv, struct rpc_xprt *xprt,
 229                struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 230{
 231        return 0;
 232}
 233
 234static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 235                struct nfs_callback_data *cb_info)
 236{
 237}
 238#endif /* CONFIG_NFS_V4_1 */
 239
 240/*
 241 * Bring up the callback thread if it is not already up.
 242 */
 243int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 244{
 245        struct svc_serv *serv = NULL;
 246        struct svc_rqst *rqstp;
 247        int (*callback_svc)(void *vrqstp);
 248        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 249        char svc_name[12];
 250        int ret = 0;
 251        int minorversion_setup;
 252
 253        mutex_lock(&nfs_callback_mutex);
 254        if (cb_info->users++ || cb_info->task != NULL) {
 255                nfs_callback_bc_serv(minorversion, xprt, cb_info);
 256                goto out;
 257        }
 258        serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
 259        if (!serv) {
 260                ret = -ENOMEM;
 261                goto out_err;
 262        }
 263
 264        minorversion_setup =  nfs_minorversion_callback_svc_setup(minorversion,
 265                                        serv, xprt, &rqstp, &callback_svc);
 266        if (!minorversion_setup) {
 267                /* v4.0 callback setup */
 268                rqstp = nfs4_callback_up(serv);
 269                callback_svc = nfs4_callback_svc;
 270        }
 271
 272        if (IS_ERR(rqstp)) {
 273                ret = PTR_ERR(rqstp);
 274                goto out_err;
 275        }
 276
 277        svc_sock_update_bufs(serv);
 278
 279        sprintf(svc_name, "nfsv4.%u-svc", minorversion);
 280        cb_info->serv = serv;
 281        cb_info->rqst = rqstp;
 282        cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
 283        if (IS_ERR(cb_info->task)) {
 284                ret = PTR_ERR(cb_info->task);
 285                svc_exit_thread(cb_info->rqst);
 286                cb_info->rqst = NULL;
 287                cb_info->task = NULL;
 288                goto out_err;
 289        }
 290out:
 291        /*
 292         * svc_create creates the svc_serv with sv_nrthreads == 1, and then
 293         * svc_prepare_thread increments that. So we need to call svc_destroy
 294         * on both success and failure so that the refcount is 1 when the
 295         * thread exits.
 296         */
 297        if (serv)
 298                svc_destroy(serv);
 299        mutex_unlock(&nfs_callback_mutex);
 300        return ret;
 301out_err:
 302        dprintk("NFS: Couldn't create callback socket or server thread; "
 303                "err = %d\n", ret);
 304        cb_info->users--;
 305        goto out;
 306}
 307
 308/*
 309 * Kill the callback thread if it's no longer being used.
 310 */
 311void nfs_callback_down(int minorversion)
 312{
 313        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 314
 315        mutex_lock(&nfs_callback_mutex);
 316        cb_info->users--;
 317        if (cb_info->users == 0 && cb_info->task != NULL) {
 318                kthread_stop(cb_info->task);
 319                svc_exit_thread(cb_info->rqst);
 320                cb_info->serv = NULL;
 321                cb_info->rqst = NULL;
 322                cb_info->task = NULL;
 323        }
 324        mutex_unlock(&nfs_callback_mutex);
 325}
 326
 327static int check_gss_callback_principal(struct nfs_client *clp,
 328                                        struct svc_rqst *rqstp)
 329{
 330        struct rpc_clnt *r = clp->cl_rpcclient;
 331        char *p = svc_gss_principal(rqstp);
 332
 333        /*
 334         * It might just be a normal user principal, in which case
 335         * userspace won't bother to tell us the name at all.
 336         */
 337        if (p == NULL)
 338                return SVC_DENIED;
 339
 340        /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
 341
 342        if (memcmp(p, "nfs@", 4) != 0)
 343                return SVC_DENIED;
 344        p += 4;
 345        if (strcmp(p, r->cl_server) != 0)
 346                return SVC_DENIED;
 347        return SVC_OK;
 348}
 349
 350static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 351{
 352        struct nfs_client *clp;
 353        RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
 354        int ret = SVC_OK;
 355
 356        /* Don't talk to strangers */
 357        clp = nfs_find_client(svc_addr(rqstp), 4);
 358        if (clp == NULL)
 359                return SVC_DROP;
 360
 361        dprintk("%s: %s NFSv4 callback!\n", __func__,
 362                        svc_print_addr(rqstp, buf, sizeof(buf)));
 363
 364        switch (rqstp->rq_authop->flavour) {
 365                case RPC_AUTH_NULL:
 366                        if (rqstp->rq_proc != CB_NULL)
 367                                ret = SVC_DENIED;
 368                        break;
 369                case RPC_AUTH_UNIX:
 370                        break;
 371                case RPC_AUTH_GSS:
 372                        ret = check_gss_callback_principal(clp, rqstp);
 373                        break;
 374                default:
 375                        ret = SVC_DENIED;
 376        }
 377        nfs_put_client(clp);
 378        return ret;
 379}
 380
 381/*
 382 * Define NFS4 callback program
 383 */
 384static struct svc_version *nfs4_callback_version[] = {
 385        [1] = &nfs4_callback_version1,
 386        [4] = &nfs4_callback_version4,
 387};
 388
 389static struct svc_stat nfs4_callback_stats;
 390
 391static struct svc_program nfs4_callback_program = {
 392        .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
 393        .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
 394        .pg_vers = nfs4_callback_version,               /* version table */
 395        .pg_name = "NFSv4 callback",                    /* service name */
 396        .pg_class = "nfs",                              /* authentication class */
 397        .pg_stats = &nfs4_callback_stats,
 398        .pg_authenticate = nfs_callback_authenticate,
 399};
 400
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.