linux/fs/cifs/cache.c
<<
>>
Prefs
   1/*
   2 *   fs/cifs/cache.c - CIFS filesystem cache index structure definitions
   3 *
   4 *   Copyright (c) 2010 Novell, Inc.
   5 *   Authors(s): Suresh Jayaraman (sjayaraman@suse.de>
   6 *
   7 *   This library is free software; you can redistribute it and/or modify
   8 *   it under the terms of the GNU Lesser General Public License as published
   9 *   by the Free Software Foundation; either version 2.1 of the License, or
  10 *   (at your option) any later version.
  11 *
  12 *   This library is distributed in the hope that it will be useful,
  13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  15 *   the GNU Lesser General Public License for more details.
  16 *
  17 *   You should have received a copy of the GNU Lesser General Public License
  18 *   along with this library; if not, write to the Free Software
  19 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20 */
  21#include "fscache.h"
  22#include "cifs_debug.h"
  23
  24/*
  25 * CIFS filesystem definition for FS-Cache
  26 */
  27struct fscache_netfs cifs_fscache_netfs = {
  28        .name = "cifs",
  29        .version = 0,
  30};
  31
  32/*
  33 * Register CIFS for caching with FS-Cache
  34 */
  35int cifs_fscache_register(void)
  36{
  37        return fscache_register_netfs(&cifs_fscache_netfs);
  38}
  39
  40/*
  41 * Unregister CIFS for caching
  42 */
  43void cifs_fscache_unregister(void)
  44{
  45        fscache_unregister_netfs(&cifs_fscache_netfs);
  46}
  47
  48/*
  49 * Key layout of CIFS server cache index object
  50 */
  51struct cifs_server_key {
  52        uint16_t        family;         /* address family */
  53        uint16_t        port;           /* IP port */
  54        union {
  55                struct in_addr  ipv4_addr;
  56                struct in6_addr ipv6_addr;
  57        } addr[0];
  58};
  59
  60/*
  61 * Server object keyed by {IPaddress,port,family} tuple
  62 */
  63static uint16_t cifs_server_get_key(const void *cookie_netfs_data,
  64                                   void *buffer, uint16_t maxbuf)
  65{
  66        const struct TCP_Server_Info *server = cookie_netfs_data;
  67        const struct sockaddr *sa = (struct sockaddr *) &server->dstaddr;
  68        const struct sockaddr_in *addr = (struct sockaddr_in *) sa;
  69        const struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) sa;
  70        struct cifs_server_key *key = buffer;
  71        uint16_t key_len = sizeof(struct cifs_server_key);
  72
  73        memset(key, 0, key_len);
  74
  75        /*
  76         * Should not be a problem as sin_family/sin6_family overlays
  77         * sa_family field
  78         */
  79        switch (sa->sa_family) {
  80        case AF_INET:
  81                key->family = sa->sa_family;
  82                key->port = addr->sin_port;
  83                key->addr[0].ipv4_addr = addr->sin_addr;
  84                key_len += sizeof(key->addr[0].ipv4_addr);
  85                break;
  86
  87        case AF_INET6:
  88                key->family = sa->sa_family;
  89                key->port = addr6->sin6_port;
  90                key->addr[0].ipv6_addr = addr6->sin6_addr;
  91                key_len += sizeof(key->addr[0].ipv6_addr);
  92                break;
  93
  94        default:
  95                cERROR(1, "CIFS: Unknown network family '%d'", sa->sa_family);
  96                key_len = 0;
  97                break;
  98        }
  99
 100        return key_len;
 101}
 102
 103/*
 104 * Server object for FS-Cache
 105 */
 106const struct fscache_cookie_def cifs_fscache_server_index_def = {
 107        .name = "CIFS.server",
 108        .type = FSCACHE_COOKIE_TYPE_INDEX,
 109        .get_key = cifs_server_get_key,
 110};
 111
 112/*
 113 * Auxiliary data attached to CIFS superblock within the cache
 114 */
 115struct cifs_fscache_super_auxdata {
 116        u64     resource_id;            /* unique server resource id */
 117};
 118
 119static char *extract_sharename(const char *treename)
 120{
 121        const char *src;
 122        char *delim, *dst;
 123        int len;
 124
 125        /* skip double chars at the beginning */
 126        src = treename + 2;
 127
 128        /* share name is always preceded by '\\' now */
 129        delim = strchr(src, '\\');
 130        if (!delim)
 131                return ERR_PTR(-EINVAL);
 132        delim++;
 133        len = strlen(delim);
 134
 135        /* caller has to free the memory */
 136        dst = kstrndup(delim, len, GFP_KERNEL);
 137        if (!dst)
 138                return ERR_PTR(-ENOMEM);
 139
 140        return dst;
 141}
 142
 143/*
 144 * Superblock object currently keyed by share name
 145 */
 146static uint16_t cifs_super_get_key(const void *cookie_netfs_data, void *buffer,
 147                                   uint16_t maxbuf)
 148{
 149        const struct cifsTconInfo *tcon = cookie_netfs_data;
 150        char *sharename;
 151        uint16_t len;
 152
 153        sharename = extract_sharename(tcon->treeName);
 154        if (IS_ERR(sharename)) {
 155                cFYI(1, "CIFS: couldn't extract sharename\n");
 156                sharename = NULL;
 157                return 0;
 158        }
 159
 160        len = strlen(sharename);
 161        if (len > maxbuf)
 162                return 0;
 163
 164        memcpy(buffer, sharename, len);
 165
 166        kfree(sharename);
 167
 168        return len;
 169}
 170
 171static uint16_t
 172cifs_fscache_super_get_aux(const void *cookie_netfs_data, void *buffer,
 173                           uint16_t maxbuf)
 174{
 175        struct cifs_fscache_super_auxdata auxdata;
 176        const struct cifsTconInfo *tcon = cookie_netfs_data;
 177
 178        memset(&auxdata, 0, sizeof(auxdata));
 179        auxdata.resource_id = tcon->resource_id;
 180
 181        if (maxbuf > sizeof(auxdata))
 182                maxbuf = sizeof(auxdata);
 183
 184        memcpy(buffer, &auxdata, maxbuf);
 185
 186        return maxbuf;
 187}
 188
 189static enum
 190fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
 191                                              const void *data,
 192                                              uint16_t datalen)
 193{
 194        struct cifs_fscache_super_auxdata auxdata;
 195        const struct cifsTconInfo *tcon = cookie_netfs_data;
 196
 197        if (datalen != sizeof(auxdata))
 198                return FSCACHE_CHECKAUX_OBSOLETE;
 199
 200        memset(&auxdata, 0, sizeof(auxdata));
 201        auxdata.resource_id = tcon->resource_id;
 202
 203        if (memcmp(data, &auxdata, datalen) != 0)
 204                return FSCACHE_CHECKAUX_OBSOLETE;
 205
 206        return FSCACHE_CHECKAUX_OKAY;
 207}
 208
 209/*
 210 * Superblock object for FS-Cache
 211 */
 212const struct fscache_cookie_def cifs_fscache_super_index_def = {
 213        .name = "CIFS.super",
 214        .type = FSCACHE_COOKIE_TYPE_INDEX,
 215        .get_key = cifs_super_get_key,
 216        .get_aux = cifs_fscache_super_get_aux,
 217        .check_aux = cifs_fscache_super_check_aux,
 218};
 219
 220/*
 221 * Auxiliary data attached to CIFS inode within the cache
 222 */
 223struct cifs_fscache_inode_auxdata {
 224        struct timespec last_write_time;
 225        struct timespec last_change_time;
 226        u64             eof;
 227};
 228
 229static uint16_t cifs_fscache_inode_get_key(const void *cookie_netfs_data,
 230                                           void *buffer, uint16_t maxbuf)
 231{
 232        const struct cifsInodeInfo *cifsi = cookie_netfs_data;
 233        uint16_t keylen;
 234
 235        /* use the UniqueId as the key */
 236        keylen = sizeof(cifsi->uniqueid);
 237        if (keylen > maxbuf)
 238                keylen = 0;
 239        else
 240                memcpy(buffer, &cifsi->uniqueid, keylen);
 241
 242        return keylen;
 243}
 244
 245static void
 246cifs_fscache_inode_get_attr(const void *cookie_netfs_data, uint64_t *size)
 247{
 248        const struct cifsInodeInfo *cifsi = cookie_netfs_data;
 249
 250        *size = cifsi->vfs_inode.i_size;
 251}
 252
 253static uint16_t
 254cifs_fscache_inode_get_aux(const void *cookie_netfs_data, void *buffer,
 255                           uint16_t maxbuf)
 256{
 257        struct cifs_fscache_inode_auxdata auxdata;
 258        const struct cifsInodeInfo *cifsi = cookie_netfs_data;
 259
 260        memset(&auxdata, 0, sizeof(auxdata));
 261        auxdata.eof = cifsi->server_eof;
 262        auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
 263        auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
 264
 265        if (maxbuf > sizeof(auxdata))
 266                maxbuf = sizeof(auxdata);
 267
 268        memcpy(buffer, &auxdata, maxbuf);
 269
 270        return maxbuf;
 271}
 272
 273static enum
 274fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,
 275                                              const void *data,
 276                                              uint16_t datalen)
 277{
 278        struct cifs_fscache_inode_auxdata auxdata;
 279        struct cifsInodeInfo *cifsi = cookie_netfs_data;
 280
 281        if (datalen != sizeof(auxdata))
 282                return FSCACHE_CHECKAUX_OBSOLETE;
 283
 284        memset(&auxdata, 0, sizeof(auxdata));
 285        auxdata.eof = cifsi->server_eof;
 286        auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
 287        auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
 288
 289        if (memcmp(data, &auxdata, datalen) != 0)
 290                return FSCACHE_CHECKAUX_OBSOLETE;
 291
 292        return FSCACHE_CHECKAUX_OKAY;
 293}
 294
 295static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data)
 296{
 297        struct cifsInodeInfo *cifsi = cookie_netfs_data;
 298        struct pagevec pvec;
 299        pgoff_t first;
 300        int loop, nr_pages;
 301
 302        pagevec_init(&pvec, 0);
 303        first = 0;
 304
 305        cFYI(1, "cifs inode 0x%p now uncached", cifsi);
 306
 307        for (;;) {
 308                nr_pages = pagevec_lookup(&pvec,
 309                                          cifsi->vfs_inode.i_mapping, first,
 310                                          PAGEVEC_SIZE - pagevec_count(&pvec));
 311                if (!nr_pages)
 312                        break;
 313
 314                for (loop = 0; loop < nr_pages; loop++)
 315                        ClearPageFsCache(pvec.pages[loop]);
 316
 317                first = pvec.pages[nr_pages - 1]->index + 1;
 318
 319                pvec.nr = nr_pages;
 320                pagevec_release(&pvec);
 321                cond_resched();
 322        }
 323}
 324
 325const struct fscache_cookie_def cifs_fscache_inode_object_def = {
 326        .name           = "CIFS.uniqueid",
 327        .type           = FSCACHE_COOKIE_TYPE_DATAFILE,
 328        .get_key        = cifs_fscache_inode_get_key,
 329        .get_attr       = cifs_fscache_inode_get_attr,
 330        .get_aux        = cifs_fscache_inode_get_aux,
 331        .check_aux      = cifs_fscache_inode_check_aux,
 332        .now_uncached   = cifs_fscache_inode_now_uncached,
 333};
 334