linux/fs/orangefs/super.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * (C) 2001 Clemson University and The University of Chicago
   4 *
   5 * See COPYING in top-level directory.
   6 */
   7
   8#include "protocol.h"
   9#include "orangefs-kernel.h"
  10#include "orangefs-bufmap.h"
  11
  12#include <linux/parser.h>
  13
  14/* a cache for orangefs-inode objects (i.e. orangefs inode private data) */
  15static struct kmem_cache *orangefs_inode_cache;
  16
  17/* list for storing orangefs specific superblocks in use */
  18LIST_HEAD(orangefs_superblocks);
  19
  20DEFINE_SPINLOCK(orangefs_superblocks_lock);
  21
  22enum {
  23        Opt_intr,
  24        Opt_acl,
  25        Opt_local_lock,
  26
  27        Opt_err
  28};
  29
  30static const match_table_t tokens = {
  31        { Opt_acl,              "acl" },
  32        { Opt_intr,             "intr" },
  33        { Opt_local_lock,       "local_lock" },
  34        { Opt_err,      NULL }
  35};
  36
  37uint64_t orangefs_features;
  38
  39static int orangefs_show_options(struct seq_file *m, struct dentry *root)
  40{
  41        struct orangefs_sb_info_s *orangefs_sb = ORANGEFS_SB(root->d_sb);
  42
  43        if (root->d_sb->s_flags & SB_POSIXACL)
  44                seq_puts(m, ",acl");
  45        if (orangefs_sb->flags & ORANGEFS_OPT_INTR)
  46                seq_puts(m, ",intr");
  47        if (orangefs_sb->flags & ORANGEFS_OPT_LOCAL_LOCK)
  48                seq_puts(m, ",local_lock");
  49        return 0;
  50}
  51
  52static int parse_mount_options(struct super_block *sb, char *options,
  53                int silent)
  54{
  55        struct orangefs_sb_info_s *orangefs_sb = ORANGEFS_SB(sb);
  56        substring_t args[MAX_OPT_ARGS];
  57        char *p;
  58
  59        /*
  60         * Force any potential flags that might be set from the mount
  61         * to zero, ie, initialize to unset.
  62         */
  63        sb->s_flags &= ~SB_POSIXACL;
  64        orangefs_sb->flags &= ~ORANGEFS_OPT_INTR;
  65        orangefs_sb->flags &= ~ORANGEFS_OPT_LOCAL_LOCK;
  66
  67        while ((p = strsep(&options, ",")) != NULL) {
  68                int token;
  69
  70                if (!*p)
  71                        continue;
  72
  73                token = match_token(p, tokens, args);
  74                switch (token) {
  75                case Opt_acl:
  76                        sb->s_flags |= SB_POSIXACL;
  77                        break;
  78                case Opt_intr:
  79                        orangefs_sb->flags |= ORANGEFS_OPT_INTR;
  80                        break;
  81                case Opt_local_lock:
  82                        orangefs_sb->flags |= ORANGEFS_OPT_LOCAL_LOCK;
  83                        break;
  84                default:
  85                        goto fail;
  86                }
  87        }
  88
  89        return 0;
  90fail:
  91        if (!silent)
  92                gossip_err("Error: mount option [%s] is not supported.\n", p);
  93        return -EINVAL;
  94}
  95
  96static void orangefs_inode_cache_ctor(void *req)
  97{
  98        struct orangefs_inode_s *orangefs_inode = req;
  99
 100        inode_init_once(&orangefs_inode->vfs_inode);
 101        init_rwsem(&orangefs_inode->xattr_sem);
 102}
 103
 104static struct inode *orangefs_alloc_inode(struct super_block *sb)
 105{
 106        struct orangefs_inode_s *orangefs_inode;
 107
 108        orangefs_inode = kmem_cache_alloc(orangefs_inode_cache, GFP_KERNEL);
 109        if (!orangefs_inode)
 110                return NULL;
 111
 112        /*
 113         * We want to clear everything except for rw_semaphore and the
 114         * vfs_inode.
 115         */
 116        memset(&orangefs_inode->refn.khandle, 0, 16);
 117        orangefs_inode->refn.fs_id = ORANGEFS_FS_ID_NULL;
 118        orangefs_inode->last_failed_block_index_read = 0;
 119        memset(orangefs_inode->link_target, 0, sizeof(orangefs_inode->link_target));
 120
 121        gossip_debug(GOSSIP_SUPER_DEBUG,
 122                     "orangefs_alloc_inode: allocated %p\n",
 123                     &orangefs_inode->vfs_inode);
 124        return &orangefs_inode->vfs_inode;
 125}
 126
 127static void orangefs_i_callback(struct rcu_head *head)
 128{
 129        struct inode *inode = container_of(head, struct inode, i_rcu);
 130        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
 131        kmem_cache_free(orangefs_inode_cache, orangefs_inode);
 132}
 133
 134static void orangefs_destroy_inode(struct inode *inode)
 135{
 136        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
 137
 138        gossip_debug(GOSSIP_SUPER_DEBUG,
 139                        "%s: deallocated %p destroying inode %pU\n",
 140                        __func__, orangefs_inode, get_khandle_from_ino(inode));
 141
 142        call_rcu(&inode->i_rcu, orangefs_i_callback);
 143}
 144
 145/*
 146 * NOTE: information filled in here is typically reflected in the
 147 * output of the system command 'df'
 148*/
 149static int orangefs_statfs(struct dentry *dentry, struct kstatfs *buf)
 150{
 151        int ret = -ENOMEM;
 152        struct orangefs_kernel_op_s *new_op = NULL;
 153        int flags = 0;
 154        struct super_block *sb = NULL;
 155
 156        sb = dentry->d_sb;
 157
 158        gossip_debug(GOSSIP_SUPER_DEBUG,
 159                     "orangefs_statfs: called on sb %p (fs_id is %d)\n",
 160                     sb,
 161                     (int)(ORANGEFS_SB(sb)->fs_id));
 162
 163        new_op = op_alloc(ORANGEFS_VFS_OP_STATFS);
 164        if (!new_op)
 165                return ret;
 166        new_op->upcall.req.statfs.fs_id = ORANGEFS_SB(sb)->fs_id;
 167
 168        if (ORANGEFS_SB(sb)->flags & ORANGEFS_OPT_INTR)
 169                flags = ORANGEFS_OP_INTERRUPTIBLE;
 170
 171        ret = service_operation(new_op, "orangefs_statfs", flags);
 172
 173        if (new_op->downcall.status < 0)
 174                goto out_op_release;
 175
 176        gossip_debug(GOSSIP_SUPER_DEBUG,
 177                     "%s: got %ld blocks available | "
 178                     "%ld blocks total | %ld block size | "
 179                     "%ld files total | %ld files avail\n",
 180                     __func__,
 181                     (long)new_op->downcall.resp.statfs.blocks_avail,
 182                     (long)new_op->downcall.resp.statfs.blocks_total,
 183                     (long)new_op->downcall.resp.statfs.block_size,
 184                     (long)new_op->downcall.resp.statfs.files_total,
 185                     (long)new_op->downcall.resp.statfs.files_avail);
 186
 187        buf->f_type = sb->s_magic;
 188        memcpy(&buf->f_fsid, &ORANGEFS_SB(sb)->fs_id, sizeof(buf->f_fsid));
 189        buf->f_bsize = new_op->downcall.resp.statfs.block_size;
 190        buf->f_namelen = ORANGEFS_NAME_MAX;
 191
 192        buf->f_blocks = (sector_t) new_op->downcall.resp.statfs.blocks_total;
 193        buf->f_bfree = (sector_t) new_op->downcall.resp.statfs.blocks_avail;
 194        buf->f_bavail = (sector_t) new_op->downcall.resp.statfs.blocks_avail;
 195        buf->f_files = (sector_t) new_op->downcall.resp.statfs.files_total;
 196        buf->f_ffree = (sector_t) new_op->downcall.resp.statfs.files_avail;
 197        buf->f_frsize = sb->s_blocksize;
 198
 199out_op_release:
 200        op_release(new_op);
 201        gossip_debug(GOSSIP_SUPER_DEBUG, "orangefs_statfs: returning %d\n", ret);
 202        return ret;
 203}
 204
 205/*
 206 * Remount as initiated by VFS layer.  We just need to reparse the mount
 207 * options, no need to signal pvfs2-client-core about it.
 208 */
 209static int orangefs_remount_fs(struct super_block *sb, int *flags, char *data)
 210{
 211        gossip_debug(GOSSIP_SUPER_DEBUG, "orangefs_remount_fs: called\n");
 212        return parse_mount_options(sb, data, 1);
 213}
 214
 215/*
 216 * Remount as initiated by pvfs2-client-core on restart.  This is used to
 217 * repopulate mount information left from previous pvfs2-client-core.
 218 *
 219 * the idea here is that given a valid superblock, we're
 220 * re-initializing the user space client with the initial mount
 221 * information specified when the super block was first initialized.
 222 * this is very different than the first initialization/creation of a
 223 * superblock.  we use the special service_priority_operation to make
 224 * sure that the mount gets ahead of any other pending operation that
 225 * is waiting for servicing.  this means that the pvfs2-client won't
 226 * fail to start several times for all other pending operations before
 227 * the client regains all of the mount information from us.
 228 * NOTE: this function assumes that the request_mutex is already acquired!
 229 */
 230int orangefs_remount(struct orangefs_sb_info_s *orangefs_sb)
 231{
 232        struct orangefs_kernel_op_s *new_op;
 233        int ret = -EINVAL;
 234
 235        gossip_debug(GOSSIP_SUPER_DEBUG, "orangefs_remount: called\n");
 236
 237        new_op = op_alloc(ORANGEFS_VFS_OP_FS_MOUNT);
 238        if (!new_op)
 239                return -ENOMEM;
 240        strncpy(new_op->upcall.req.fs_mount.orangefs_config_server,
 241                orangefs_sb->devname,
 242                ORANGEFS_MAX_SERVER_ADDR_LEN);
 243
 244        gossip_debug(GOSSIP_SUPER_DEBUG,
 245                     "Attempting ORANGEFS Remount via host %s\n",
 246                     new_op->upcall.req.fs_mount.orangefs_config_server);
 247
 248        /*
 249         * we assume that the calling function has already acquired the
 250         * request_mutex to prevent other operations from bypassing
 251         * this one
 252         */
 253        ret = service_operation(new_op, "orangefs_remount",
 254                ORANGEFS_OP_PRIORITY | ORANGEFS_OP_NO_MUTEX);
 255        gossip_debug(GOSSIP_SUPER_DEBUG,
 256                     "orangefs_remount: mount got return value of %d\n",
 257                     ret);
 258        if (ret == 0) {
 259                /*
 260                 * store the id assigned to this sb -- it's just a
 261                 * short-lived mapping that the system interface uses
 262                 * to map this superblock to a particular mount entry
 263                 */
 264                orangefs_sb->id = new_op->downcall.resp.fs_mount.id;
 265                orangefs_sb->mount_pending = 0;
 266        }
 267
 268        op_release(new_op);
 269
 270        if (orangefs_userspace_version >= 20906) {
 271                new_op = op_alloc(ORANGEFS_VFS_OP_FEATURES);
 272                if (!new_op)
 273                        return -ENOMEM;
 274                new_op->upcall.req.features.features = 0;
 275                ret = service_operation(new_op, "orangefs_features",
 276                    ORANGEFS_OP_PRIORITY | ORANGEFS_OP_NO_MUTEX);
 277                if (!ret)
 278                        orangefs_features =
 279                            new_op->downcall.resp.features.features;
 280                else
 281                        orangefs_features = 0;
 282                op_release(new_op);
 283        } else {
 284                orangefs_features = 0;
 285        }
 286
 287        return ret;
 288}
 289
 290int fsid_key_table_initialize(void)
 291{
 292        return 0;
 293}
 294
 295void fsid_key_table_finalize(void)
 296{
 297}
 298
 299static const struct super_operations orangefs_s_ops = {
 300        .alloc_inode = orangefs_alloc_inode,
 301        .destroy_inode = orangefs_destroy_inode,
 302        .drop_inode = generic_delete_inode,
 303        .statfs = orangefs_statfs,
 304        .remount_fs = orangefs_remount_fs,
 305        .show_options = orangefs_show_options,
 306};
 307
 308static struct dentry *orangefs_fh_to_dentry(struct super_block *sb,
 309                                  struct fid *fid,
 310                                  int fh_len,
 311                                  int fh_type)
 312{
 313        struct orangefs_object_kref refn;
 314
 315        if (fh_len < 5 || fh_type > 2)
 316                return NULL;
 317
 318        ORANGEFS_khandle_from(&(refn.khandle), fid->raw, 16);
 319        refn.fs_id = (u32) fid->raw[4];
 320        gossip_debug(GOSSIP_SUPER_DEBUG,
 321                     "fh_to_dentry: handle %pU, fs_id %d\n",
 322                     &refn.khandle,
 323                     refn.fs_id);
 324
 325        return d_obtain_alias(orangefs_iget(sb, &refn));
 326}
 327
 328static int orangefs_encode_fh(struct inode *inode,
 329                    __u32 *fh,
 330                    int *max_len,
 331                    struct inode *parent)
 332{
 333        int len = parent ? 10 : 5;
 334        int type = 1;
 335        struct orangefs_object_kref refn;
 336
 337        if (*max_len < len) {
 338                gossip_err("fh buffer is too small for encoding\n");
 339                *max_len = len;
 340                type = 255;
 341                goto out;
 342        }
 343
 344        refn = ORANGEFS_I(inode)->refn;
 345        ORANGEFS_khandle_to(&refn.khandle, fh, 16);
 346        fh[4] = refn.fs_id;
 347
 348        gossip_debug(GOSSIP_SUPER_DEBUG,
 349                     "Encoding fh: handle %pU, fsid %u\n",
 350                     &refn.khandle,
 351                     refn.fs_id);
 352
 353
 354        if (parent) {
 355                refn = ORANGEFS_I(parent)->refn;
 356                ORANGEFS_khandle_to(&refn.khandle, (char *) fh + 20, 16);
 357                fh[9] = refn.fs_id;
 358
 359                type = 2;
 360                gossip_debug(GOSSIP_SUPER_DEBUG,
 361                             "Encoding parent: handle %pU, fsid %u\n",
 362                             &refn.khandle,
 363                             refn.fs_id);
 364        }
 365        *max_len = len;
 366
 367out:
 368        return type;
 369}
 370
 371static const struct export_operations orangefs_export_ops = {
 372        .encode_fh = orangefs_encode_fh,
 373        .fh_to_dentry = orangefs_fh_to_dentry,
 374};
 375
 376static int orangefs_unmount(int id, __s32 fs_id, const char *devname)
 377{
 378        struct orangefs_kernel_op_s *op;
 379        int r;
 380        op = op_alloc(ORANGEFS_VFS_OP_FS_UMOUNT);
 381        if (!op)
 382                return -ENOMEM;
 383        op->upcall.req.fs_umount.id = id;
 384        op->upcall.req.fs_umount.fs_id = fs_id;
 385        strncpy(op->upcall.req.fs_umount.orangefs_config_server,
 386            devname, ORANGEFS_MAX_SERVER_ADDR_LEN - 1);
 387        r = service_operation(op, "orangefs_fs_umount", 0);
 388        /* Not much to do about an error here. */
 389        if (r)
 390                gossip_err("orangefs_unmount: service_operation %d\n", r);
 391        op_release(op);
 392        return r;
 393}
 394
 395static int orangefs_fill_sb(struct super_block *sb,
 396                struct orangefs_fs_mount_response *fs_mount,
 397                void *data, int silent)
 398{
 399        int ret = -EINVAL;
 400        struct inode *root = NULL;
 401        struct dentry *root_dentry = NULL;
 402        struct orangefs_object_kref root_object;
 403
 404        /* alloc and init our private orangefs sb info */
 405        sb->s_fs_info = kzalloc(sizeof(struct orangefs_sb_info_s), GFP_KERNEL);
 406        if (!ORANGEFS_SB(sb))
 407                return -ENOMEM;
 408        ORANGEFS_SB(sb)->sb = sb;
 409
 410        ORANGEFS_SB(sb)->root_khandle = fs_mount->root_khandle;
 411        ORANGEFS_SB(sb)->fs_id = fs_mount->fs_id;
 412        ORANGEFS_SB(sb)->id = fs_mount->id;
 413
 414        if (data) {
 415                ret = parse_mount_options(sb, data, silent);
 416                if (ret)
 417                        return ret;
 418        }
 419
 420        /* Hang the xattr handlers off the superblock */
 421        sb->s_xattr = orangefs_xattr_handlers;
 422        sb->s_magic = ORANGEFS_SUPER_MAGIC;
 423        sb->s_op = &orangefs_s_ops;
 424        sb->s_d_op = &orangefs_dentry_operations;
 425
 426        sb->s_blocksize = orangefs_bufmap_size_query();
 427        sb->s_blocksize_bits = orangefs_bufmap_shift_query();
 428        sb->s_maxbytes = MAX_LFS_FILESIZE;
 429
 430        root_object.khandle = ORANGEFS_SB(sb)->root_khandle;
 431        root_object.fs_id = ORANGEFS_SB(sb)->fs_id;
 432        gossip_debug(GOSSIP_SUPER_DEBUG,
 433                     "get inode %pU, fsid %d\n",
 434                     &root_object.khandle,
 435                     root_object.fs_id);
 436
 437        root = orangefs_iget(sb, &root_object);
 438        if (IS_ERR(root))
 439                return PTR_ERR(root);
 440
 441        gossip_debug(GOSSIP_SUPER_DEBUG,
 442                     "Allocated root inode [%p] with mode %x\n",
 443                     root,
 444                     root->i_mode);
 445
 446        /* allocates and places root dentry in dcache */
 447        root_dentry = d_make_root(root);
 448        if (!root_dentry)
 449                return -ENOMEM;
 450
 451        sb->s_export_op = &orangefs_export_ops;
 452        sb->s_root = root_dentry;
 453        return 0;
 454}
 455
 456struct dentry *orangefs_mount(struct file_system_type *fst,
 457                           int flags,
 458                           const char *devname,
 459                           void *data)
 460{
 461        int ret = -EINVAL;
 462        struct super_block *sb = ERR_PTR(-EINVAL);
 463        struct orangefs_kernel_op_s *new_op;
 464        struct dentry *d = ERR_PTR(-EINVAL);
 465
 466        gossip_debug(GOSSIP_SUPER_DEBUG,
 467                     "orangefs_mount: called with devname %s\n",
 468                     devname);
 469
 470        if (!devname) {
 471                gossip_err("ERROR: device name not specified.\n");
 472                return ERR_PTR(-EINVAL);
 473        }
 474
 475        new_op = op_alloc(ORANGEFS_VFS_OP_FS_MOUNT);
 476        if (!new_op)
 477                return ERR_PTR(-ENOMEM);
 478
 479        strncpy(new_op->upcall.req.fs_mount.orangefs_config_server,
 480                devname,
 481                ORANGEFS_MAX_SERVER_ADDR_LEN - 1);
 482
 483        gossip_debug(GOSSIP_SUPER_DEBUG,
 484                     "Attempting ORANGEFS Mount via host %s\n",
 485                     new_op->upcall.req.fs_mount.orangefs_config_server);
 486
 487        ret = service_operation(new_op, "orangefs_mount", 0);
 488        gossip_debug(GOSSIP_SUPER_DEBUG,
 489                     "orangefs_mount: mount got return value of %d\n", ret);
 490        if (ret)
 491                goto free_op;
 492
 493        if (new_op->downcall.resp.fs_mount.fs_id == ORANGEFS_FS_ID_NULL) {
 494                gossip_err("ERROR: Retrieved null fs_id\n");
 495                ret = -EINVAL;
 496                goto free_op;
 497        }
 498
 499        sb = sget(fst, NULL, set_anon_super, flags, NULL);
 500
 501        if (IS_ERR(sb)) {
 502                d = ERR_CAST(sb);
 503                orangefs_unmount(new_op->downcall.resp.fs_mount.id,
 504                    new_op->downcall.resp.fs_mount.fs_id, devname);
 505                goto free_op;
 506        }
 507
 508        ret = orangefs_fill_sb(sb,
 509              &new_op->downcall.resp.fs_mount, data,
 510              flags & SB_SILENT ? 1 : 0);
 511
 512        if (ret) {
 513                d = ERR_PTR(ret);
 514                goto free_sb_and_op;
 515        }
 516
 517        /*
 518         * on successful mount, store the devname and data
 519         * used
 520         */
 521        strncpy(ORANGEFS_SB(sb)->devname,
 522                devname,
 523                ORANGEFS_MAX_SERVER_ADDR_LEN - 1);
 524
 525        /* mount_pending must be cleared */
 526        ORANGEFS_SB(sb)->mount_pending = 0;
 527
 528        /*
 529         * finally, add this sb to our list of known orangefs
 530         * sb's
 531         */
 532        gossip_debug(GOSSIP_SUPER_DEBUG,
 533                     "Adding SB %p to orangefs superblocks\n",
 534                     ORANGEFS_SB(sb));
 535        spin_lock(&orangefs_superblocks_lock);
 536        list_add_tail(&ORANGEFS_SB(sb)->list, &orangefs_superblocks);
 537        spin_unlock(&orangefs_superblocks_lock);
 538        op_release(new_op);
 539
 540        /* Must be removed from the list now. */
 541        ORANGEFS_SB(sb)->no_list = 0;
 542
 543        if (orangefs_userspace_version >= 20906) {
 544                new_op = op_alloc(ORANGEFS_VFS_OP_FEATURES);
 545                if (!new_op)
 546                        return ERR_PTR(-ENOMEM);
 547                new_op->upcall.req.features.features = 0;
 548                ret = service_operation(new_op, "orangefs_features", 0);
 549                orangefs_features = new_op->downcall.resp.features.features;
 550                op_release(new_op);
 551        } else {
 552                orangefs_features = 0;
 553        }
 554
 555        return dget(sb->s_root);
 556
 557free_sb_and_op:
 558        /* Will call orangefs_kill_sb with sb not in list. */
 559        ORANGEFS_SB(sb)->no_list = 1;
 560        /* ORANGEFS_VFS_OP_FS_UMOUNT is done by orangefs_kill_sb. */
 561        deactivate_locked_super(sb);
 562free_op:
 563        gossip_err("orangefs_mount: mount request failed with %d\n", ret);
 564        if (ret == -EINVAL) {
 565                gossip_err("Ensure that all orangefs-servers have the same FS configuration files\n");
 566                gossip_err("Look at pvfs2-client-core log file (typically /tmp/pvfs2-client.log) for more details\n");
 567        }
 568
 569        op_release(new_op);
 570
 571        return d;
 572}
 573
 574void orangefs_kill_sb(struct super_block *sb)
 575{
 576        int r;
 577        gossip_debug(GOSSIP_SUPER_DEBUG, "orangefs_kill_sb: called\n");
 578
 579        /* provided sb cleanup */
 580        kill_anon_super(sb);
 581
 582        if (!ORANGEFS_SB(sb)) {
 583                mutex_lock(&orangefs_request_mutex);
 584                mutex_unlock(&orangefs_request_mutex);
 585                return;
 586        }
 587        /*
 588         * issue the unmount to userspace to tell it to remove the
 589         * dynamic mount info it has for this superblock
 590         */
 591        r = orangefs_unmount(ORANGEFS_SB(sb)->id, ORANGEFS_SB(sb)->fs_id,
 592            ORANGEFS_SB(sb)->devname);
 593        if (!r)
 594                ORANGEFS_SB(sb)->mount_pending = 1;
 595
 596        if (!ORANGEFS_SB(sb)->no_list) {
 597                /* remove the sb from our list of orangefs specific sb's */
 598                spin_lock(&orangefs_superblocks_lock);
 599                /* not list_del_init */
 600                __list_del_entry(&ORANGEFS_SB(sb)->list);
 601                ORANGEFS_SB(sb)->list.prev = NULL;
 602                spin_unlock(&orangefs_superblocks_lock);
 603        }
 604
 605        /*
 606         * make sure that ORANGEFS_DEV_REMOUNT_ALL loop that might've seen us
 607         * gets completed before we free the dang thing.
 608         */
 609        mutex_lock(&orangefs_request_mutex);
 610        mutex_unlock(&orangefs_request_mutex);
 611
 612        /* free the orangefs superblock private data */
 613        kfree(ORANGEFS_SB(sb));
 614}
 615
 616int orangefs_inode_cache_initialize(void)
 617{
 618        orangefs_inode_cache = kmem_cache_create_usercopy(
 619                                        "orangefs_inode_cache",
 620                                        sizeof(struct orangefs_inode_s),
 621                                        0,
 622                                        ORANGEFS_CACHE_CREATE_FLAGS,
 623                                        offsetof(struct orangefs_inode_s,
 624                                                link_target),
 625                                        sizeof_field(struct orangefs_inode_s,
 626                                                link_target),
 627                                        orangefs_inode_cache_ctor);
 628
 629        if (!orangefs_inode_cache) {
 630                gossip_err("Cannot create orangefs_inode_cache\n");
 631                return -ENOMEM;
 632        }
 633        return 0;
 634}
 635
 636int orangefs_inode_cache_finalize(void)
 637{
 638        kmem_cache_destroy(orangefs_inode_cache);
 639        return 0;
 640}
 641