linux/fs/nfs/nfs4client.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
   3 * Written by David Howells (dhowells@redhat.com)
   4 */
   5#include <linux/module.h>
   6#include <linux/nfs_fs.h>
   7#include <linux/nfs_idmap.h>
   8#include <linux/nfs_mount.h>
   9#include <linux/sunrpc/auth.h>
  10#include <linux/sunrpc/xprt.h>
  11#include <linux/sunrpc/bc_xprt.h>
  12#include "internal.h"
  13#include "callback.h"
  14#include "delegation.h"
  15#include "pnfs.h"
  16#include "netns.h"
  17
  18#define NFSDBG_FACILITY         NFSDBG_CLIENT
  19
  20/*
  21 * Get a unique NFSv4.0 callback identifier which will be used
  22 * by the V4.0 callback service to lookup the nfs_client struct
  23 */
  24static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
  25{
  26        int ret = 0;
  27        struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
  28
  29        if (clp->rpc_ops->version != 4 || minorversion != 0)
  30                return ret;
  31retry:
  32        if (!idr_pre_get(&nn->cb_ident_idr, GFP_KERNEL))
  33                return -ENOMEM;
  34        spin_lock(&nn->nfs_client_lock);
  35        ret = idr_get_new(&nn->cb_ident_idr, clp, &clp->cl_cb_ident);
  36        spin_unlock(&nn->nfs_client_lock);
  37        if (ret == -EAGAIN)
  38                goto retry;
  39        return ret;
  40}
  41
  42#ifdef CONFIG_NFS_V4_1
  43static void nfs4_shutdown_session(struct nfs_client *clp)
  44{
  45        if (nfs4_has_session(clp)) {
  46                nfs4_destroy_session(clp->cl_session);
  47                nfs4_destroy_clientid(clp);
  48        }
  49
  50}
  51#else /* CONFIG_NFS_V4_1 */
  52static void nfs4_shutdown_session(struct nfs_client *clp)
  53{
  54}
  55#endif /* CONFIG_NFS_V4_1 */
  56
  57struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
  58{
  59        int err;
  60        struct nfs_client *clp = nfs_alloc_client(cl_init);
  61        if (IS_ERR(clp))
  62                return clp;
  63
  64        err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
  65        if (err)
  66                goto error;
  67
  68        spin_lock_init(&clp->cl_lock);
  69        INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
  70        rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
  71        clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
  72        clp->cl_minorversion = cl_init->minorversion;
  73        clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
  74        return clp;
  75
  76error:
  77        nfs_free_client(clp);
  78        return ERR_PTR(err);
  79}
  80
  81/*
  82 * Destroy the NFS4 callback service
  83 */
  84static void nfs4_destroy_callback(struct nfs_client *clp)
  85{
  86        if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
  87                nfs_callback_down(clp->cl_mvops->minor_version);
  88}
  89
  90static void nfs4_shutdown_client(struct nfs_client *clp)
  91{
  92        if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
  93                nfs4_kill_renewd(clp);
  94        nfs4_shutdown_session(clp);
  95        nfs4_destroy_callback(clp);
  96        if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
  97                nfs_idmap_delete(clp);
  98
  99        rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
 100        kfree(clp->cl_serverowner);
 101        kfree(clp->cl_serverscope);
 102        kfree(clp->cl_implid);
 103}
 104
 105void nfs4_free_client(struct nfs_client *clp)
 106{
 107        nfs4_shutdown_client(clp);
 108        nfs_free_client(clp);
 109}
 110
 111/*
 112 * Initialize the NFS4 callback service
 113 */
 114static int nfs4_init_callback(struct nfs_client *clp)
 115{
 116        int error;
 117
 118        if (clp->rpc_ops->version == 4) {
 119                struct rpc_xprt *xprt;
 120
 121                xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
 122
 123                if (nfs4_has_session(clp)) {
 124                        error = xprt_setup_backchannel(xprt,
 125                                                NFS41_BC_MIN_CALLBACKS);
 126                        if (error < 0)
 127                                return error;
 128                }
 129
 130                error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
 131                if (error < 0) {
 132                        dprintk("%s: failed to start callback. Error = %d\n",
 133                                __func__, error);
 134                        return error;
 135                }
 136                __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
 137        }
 138        return 0;
 139}
 140
 141/*
 142 * Initialize the minor version specific parts of an NFS4 client record
 143 */
 144static int nfs4_init_client_minor_version(struct nfs_client *clp)
 145{
 146#if defined(CONFIG_NFS_V4_1)
 147        if (clp->cl_mvops->minor_version) {
 148                struct nfs4_session *session = NULL;
 149                /*
 150                 * Create the session and mark it expired.
 151                 * When a SEQUENCE operation encounters the expired session
 152                 * it will do session recovery to initialize it.
 153                 */
 154                session = nfs4_alloc_session(clp);
 155                if (!session)
 156                        return -ENOMEM;
 157
 158                clp->cl_session = session;
 159                /*
 160                 * The create session reply races with the server back
 161                 * channel probe. Mark the client NFS_CS_SESSION_INITING
 162                 * so that the client back channel can find the
 163                 * nfs_client struct
 164                 */
 165                nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
 166        }
 167#endif /* CONFIG_NFS_V4_1 */
 168
 169        return nfs4_init_callback(clp);
 170}
 171
 172/**
 173 * nfs4_init_client - Initialise an NFS4 client record
 174 *
 175 * @clp: nfs_client to initialise
 176 * @timeparms: timeout parameters for underlying RPC transport
 177 * @ip_addr: callback IP address in presentation format
 178 * @authflavor: authentication flavor for underlying RPC transport
 179 *
 180 * Returns pointer to an NFS client, or an ERR_PTR value.
 181 */
 182struct nfs_client *nfs4_init_client(struct nfs_client *clp,
 183                                    const struct rpc_timeout *timeparms,
 184                                    const char *ip_addr,
 185                                    rpc_authflavor_t authflavour)
 186{
 187        char buf[INET6_ADDRSTRLEN + 1];
 188        int error;
 189
 190        if (clp->cl_cons_state == NFS_CS_READY) {
 191                /* the client is initialised already */
 192                dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
 193                return clp;
 194        }
 195
 196        /* Check NFS protocol revision and initialize RPC op vector */
 197        clp->rpc_ops = &nfs_v4_clientops;
 198
 199        __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
 200        error = nfs_create_rpc_client(clp, timeparms, authflavour);
 201        if (error < 0)
 202                goto error;
 203
 204        /* If no clientaddr= option was specified, find a usable cb address */
 205        if (ip_addr == NULL) {
 206                struct sockaddr_storage cb_addr;
 207                struct sockaddr *sap = (struct sockaddr *)&cb_addr;
 208
 209                error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
 210                if (error < 0)
 211                        goto error;
 212                error = rpc_ntop(sap, buf, sizeof(buf));
 213                if (error < 0)
 214                        goto error;
 215                ip_addr = (const char *)buf;
 216        }
 217        strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
 218
 219        error = nfs_idmap_new(clp);
 220        if (error < 0) {
 221                dprintk("%s: failed to create idmapper. Error = %d\n",
 222                        __func__, error);
 223                goto error;
 224        }
 225        __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
 226
 227        error = nfs4_init_client_minor_version(clp);
 228        if (error < 0)
 229                goto error;
 230
 231        if (!nfs4_has_session(clp))
 232                nfs_mark_client_ready(clp, NFS_CS_READY);
 233        return clp;
 234
 235error:
 236        nfs_mark_client_ready(clp, error);
 237        nfs_put_client(clp);
 238        dprintk("<-- nfs4_init_client() = xerror %d\n", error);
 239        return ERR_PTR(error);
 240}
 241
 242static void nfs4_destroy_server(struct nfs_server *server)
 243{
 244        nfs_server_return_all_delegations(server);
 245        unset_pnfs_layoutdriver(server);
 246        nfs4_purge_state_owners(server);
 247}
 248
 249/*
 250 * NFSv4.0 callback thread helper
 251 *
 252 * Find a client by callback identifier
 253 */
 254struct nfs_client *
 255nfs4_find_client_ident(struct net *net, int cb_ident)
 256{
 257        struct nfs_client *clp;
 258        struct nfs_net *nn = net_generic(net, nfs_net_id);
 259
 260        spin_lock(&nn->nfs_client_lock);
 261        clp = idr_find(&nn->cb_ident_idr, cb_ident);
 262        if (clp)
 263                atomic_inc(&clp->cl_count);
 264        spin_unlock(&nn->nfs_client_lock);
 265        return clp;
 266}
 267
 268#if defined(CONFIG_NFS_V4_1)
 269/* Common match routine for v4.0 and v4.1 callback services */
 270static bool nfs4_cb_match_client(const struct sockaddr *addr,
 271                struct nfs_client *clp, u32 minorversion)
 272{
 273        struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
 274
 275        /* Don't match clients that failed to initialise */
 276        if (!(clp->cl_cons_state == NFS_CS_READY ||
 277            clp->cl_cons_state == NFS_CS_SESSION_INITING))
 278                return false;
 279
 280        smp_rmb();
 281
 282        /* Match the version and minorversion */
 283        if (clp->rpc_ops->version != 4 ||
 284            clp->cl_minorversion != minorversion)
 285                return false;
 286
 287        /* Match only the IP address, not the port number */
 288        if (!nfs_sockaddr_match_ipaddr(addr, clap))
 289                return false;
 290
 291        return true;
 292}
 293
 294/*
 295 * NFSv4.1 callback thread helper
 296 * For CB_COMPOUND calls, find a client by IP address, protocol version,
 297 * minorversion, and sessionID
 298 *
 299 * Returns NULL if no such client
 300 */
 301struct nfs_client *
 302nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
 303                           struct nfs4_sessionid *sid)
 304{
 305        struct nfs_client *clp;
 306        struct nfs_net *nn = net_generic(net, nfs_net_id);
 307
 308        spin_lock(&nn->nfs_client_lock);
 309        list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
 310                if (nfs4_cb_match_client(addr, clp, 1) == false)
 311                        continue;
 312
 313                if (!nfs4_has_session(clp))
 314                        continue;
 315
 316                /* Match sessionid*/
 317                if (memcmp(clp->cl_session->sess_id.data,
 318                    sid->data, NFS4_MAX_SESSIONID_LEN) != 0)
 319                        continue;
 320
 321                atomic_inc(&clp->cl_count);
 322                spin_unlock(&nn->nfs_client_lock);
 323                return clp;
 324        }
 325        spin_unlock(&nn->nfs_client_lock);
 326        return NULL;
 327}
 328
 329#else /* CONFIG_NFS_V4_1 */
 330
 331struct nfs_client *
 332nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
 333                           struct nfs4_sessionid *sid)
 334{
 335        return NULL;
 336}
 337#endif /* CONFIG_NFS_V4_1 */
 338
 339/*
 340 * Set up an NFS4 client
 341 */
 342static int nfs4_set_client(struct nfs_server *server,
 343                const char *hostname,
 344                const struct sockaddr *addr,
 345                const size_t addrlen,
 346                const char *ip_addr,
 347                rpc_authflavor_t authflavour,
 348                int proto, const struct rpc_timeout *timeparms,
 349                u32 minorversion, struct net *net)
 350{
 351        struct nfs_client_initdata cl_init = {
 352                .hostname = hostname,
 353                .addr = addr,
 354                .addrlen = addrlen,
 355                .nfs_mod = &nfs_v4,
 356                .proto = proto,
 357                .minorversion = minorversion,
 358                .net = net,
 359        };
 360        struct nfs_client *clp;
 361        int error;
 362
 363        dprintk("--> nfs4_set_client()\n");
 364
 365        if (server->flags & NFS_MOUNT_NORESVPORT)
 366                set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
 367
 368        /* Allocate or find a client reference we can use */
 369        clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
 370        if (IS_ERR(clp)) {
 371                error = PTR_ERR(clp);
 372                goto error;
 373        }
 374
 375        /*
 376         * Query for the lease time on clientid setup or renewal
 377         *
 378         * Note that this will be set on nfs_clients that were created
 379         * only for the DS role and did not set this bit, but now will
 380         * serve a dual role.
 381         */
 382        set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state);
 383
 384        server->nfs_client = clp;
 385        dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp);
 386        return 0;
 387error:
 388        dprintk("<-- nfs4_set_client() = xerror %d\n", error);
 389        return error;
 390}
 391
 392/*
 393 * Set up a pNFS Data Server client.
 394 *
 395 * Return any existing nfs_client that matches server address,port,version
 396 * and minorversion.
 397 *
 398 * For a new nfs_client, use a soft mount (default), a low retrans and a
 399 * low timeout interval so that if a connection is lost, we retry through
 400 * the MDS.
 401 */
 402struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
 403                const struct sockaddr *ds_addr, int ds_addrlen,
 404                int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
 405{
 406        struct nfs_client_initdata cl_init = {
 407                .addr = ds_addr,
 408                .addrlen = ds_addrlen,
 409                .nfs_mod = &nfs_v4,
 410                .proto = ds_proto,
 411                .minorversion = mds_clp->cl_minorversion,
 412                .net = mds_clp->cl_net,
 413        };
 414        struct rpc_timeout ds_timeout;
 415        struct nfs_client *clp;
 416
 417        /*
 418         * Set an authflavor equual to the MDS value. Use the MDS nfs_client
 419         * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
 420         * (section 13.1 RFC 5661).
 421         */
 422        nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
 423        clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
 424                             mds_clp->cl_rpcclient->cl_auth->au_flavor);
 425
 426        dprintk("<-- %s %p\n", __func__, clp);
 427        return clp;
 428}
 429EXPORT_SYMBOL_GPL(nfs4_set_ds_client);
 430
 431/*
 432 * Session has been established, and the client marked ready.
 433 * Set the mount rsize and wsize with negotiated fore channel
 434 * attributes which will be bound checked in nfs_server_set_fsinfo.
 435 */
 436static void nfs4_session_set_rwsize(struct nfs_server *server)
 437{
 438#ifdef CONFIG_NFS_V4_1
 439        struct nfs4_session *sess;
 440        u32 server_resp_sz;
 441        u32 server_rqst_sz;
 442
 443        if (!nfs4_has_session(server->nfs_client))
 444                return;
 445        sess = server->nfs_client->cl_session;
 446        server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead;
 447        server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead;
 448
 449        if (server->rsize > server_resp_sz)
 450                server->rsize = server_resp_sz;
 451        if (server->wsize > server_rqst_sz)
 452                server->wsize = server_rqst_sz;
 453#endif /* CONFIG_NFS_V4_1 */
 454}
 455
 456static int nfs4_server_common_setup(struct nfs_server *server,
 457                struct nfs_fh *mntfh)
 458{
 459        struct nfs_fattr *fattr;
 460        int error;
 461
 462        BUG_ON(!server->nfs_client);
 463        BUG_ON(!server->nfs_client->rpc_ops);
 464        BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
 465
 466        /* data servers support only a subset of NFSv4.1 */
 467        if (is_ds_only_client(server->nfs_client))
 468                return -EPROTONOSUPPORT;
 469
 470        fattr = nfs_alloc_fattr();
 471        if (fattr == NULL)
 472                return -ENOMEM;
 473
 474        /* We must ensure the session is initialised first */
 475        error = nfs4_init_session(server);
 476        if (error < 0)
 477                goto out;
 478
 479        /* Probe the root fh to retrieve its FSID and filehandle */
 480        error = nfs4_get_rootfh(server, mntfh);
 481        if (error < 0)
 482                goto out;
 483
 484        dprintk("Server FSID: %llx:%llx\n",
 485                        (unsigned long long) server->fsid.major,
 486                        (unsigned long long) server->fsid.minor);
 487        dprintk("Mount FH: %d\n", mntfh->size);
 488
 489        nfs4_session_set_rwsize(server);
 490
 491        error = nfs_probe_fsinfo(server, mntfh, fattr);
 492        if (error < 0)
 493                goto out;
 494
 495        if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
 496                server->namelen = NFS4_MAXNAMLEN;
 497
 498        nfs_server_insert_lists(server);
 499        server->mount_time = jiffies;
 500        server->destroy = nfs4_destroy_server;
 501out:
 502        nfs_free_fattr(fattr);
 503        return error;
 504}
 505
 506/*
 507 * Create a version 4 volume record
 508 */
 509static int nfs4_init_server(struct nfs_server *server,
 510                const struct nfs_parsed_mount_data *data)
 511{
 512        struct rpc_timeout timeparms;
 513        int error;
 514
 515        dprintk("--> nfs4_init_server()\n");
 516
 517        nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
 518                        data->timeo, data->retrans);
 519
 520        /* Initialise the client representation from the mount data */
 521        server->flags = data->flags;
 522        server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
 523        if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
 524                        server->caps |= NFS_CAP_READDIRPLUS;
 525        server->options = data->options;
 526
 527        /* Get a client record */
 528        error = nfs4_set_client(server,
 529                        data->nfs_server.hostname,
 530                        (const struct sockaddr *)&data->nfs_server.address,
 531                        data->nfs_server.addrlen,
 532                        data->client_address,
 533                        data->auth_flavors[0],
 534                        data->nfs_server.protocol,
 535                        &timeparms,
 536                        data->minorversion,
 537                        data->net);
 538        if (error < 0)
 539                goto error;
 540
 541        /*
 542         * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
 543         * authentication.
 544         */
 545        if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX)
 546                server->caps |= NFS_CAP_UIDGID_NOMAP;
 547
 548        if (data->rsize)
 549                server->rsize = nfs_block_size(data->rsize, NULL);
 550        if (data->wsize)
 551                server->wsize = nfs_block_size(data->wsize, NULL);
 552
 553        server->acregmin = data->acregmin * HZ;
 554        server->acregmax = data->acregmax * HZ;
 555        server->acdirmin = data->acdirmin * HZ;
 556        server->acdirmax = data->acdirmax * HZ;
 557
 558        server->port = data->nfs_server.port;
 559
 560        error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
 561
 562error:
 563        /* Done */
 564        dprintk("<-- nfs4_init_server() = %d\n", error);
 565        return error;
 566}
 567
 568/*
 569 * Create a version 4 volume record
 570 * - keyed on server and FSID
 571 */
 572/*struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
 573                                      struct nfs_fh *mntfh)*/
 574struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
 575                                      struct nfs_subversion *nfs_mod)
 576{
 577        struct nfs_server *server;
 578        int error;
 579
 580        dprintk("--> nfs4_create_server()\n");
 581
 582        server = nfs_alloc_server();
 583        if (!server)
 584                return ERR_PTR(-ENOMEM);
 585
 586        /* set up the general RPC client */
 587        error = nfs4_init_server(server, mount_info->parsed);
 588        if (error < 0)
 589                goto error;
 590
 591        error = nfs4_server_common_setup(server, mount_info->mntfh);
 592        if (error < 0)
 593                goto error;
 594
 595        dprintk("<-- nfs4_create_server() = %p\n", server);
 596        return server;
 597
 598error:
 599        nfs_free_server(server);
 600        dprintk("<-- nfs4_create_server() = error %d\n", error);
 601        return ERR_PTR(error);
 602}
 603
 604/*
 605 * Create an NFS4 referral server record
 606 */
 607struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
 608                                               struct nfs_fh *mntfh)
 609{
 610        struct nfs_client *parent_client;
 611        struct nfs_server *server, *parent_server;
 612        int error;
 613
 614        dprintk("--> nfs4_create_referral_server()\n");
 615
 616        server = nfs_alloc_server();
 617        if (!server)
 618                return ERR_PTR(-ENOMEM);
 619
 620        parent_server = NFS_SB(data->sb);
 621        parent_client = parent_server->nfs_client;
 622
 623        /* Initialise the client representation from the parent server */
 624        nfs_server_copy_userdata(server, parent_server);
 625        server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
 626
 627        /* Get a client representation.
 628         * Note: NFSv4 always uses TCP, */
 629        error = nfs4_set_client(server, data->hostname,
 630                                data->addr,
 631                                data->addrlen,
 632                                parent_client->cl_ipaddr,
 633                                data->authflavor,
 634                                rpc_protocol(parent_server->client),
 635                                parent_server->client->cl_timeout,
 636                                parent_client->cl_mvops->minor_version,
 637                                parent_client->cl_net);
 638        if (error < 0)
 639                goto error;
 640
 641        error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
 642        if (error < 0)
 643                goto error;
 644
 645        error = nfs4_server_common_setup(server, mntfh);
 646        if (error < 0)
 647                goto error;
 648
 649        dprintk("<-- nfs_create_referral_server() = %p\n", server);
 650        return server;
 651
 652error:
 653        nfs_free_server(server);
 654        dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
 655        return ERR_PTR(error);
 656}
 657
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.