linux/fs/nfs/callback_proc.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfs/callback_proc.c
   3 *
   4 * Copyright (C) 2004 Trond Myklebust
   5 *
   6 * NFSv4 callback procedures
   7 */
   8#include <linux/nfs4.h>
   9#include <linux/nfs_fs.h>
  10#include <linux/slab.h>
  11#include <linux/rcupdate.h>
  12#include "nfs4_fs.h"
  13#include "callback.h"
  14#include "delegation.h"
  15#include "internal.h"
  16#include "pnfs.h"
  17#include "nfs4session.h"
  18#include "nfs4trace.h"
  19
  20#ifdef NFS_DEBUG
  21#define NFSDBG_FACILITY NFSDBG_CALLBACK
  22#endif
  23
  24__be32 nfs4_callback_getattr(struct cb_getattrargs *args,
  25                             struct cb_getattrres *res,
  26                             struct cb_process_state *cps)
  27{
  28        struct nfs_delegation *delegation;
  29        struct nfs_inode *nfsi;
  30        struct inode *inode;
  31
  32        res->status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
  33        if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
  34                goto out;
  35
  36        res->bitmap[0] = res->bitmap[1] = 0;
  37        res->status = htonl(NFS4ERR_BADHANDLE);
  38
  39        dprintk_rcu("NFS: GETATTR callback request from %s\n",
  40                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
  41
  42        inode = nfs_delegation_find_inode(cps->clp, &args->fh);
  43        if (inode == NULL)
  44                goto out;
  45        nfsi = NFS_I(inode);
  46        rcu_read_lock();
  47        delegation = rcu_dereference(nfsi->delegation);
  48        if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0)
  49                goto out_iput;
  50        res->size = i_size_read(inode);
  51        res->change_attr = delegation->change_attr;
  52        if (nfsi->npages != 0)
  53                res->change_attr++;
  54        res->ctime = inode->i_ctime;
  55        res->mtime = inode->i_mtime;
  56        res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) &
  57                args->bitmap[0];
  58        res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) &
  59                args->bitmap[1];
  60        res->status = 0;
  61out_iput:
  62        rcu_read_unlock();
  63        iput(inode);
  64out:
  65        dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status));
  66        return res->status;
  67}
  68
  69__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
  70                            struct cb_process_state *cps)
  71{
  72        struct inode *inode;
  73        __be32 res;
  74        
  75        res = htonl(NFS4ERR_OP_NOT_IN_SESSION);
  76        if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
  77                goto out;
  78
  79        dprintk_rcu("NFS: RECALL callback request from %s\n",
  80                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
  81
  82        res = htonl(NFS4ERR_BADHANDLE);
  83        inode = nfs_delegation_find_inode(cps->clp, &args->fh);
  84        if (inode == NULL)
  85                goto out;
  86        /* Set up a helper thread to actually return the delegation */
  87        switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
  88        case 0:
  89                res = 0;
  90                break;
  91        case -ENOENT:
  92                res = htonl(NFS4ERR_BAD_STATEID);
  93                break;
  94        default:
  95                res = htonl(NFS4ERR_RESOURCE);
  96        }
  97        trace_nfs4_recall_delegation(inode, -ntohl(res));
  98        iput(inode);
  99out:
 100        dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
 101        return res;
 102}
 103
 104#if defined(CONFIG_NFS_V4_1)
 105
 106/*
 107 * Lookup a layout by filehandle.
 108 *
 109 * Note: gets a refcount on the layout hdr and on its respective inode.
 110 * Caller must put the layout hdr and the inode.
 111 *
 112 * TODO: keep track of all layouts (and delegations) in a hash table
 113 * hashed by filehandle.
 114 */
 115static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp, struct nfs_fh *fh)
 116{
 117        struct nfs_server *server;
 118        struct inode *ino;
 119        struct pnfs_layout_hdr *lo;
 120
 121        list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
 122                list_for_each_entry(lo, &server->layouts, plh_layouts) {
 123                        if (nfs_compare_fh(fh, &NFS_I(lo->plh_inode)->fh))
 124                                continue;
 125                        ino = igrab(lo->plh_inode);
 126                        if (!ino)
 127                                continue;
 128                        spin_lock(&ino->i_lock);
 129                        /* Is this layout in the process of being freed? */
 130                        if (NFS_I(ino)->layout != lo) {
 131                                spin_unlock(&ino->i_lock);
 132                                iput(ino);
 133                                continue;
 134                        }
 135                        pnfs_get_layout_hdr(lo);
 136                        spin_unlock(&ino->i_lock);
 137                        return lo;
 138                }
 139        }
 140
 141        return NULL;
 142}
 143
 144static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp, struct nfs_fh *fh)
 145{
 146        struct pnfs_layout_hdr *lo;
 147
 148        spin_lock(&clp->cl_lock);
 149        rcu_read_lock();
 150        lo = get_layout_by_fh_locked(clp, fh);
 151        rcu_read_unlock();
 152        spin_unlock(&clp->cl_lock);
 153
 154        return lo;
 155}
 156
 157static u32 initiate_file_draining(struct nfs_client *clp,
 158                                  struct cb_layoutrecallargs *args)
 159{
 160        struct inode *ino;
 161        struct pnfs_layout_hdr *lo;
 162        u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
 163        LIST_HEAD(free_me_list);
 164
 165        lo = get_layout_by_fh(clp, &args->cbl_fh);
 166        if (!lo)
 167                return NFS4ERR_NOMATCHING_LAYOUT;
 168
 169        ino = lo->plh_inode;
 170        spin_lock(&ino->i_lock);
 171        if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) ||
 172            pnfs_mark_matching_lsegs_invalid(lo, &free_me_list,
 173                                        &args->cbl_range))
 174                rv = NFS4ERR_DELAY;
 175        else
 176                rv = NFS4ERR_NOMATCHING_LAYOUT;
 177        pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
 178        spin_unlock(&ino->i_lock);
 179        pnfs_free_lseg_list(&free_me_list);
 180        pnfs_put_layout_hdr(lo);
 181        iput(ino);
 182        return rv;
 183}
 184
 185static u32 initiate_bulk_draining(struct nfs_client *clp,
 186                                  struct cb_layoutrecallargs *args)
 187{
 188        int stat;
 189
 190        if (args->cbl_recall_type == RETURN_FSID)
 191                stat = pnfs_destroy_layouts_byfsid(clp, &args->cbl_fsid, true);
 192        else
 193                stat = pnfs_destroy_layouts_byclid(clp, true);
 194        if (stat != 0)
 195                return NFS4ERR_DELAY;
 196        return NFS4ERR_NOMATCHING_LAYOUT;
 197}
 198
 199static u32 do_callback_layoutrecall(struct nfs_client *clp,
 200                                    struct cb_layoutrecallargs *args)
 201{
 202        u32 res;
 203
 204        dprintk("%s enter, type=%i\n", __func__, args->cbl_recall_type);
 205        if (args->cbl_recall_type == RETURN_FILE)
 206                res = initiate_file_draining(clp, args);
 207        else
 208                res = initiate_bulk_draining(clp, args);
 209        dprintk("%s returning %i\n", __func__, res);
 210        return res;
 211
 212}
 213
 214__be32 nfs4_callback_layoutrecall(struct cb_layoutrecallargs *args,
 215                                  void *dummy, struct cb_process_state *cps)
 216{
 217        u32 res;
 218
 219        dprintk("%s: -->\n", __func__);
 220
 221        if (cps->clp)
 222                res = do_callback_layoutrecall(cps->clp, args);
 223        else
 224                res = NFS4ERR_OP_NOT_IN_SESSION;
 225
 226        dprintk("%s: exit with status = %d\n", __func__, res);
 227        return cpu_to_be32(res);
 228}
 229
 230static void pnfs_recall_all_layouts(struct nfs_client *clp)
 231{
 232        struct cb_layoutrecallargs args;
 233
 234        /* Pretend we got a CB_LAYOUTRECALL(ALL) */
 235        memset(&args, 0, sizeof(args));
 236        args.cbl_recall_type = RETURN_ALL;
 237        /* FIXME we ignore errors, what should we do? */
 238        do_callback_layoutrecall(clp, &args);
 239}
 240
 241__be32 nfs4_callback_devicenotify(struct cb_devicenotifyargs *args,
 242                                  void *dummy, struct cb_process_state *cps)
 243{
 244        int i;
 245        __be32 res = 0;
 246        struct nfs_client *clp = cps->clp;
 247        struct nfs_server *server = NULL;
 248
 249        dprintk("%s: -->\n", __func__);
 250
 251        if (!clp) {
 252                res = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION);
 253                goto out;
 254        }
 255
 256        for (i = 0; i < args->ndevs; i++) {
 257                struct cb_devicenotifyitem *dev = &args->devs[i];
 258
 259                if (!server ||
 260                    server->pnfs_curr_ld->id != dev->cbd_layout_type) {
 261                        rcu_read_lock();
 262                        list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
 263                                if (server->pnfs_curr_ld &&
 264                                    server->pnfs_curr_ld->id == dev->cbd_layout_type) {
 265                                        rcu_read_unlock();
 266                                        goto found;
 267                                }
 268                        rcu_read_unlock();
 269                        dprintk("%s: layout type %u not found\n",
 270                                __func__, dev->cbd_layout_type);
 271                        continue;
 272                }
 273
 274        found:
 275                if (dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE)
 276                        dprintk("%s: NOTIFY_DEVICEID4_CHANGE not supported, "
 277                                "deleting instead\n", __func__);
 278                nfs4_delete_deviceid(server->pnfs_curr_ld, clp, &dev->cbd_dev_id);
 279        }
 280
 281out:
 282        kfree(args->devs);
 283        dprintk("%s: exit with status = %u\n",
 284                __func__, be32_to_cpu(res));
 285        return res;
 286}
 287
 288/*
 289 * Validate the sequenceID sent by the server.
 290 * Return success if the sequenceID is one more than what we last saw on
 291 * this slot, accounting for wraparound.  Increments the slot's sequence.
 292 *
 293 * We don't yet implement a duplicate request cache, instead we set the
 294 * back channel ca_maxresponsesize_cached to zero. This is OK for now
 295 * since we only currently implement idempotent callbacks anyway.
 296 *
 297 * We have a single slot backchannel at this time, so we don't bother
 298 * checking the used_slots bit array on the table.  The lower layer guarantees
 299 * a single outstanding callback request at a time.
 300 */
 301static __be32
 302validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
 303{
 304        struct nfs4_slot *slot;
 305
 306        dprintk("%s enter. slotid %u seqid %u\n",
 307                __func__, args->csa_slotid, args->csa_sequenceid);
 308
 309        if (args->csa_slotid >= NFS41_BC_MAX_CALLBACKS)
 310                return htonl(NFS4ERR_BADSLOT);
 311
 312        slot = tbl->slots + args->csa_slotid;
 313        dprintk("%s slot table seqid: %u\n", __func__, slot->seq_nr);
 314
 315        /* Normal */
 316        if (likely(args->csa_sequenceid == slot->seq_nr + 1)) {
 317                slot->seq_nr++;
 318                goto out_ok;
 319        }
 320
 321        /* Replay */
 322        if (args->csa_sequenceid == slot->seq_nr) {
 323                dprintk("%s seqid %u is a replay\n",
 324                        __func__, args->csa_sequenceid);
 325                /* Signal process_op to set this error on next op */
 326                if (args->csa_cachethis == 0)
 327                        return htonl(NFS4ERR_RETRY_UNCACHED_REP);
 328
 329                /* The ca_maxresponsesize_cached is 0 with no DRC */
 330                else if (args->csa_cachethis == 1)
 331                        return htonl(NFS4ERR_REP_TOO_BIG_TO_CACHE);
 332        }
 333
 334        /* Wraparound */
 335        if (args->csa_sequenceid == 1 && (slot->seq_nr + 1) == 0) {
 336                slot->seq_nr = 1;
 337                goto out_ok;
 338        }
 339
 340        /* Misordered request */
 341        return htonl(NFS4ERR_SEQ_MISORDERED);
 342out_ok:
 343        tbl->highest_used_slotid = args->csa_slotid;
 344        return htonl(NFS4_OK);
 345}
 346
 347/*
 348 * For each referring call triple, check the session's slot table for
 349 * a match.  If the slot is in use and the sequence numbers match, the
 350 * client is still waiting for a response to the original request.
 351 */
 352static bool referring_call_exists(struct nfs_client *clp,
 353                                  uint32_t nrclists,
 354                                  struct referring_call_list *rclists)
 355{
 356        bool status = 0;
 357        int i, j;
 358        struct nfs4_session *session;
 359        struct nfs4_slot_table *tbl;
 360        struct referring_call_list *rclist;
 361        struct referring_call *ref;
 362
 363        /*
 364         * XXX When client trunking is implemented, this becomes
 365         * a session lookup from within the loop
 366         */
 367        session = clp->cl_session;
 368        tbl = &session->fc_slot_table;
 369
 370        for (i = 0; i < nrclists; i++) {
 371                rclist = &rclists[i];
 372                if (memcmp(session->sess_id.data,
 373                           rclist->rcl_sessionid.data,
 374                           NFS4_MAX_SESSIONID_LEN) != 0)
 375                        continue;
 376
 377                for (j = 0; j < rclist->rcl_nrefcalls; j++) {
 378                        ref = &rclist->rcl_refcalls[j];
 379
 380                        dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u "
 381                                "slotid %u\n", __func__,
 382                                ((u32 *)&rclist->rcl_sessionid.data)[0],
 383                                ((u32 *)&rclist->rcl_sessionid.data)[1],
 384                                ((u32 *)&rclist->rcl_sessionid.data)[2],
 385                                ((u32 *)&rclist->rcl_sessionid.data)[3],
 386                                ref->rc_sequenceid, ref->rc_slotid);
 387
 388                        spin_lock(&tbl->slot_tbl_lock);
 389                        status = (test_bit(ref->rc_slotid, tbl->used_slots) &&
 390                                  tbl->slots[ref->rc_slotid].seq_nr ==
 391                                        ref->rc_sequenceid);
 392                        spin_unlock(&tbl->slot_tbl_lock);
 393                        if (status)
 394                                goto out;
 395                }
 396        }
 397
 398out:
 399        return status;
 400}
 401
 402__be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
 403                              struct cb_sequenceres *res,
 404                              struct cb_process_state *cps)
 405{
 406        struct nfs4_slot_table *tbl;
 407        struct nfs_client *clp;
 408        int i;
 409        __be32 status = htonl(NFS4ERR_BADSESSION);
 410
 411        clp = nfs4_find_client_sessionid(cps->net, args->csa_addr,
 412                                         &args->csa_sessionid, cps->minorversion);
 413        if (clp == NULL)
 414                goto out;
 415
 416        tbl = &clp->cl_session->bc_slot_table;
 417
 418        spin_lock(&tbl->slot_tbl_lock);
 419        /* state manager is resetting the session */
 420        if (test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
 421                spin_unlock(&tbl->slot_tbl_lock);
 422                status = htonl(NFS4ERR_DELAY);
 423                /* Return NFS4ERR_BADSESSION if we're draining the session
 424                 * in order to reset it.
 425                 */
 426                if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
 427                        status = htonl(NFS4ERR_BADSESSION);
 428                goto out;
 429        }
 430
 431        status = validate_seqid(&clp->cl_session->bc_slot_table, args);
 432        spin_unlock(&tbl->slot_tbl_lock);
 433        if (status)
 434                goto out;
 435
 436        cps->slotid = args->csa_slotid;
 437
 438        /*
 439         * Check for pending referring calls.  If a match is found, a
 440         * related callback was received before the response to the original
 441         * call.
 442         */
 443        if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
 444                status = htonl(NFS4ERR_DELAY);
 445                goto out;
 446        }
 447
 448        memcpy(&res->csr_sessionid, &args->csa_sessionid,
 449               sizeof(res->csr_sessionid));
 450        res->csr_sequenceid = args->csa_sequenceid;
 451        res->csr_slotid = args->csa_slotid;
 452        res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
 453        res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
 454
 455out:
 456        cps->clp = clp; /* put in nfs4_callback_compound */
 457        for (i = 0; i < args->csa_nrclists; i++)
 458                kfree(args->csa_rclists[i].rcl_refcalls);
 459        kfree(args->csa_rclists);
 460
 461        if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
 462                cps->drc_status = status;
 463                status = 0;
 464        } else
 465                res->csr_status = status;
 466
 467        trace_nfs4_cb_sequence(args, res, status);
 468        dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
 469                ntohl(status), ntohl(res->csr_status));
 470        return status;
 471}
 472
 473static bool
 474validate_bitmap_values(unsigned long mask)
 475{
 476        return (mask & ~RCA4_TYPE_MASK_ALL) == 0;
 477}
 478
 479__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
 480                               struct cb_process_state *cps)
 481{
 482        __be32 status;
 483        fmode_t flags = 0;
 484
 485        status = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION);
 486        if (!cps->clp) /* set in cb_sequence */
 487                goto out;
 488
 489        dprintk_rcu("NFS: RECALL_ANY callback request from %s\n",
 490                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
 491
 492        status = cpu_to_be32(NFS4ERR_INVAL);
 493        if (!validate_bitmap_values(args->craa_type_mask))
 494                goto out;
 495
 496        status = cpu_to_be32(NFS4_OK);
 497        if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *)
 498                     &args->craa_type_mask))
 499                flags = FMODE_READ;
 500        if (test_bit(RCA4_TYPE_MASK_WDATA_DLG, (const unsigned long *)
 501                     &args->craa_type_mask))
 502                flags |= FMODE_WRITE;
 503        if (test_bit(RCA4_TYPE_MASK_FILE_LAYOUT, (const unsigned long *)
 504                     &args->craa_type_mask))
 505                pnfs_recall_all_layouts(cps->clp);
 506        if (flags)
 507                nfs_expire_unused_delegation_types(cps->clp, flags);
 508out:
 509        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 510        return status;
 511}
 512
 513/* Reduce the fore channel's max_slots to the target value */
 514__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
 515                                struct cb_process_state *cps)
 516{
 517        struct nfs4_slot_table *fc_tbl;
 518        __be32 status;
 519
 520        status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
 521        if (!cps->clp) /* set in cb_sequence */
 522                goto out;
 523
 524        dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target highest slotid %u\n",
 525                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR),
 526                args->crsa_target_highest_slotid);
 527
 528        fc_tbl = &cps->clp->cl_session->fc_slot_table;
 529
 530        status = htonl(NFS4_OK);
 531
 532        nfs41_set_target_slotid(fc_tbl, args->crsa_target_highest_slotid);
 533        nfs41_server_notify_target_slotid_update(cps->clp);
 534out:
 535        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 536        return status;
 537}
 538#endif /* CONFIG_NFS_V4_1 */
 539