linux/fs/ceph/cache.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Ceph cache definitions.
   4 *
   5 *  Copyright (C) 2013 by Adfin Solutions, Inc. All Rights Reserved.
   6 *  Written by Milosz Tanski (milosz@adfin.com)
   7 */
   8
   9#include <linux/ceph/ceph_debug.h>
  10
  11#include <linux/fs_context.h>
  12#include "super.h"
  13#include "cache.h"
  14
  15struct ceph_aux_inode {
  16        u64     version;
  17        u64     mtime_sec;
  18        u64     mtime_nsec;
  19};
  20
  21struct fscache_netfs ceph_cache_netfs = {
  22        .name           = "ceph",
  23        .version        = 0,
  24};
  25
  26static DEFINE_MUTEX(ceph_fscache_lock);
  27static LIST_HEAD(ceph_fscache_list);
  28
  29struct ceph_fscache_entry {
  30        struct list_head list;
  31        struct fscache_cookie *fscache;
  32        size_t uniq_len;
  33        /* The following members must be last */
  34        struct ceph_fsid fsid;
  35        char uniquifier[];
  36};
  37
  38static const struct fscache_cookie_def ceph_fscache_fsid_object_def = {
  39        .name           = "CEPH.fsid",
  40        .type           = FSCACHE_COOKIE_TYPE_INDEX,
  41};
  42
  43int __init ceph_fscache_register(void)
  44{
  45        return fscache_register_netfs(&ceph_cache_netfs);
  46}
  47
  48void ceph_fscache_unregister(void)
  49{
  50        fscache_unregister_netfs(&ceph_cache_netfs);
  51}
  52
  53int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc)
  54{
  55        const struct ceph_fsid *fsid = &fsc->client->fsid;
  56        const char *fscache_uniq = fsc->mount_options->fscache_uniq;
  57        size_t uniq_len = fscache_uniq ? strlen(fscache_uniq) : 0;
  58        struct ceph_fscache_entry *ent;
  59        int err = 0;
  60
  61        mutex_lock(&ceph_fscache_lock);
  62        list_for_each_entry(ent, &ceph_fscache_list, list) {
  63                if (memcmp(&ent->fsid, fsid, sizeof(*fsid)))
  64                        continue;
  65                if (ent->uniq_len != uniq_len)
  66                        continue;
  67                if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len))
  68                        continue;
  69
  70                errorfc(fc, "fscache cookie already registered for fsid %pU, use fsc=<uniquifier> option",
  71                       fsid);
  72                err = -EBUSY;
  73                goto out_unlock;
  74        }
  75
  76        ent = kzalloc(sizeof(*ent) + uniq_len, GFP_KERNEL);
  77        if (!ent) {
  78                err = -ENOMEM;
  79                goto out_unlock;
  80        }
  81
  82        memcpy(&ent->fsid, fsid, sizeof(*fsid));
  83        if (uniq_len > 0) {
  84                memcpy(&ent->uniquifier, fscache_uniq, uniq_len);
  85                ent->uniq_len = uniq_len;
  86        }
  87
  88        fsc->fscache = fscache_acquire_cookie(ceph_cache_netfs.primary_index,
  89                                              &ceph_fscache_fsid_object_def,
  90                                              &ent->fsid, sizeof(ent->fsid) + uniq_len,
  91                                              NULL, 0,
  92                                              fsc, 0, true);
  93
  94        if (fsc->fscache) {
  95                ent->fscache = fsc->fscache;
  96                list_add_tail(&ent->list, &ceph_fscache_list);
  97        } else {
  98                kfree(ent);
  99                errorfc(fc, "unable to register fscache cookie for fsid %pU",
 100                       fsid);
 101                /* all other fs ignore this error */
 102        }
 103out_unlock:
 104        mutex_unlock(&ceph_fscache_lock);
 105        return err;
 106}
 107
 108static enum fscache_checkaux ceph_fscache_inode_check_aux(
 109        void *cookie_netfs_data, const void *data, uint16_t dlen,
 110        loff_t object_size)
 111{
 112        struct ceph_aux_inode aux;
 113        struct ceph_inode_info* ci = cookie_netfs_data;
 114        struct inode* inode = &ci->vfs_inode;
 115
 116        if (dlen != sizeof(aux) ||
 117            i_size_read(inode) != object_size)
 118                return FSCACHE_CHECKAUX_OBSOLETE;
 119
 120        memset(&aux, 0, sizeof(aux));
 121        aux.version = ci->i_version;
 122        aux.mtime_sec = inode->i_mtime.tv_sec;
 123        aux.mtime_nsec = inode->i_mtime.tv_nsec;
 124
 125        if (memcmp(data, &aux, sizeof(aux)) != 0)
 126                return FSCACHE_CHECKAUX_OBSOLETE;
 127
 128        dout("ceph inode 0x%p cached okay\n", ci);
 129        return FSCACHE_CHECKAUX_OKAY;
 130}
 131
 132static const struct fscache_cookie_def ceph_fscache_inode_object_def = {
 133        .name           = "CEPH.inode",
 134        .type           = FSCACHE_COOKIE_TYPE_DATAFILE,
 135        .check_aux      = ceph_fscache_inode_check_aux,
 136};
 137
 138void ceph_fscache_register_inode_cookie(struct inode *inode)
 139{
 140        struct ceph_inode_info *ci = ceph_inode(inode);
 141        struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
 142        struct ceph_aux_inode aux;
 143
 144        /* No caching for filesystem */
 145        if (!fsc->fscache)
 146                return;
 147
 148        /* Only cache for regular files that are read only */
 149        if (!S_ISREG(inode->i_mode))
 150                return;
 151
 152        inode_lock_nested(inode, I_MUTEX_CHILD);
 153        if (!ci->fscache) {
 154                memset(&aux, 0, sizeof(aux));
 155                aux.version = ci->i_version;
 156                aux.mtime_sec = inode->i_mtime.tv_sec;
 157                aux.mtime_nsec = inode->i_mtime.tv_nsec;
 158                ci->fscache = fscache_acquire_cookie(fsc->fscache,
 159                                                     &ceph_fscache_inode_object_def,
 160                                                     &ci->i_vino, sizeof(ci->i_vino),
 161                                                     &aux, sizeof(aux),
 162                                                     ci, i_size_read(inode), false);
 163        }
 164        inode_unlock(inode);
 165}
 166
 167void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci)
 168{
 169        struct fscache_cookie* cookie;
 170
 171        if ((cookie = ci->fscache) == NULL)
 172                return;
 173
 174        ci->fscache = NULL;
 175
 176        fscache_relinquish_cookie(cookie, &ci->i_vino, false);
 177}
 178
 179static bool ceph_fscache_can_enable(void *data)
 180{
 181        struct inode *inode = data;
 182        return !inode_is_open_for_write(inode);
 183}
 184
 185void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp)
 186{
 187        struct ceph_inode_info *ci = ceph_inode(inode);
 188
 189        if (!fscache_cookie_valid(ci->fscache))
 190                return;
 191
 192        if (inode_is_open_for_write(inode)) {
 193                dout("fscache_file_set_cookie %p %p disabling cache\n",
 194                     inode, filp);
 195                fscache_disable_cookie(ci->fscache, &ci->i_vino, false);
 196        } else {
 197                fscache_enable_cookie(ci->fscache, &ci->i_vino, i_size_read(inode),
 198                                      ceph_fscache_can_enable, inode);
 199                if (fscache_cookie_enabled(ci->fscache)) {
 200                        dout("fscache_file_set_cookie %p %p enabling cache\n",
 201                             inode, filp);
 202                }
 203        }
 204}
 205
 206void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc)
 207{
 208        if (fscache_cookie_valid(fsc->fscache)) {
 209                struct ceph_fscache_entry *ent;
 210                bool found = false;
 211
 212                mutex_lock(&ceph_fscache_lock);
 213                list_for_each_entry(ent, &ceph_fscache_list, list) {
 214                        if (ent->fscache == fsc->fscache) {
 215                                list_del(&ent->list);
 216                                kfree(ent);
 217                                found = true;
 218                                break;
 219                        }
 220                }
 221                WARN_ON_ONCE(!found);
 222                mutex_unlock(&ceph_fscache_lock);
 223
 224                __fscache_relinquish_cookie(fsc->fscache, NULL, false);
 225        }
 226        fsc->fscache = NULL;
 227}
 228