linux/fs/fat/cache.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  linux/fs/fat/cache.c
   4 *
   5 *  Written 1992,1993 by Werner Almesberger
   6 *
   7 *  Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
   8 *      of inode number.
   9 *  May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
  10 */
  11
  12#include <linux/slab.h>
  13#include "fat.h"
  14
  15/* this must be > 0. */
  16#define FAT_MAX_CACHE   8
  17
  18struct fat_cache {
  19        struct list_head cache_list;
  20        int nr_contig;  /* number of contiguous clusters */
  21        int fcluster;   /* cluster number in the file. */
  22        int dcluster;   /* cluster number on disk. */
  23};
  24
  25struct fat_cache_id {
  26        unsigned int id;
  27        int nr_contig;
  28        int fcluster;
  29        int dcluster;
  30};
  31
  32static inline int fat_max_cache(struct inode *inode)
  33{
  34        return FAT_MAX_CACHE;
  35}
  36
  37static struct kmem_cache *fat_cache_cachep;
  38
  39static void init_once(void *foo)
  40{
  41        struct fat_cache *cache = (struct fat_cache *)foo;
  42
  43        INIT_LIST_HEAD(&cache->cache_list);
  44}
  45
  46int __init fat_cache_init(void)
  47{
  48        fat_cache_cachep = kmem_cache_create("fat_cache",
  49                                sizeof(struct fat_cache),
  50                                0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
  51                                init_once);
  52        if (fat_cache_cachep == NULL)
  53                return -ENOMEM;
  54        return 0;
  55}
  56
  57void fat_cache_destroy(void)
  58{
  59        kmem_cache_destroy(fat_cache_cachep);
  60}
  61
  62static inline struct fat_cache *fat_cache_alloc(struct inode *inode)
  63{
  64        return kmem_cache_alloc(fat_cache_cachep, GFP_NOFS);
  65}
  66
  67static inline void fat_cache_free(struct fat_cache *cache)
  68{
  69        BUG_ON(!list_empty(&cache->cache_list));
  70        kmem_cache_free(fat_cache_cachep, cache);
  71}
  72
  73static inline void fat_cache_update_lru(struct inode *inode,
  74                                        struct fat_cache *cache)
  75{
  76        if (MSDOS_I(inode)->cache_lru.next != &cache->cache_list)
  77                list_move(&cache->cache_list, &MSDOS_I(inode)->cache_lru);
  78}
  79
  80static int fat_cache_lookup(struct inode *inode, int fclus,
  81                            struct fat_cache_id *cid,
  82                            int *cached_fclus, int *cached_dclus)
  83{
  84        static struct fat_cache nohit = { .fcluster = 0, };
  85
  86        struct fat_cache *hit = &nohit, *p;
  87        int offset = -1;
  88
  89        spin_lock(&MSDOS_I(inode)->cache_lru_lock);
  90        list_for_each_entry(p, &MSDOS_I(inode)->cache_lru, cache_list) {
  91                /* Find the cache of "fclus" or nearest cache. */
  92                if (p->fcluster <= fclus && hit->fcluster < p->fcluster) {
  93                        hit = p;
  94                        if ((hit->fcluster + hit->nr_contig) < fclus) {
  95                                offset = hit->nr_contig;
  96                        } else {
  97                                offset = fclus - hit->fcluster;
  98                                break;
  99                        }
 100                }
 101        }
 102        if (hit != &nohit) {
 103                fat_cache_update_lru(inode, hit);
 104
 105                cid->id = MSDOS_I(inode)->cache_valid_id;
 106                cid->nr_contig = hit->nr_contig;
 107                cid->fcluster = hit->fcluster;
 108                cid->dcluster = hit->dcluster;
 109                *cached_fclus = cid->fcluster + offset;
 110                *cached_dclus = cid->dcluster + offset;
 111        }
 112        spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
 113
 114        return offset;
 115}
 116
 117static struct fat_cache *fat_cache_merge(struct inode *inode,
 118                                         struct fat_cache_id *new)
 119{
 120        struct fat_cache *p;
 121
 122        list_for_each_entry(p, &MSDOS_I(inode)->cache_lru, cache_list) {
 123                /* Find the same part as "new" in cluster-chain. */
 124                if (p->fcluster == new->fcluster) {
 125                        BUG_ON(p->dcluster != new->dcluster);
 126                        if (new->nr_contig > p->nr_contig)
 127                                p->nr_contig = new->nr_contig;
 128                        return p;
 129                }
 130        }
 131        return NULL;
 132}
 133
 134static void fat_cache_add(struct inode *inode, struct fat_cache_id *new)
 135{
 136        struct fat_cache *cache, *tmp;
 137
 138        if (new->fcluster == -1) /* dummy cache */
 139                return;
 140
 141        spin_lock(&MSDOS_I(inode)->cache_lru_lock);
 142        if (new->id != FAT_CACHE_VALID &&
 143            new->id != MSDOS_I(inode)->cache_valid_id)
 144                goto out;       /* this cache was invalidated */
 145
 146        cache = fat_cache_merge(inode, new);
 147        if (cache == NULL) {
 148                if (MSDOS_I(inode)->nr_caches < fat_max_cache(inode)) {
 149                        MSDOS_I(inode)->nr_caches++;
 150                        spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
 151
 152                        tmp = fat_cache_alloc(inode);
 153                        if (!tmp) {
 154                                spin_lock(&MSDOS_I(inode)->cache_lru_lock);
 155                                MSDOS_I(inode)->nr_caches--;
 156                                spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
 157                                return;
 158                        }
 159
 160                        spin_lock(&MSDOS_I(inode)->cache_lru_lock);
 161                        cache = fat_cache_merge(inode, new);
 162                        if (cache != NULL) {
 163                                MSDOS_I(inode)->nr_caches--;
 164                                fat_cache_free(tmp);
 165                                goto out_update_lru;
 166                        }
 167                        cache = tmp;
 168                } else {
 169                        struct list_head *p = MSDOS_I(inode)->cache_lru.prev;
 170                        cache = list_entry(p, struct fat_cache, cache_list);
 171                }
 172                cache->fcluster = new->fcluster;
 173                cache->dcluster = new->dcluster;
 174                cache->nr_contig = new->nr_contig;
 175        }
 176out_update_lru:
 177        fat_cache_update_lru(inode, cache);
 178out:
 179        spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
 180}
 181
 182/*
 183 * Cache invalidation occurs rarely, thus the LRU chain is not updated. It
 184 * fixes itself after a while.
 185 */
 186static void __fat_cache_inval_inode(struct inode *inode)
 187{
 188        struct msdos_inode_info *i = MSDOS_I(inode);
 189        struct fat_cache *cache;
 190
 191        while (!list_empty(&i->cache_lru)) {
 192                cache = list_entry(i->cache_lru.next,
 193                                   struct fat_cache, cache_list);
 194                list_del_init(&cache->cache_list);
 195                i->nr_caches--;
 196                fat_cache_free(cache);
 197        }
 198        /* Update. The copy of caches before this id is discarded. */
 199        i->cache_valid_id++;
 200        if (i->cache_valid_id == FAT_CACHE_VALID)
 201                i->cache_valid_id++;
 202}
 203
 204void fat_cache_inval_inode(struct inode *inode)
 205{
 206        spin_lock(&MSDOS_I(inode)->cache_lru_lock);
 207        __fat_cache_inval_inode(inode);
 208        spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
 209}
 210
 211static inline int cache_contiguous(struct fat_cache_id *cid, int dclus)
 212{
 213        cid->nr_contig++;
 214        return ((cid->dcluster + cid->nr_contig) == dclus);
 215}
 216
 217static inline void cache_init(struct fat_cache_id *cid, int fclus, int dclus)
 218{
 219        cid->id = FAT_CACHE_VALID;
 220        cid->fcluster = fclus;
 221        cid->dcluster = dclus;
 222        cid->nr_contig = 0;
 223}
 224
 225int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
 226{
 227        struct super_block *sb = inode->i_sb;
 228        struct msdos_sb_info *sbi = MSDOS_SB(sb);
 229        const int limit = sb->s_maxbytes >> sbi->cluster_bits;
 230        struct fat_entry fatent;
 231        struct fat_cache_id cid;
 232        int nr;
 233
 234        BUG_ON(MSDOS_I(inode)->i_start == 0);
 235
 236        *fclus = 0;
 237        *dclus = MSDOS_I(inode)->i_start;
 238        if (!fat_valid_entry(sbi, *dclus)) {
 239                fat_fs_error_ratelimit(sb,
 240                        "%s: invalid start cluster (i_pos %lld, start %08x)",
 241                        __func__, MSDOS_I(inode)->i_pos, *dclus);
 242                return -EIO;
 243        }
 244        if (cluster == 0)
 245                return 0;
 246
 247        if (fat_cache_lookup(inode, cluster, &cid, fclus, dclus) < 0) {
 248                /*
 249                 * dummy, always not contiguous
 250                 * This is reinitialized by cache_init(), later.
 251                 */
 252                cache_init(&cid, -1, -1);
 253        }
 254
 255        fatent_init(&fatent);
 256        while (*fclus < cluster) {
 257                /* prevent the infinite loop of cluster chain */
 258                if (*fclus > limit) {
 259                        fat_fs_error_ratelimit(sb,
 260                                "%s: detected the cluster chain loop (i_pos %lld)",
 261                                __func__, MSDOS_I(inode)->i_pos);
 262                        nr = -EIO;
 263                        goto out;
 264                }
 265
 266                nr = fat_ent_read(inode, &fatent, *dclus);
 267                if (nr < 0)
 268                        goto out;
 269                else if (nr == FAT_ENT_FREE) {
 270                        fat_fs_error_ratelimit(sb,
 271                                "%s: invalid cluster chain (i_pos %lld)",
 272                                __func__, MSDOS_I(inode)->i_pos);
 273                        nr = -EIO;
 274                        goto out;
 275                } else if (nr == FAT_ENT_EOF) {
 276                        fat_cache_add(inode, &cid);
 277                        goto out;
 278                }
 279                (*fclus)++;
 280                *dclus = nr;
 281                if (!cache_contiguous(&cid, *dclus))
 282                        cache_init(&cid, *fclus, *dclus);
 283        }
 284        nr = 0;
 285        fat_cache_add(inode, &cid);
 286out:
 287        fatent_brelse(&fatent);
 288        return nr;
 289}
 290
 291static int fat_bmap_cluster(struct inode *inode, int cluster)
 292{
 293        struct super_block *sb = inode->i_sb;
 294        int ret, fclus, dclus;
 295
 296        if (MSDOS_I(inode)->i_start == 0)
 297                return 0;
 298
 299        ret = fat_get_cluster(inode, cluster, &fclus, &dclus);
 300        if (ret < 0)
 301                return ret;
 302        else if (ret == FAT_ENT_EOF) {
 303                fat_fs_error(sb, "%s: request beyond EOF (i_pos %lld)",
 304                             __func__, MSDOS_I(inode)->i_pos);
 305                return -EIO;
 306        }
 307        return dclus;
 308}
 309
 310int fat_get_mapped_cluster(struct inode *inode, sector_t sector,
 311                           sector_t last_block,
 312                           unsigned long *mapped_blocks, sector_t *bmap)
 313{
 314        struct super_block *sb = inode->i_sb;
 315        struct msdos_sb_info *sbi = MSDOS_SB(sb);
 316        int cluster, offset;
 317
 318        cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits);
 319        offset  = sector & (sbi->sec_per_clus - 1);
 320        cluster = fat_bmap_cluster(inode, cluster);
 321        if (cluster < 0)
 322                return cluster;
 323        else if (cluster) {
 324                *bmap = fat_clus_to_blknr(sbi, cluster) + offset;
 325                *mapped_blocks = sbi->sec_per_clus - offset;
 326                if (*mapped_blocks > last_block - sector)
 327                        *mapped_blocks = last_block - sector;
 328        }
 329
 330        return 0;
 331}
 332
 333static int is_exceed_eof(struct inode *inode, sector_t sector,
 334                         sector_t *last_block, int create)
 335{
 336        struct super_block *sb = inode->i_sb;
 337        const unsigned long blocksize = sb->s_blocksize;
 338        const unsigned char blocksize_bits = sb->s_blocksize_bits;
 339
 340        *last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits;
 341        if (sector >= *last_block) {
 342                if (!create)
 343                        return 1;
 344
 345                /*
 346                 * ->mmu_private can access on only allocation path.
 347                 * (caller must hold ->i_mutex)
 348                 */
 349                *last_block = (MSDOS_I(inode)->mmu_private + (blocksize - 1))
 350                        >> blocksize_bits;
 351                if (sector >= *last_block)
 352                        return 1;
 353        }
 354
 355        return 0;
 356}
 357
 358int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
 359             unsigned long *mapped_blocks, int create, bool from_bmap)
 360{
 361        struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
 362        sector_t last_block;
 363
 364        *phys = 0;
 365        *mapped_blocks = 0;
 366        if (!is_fat32(sbi) && (inode->i_ino == MSDOS_ROOT_INO)) {
 367                if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) {
 368                        *phys = sector + sbi->dir_start;
 369                        *mapped_blocks = 1;
 370                }
 371                return 0;
 372        }
 373
 374        if (!from_bmap) {
 375                if (is_exceed_eof(inode, sector, &last_block, create))
 376                        return 0;
 377        } else {
 378                last_block = inode->i_blocks >>
 379                                (inode->i_sb->s_blocksize_bits - 9);
 380                if (sector >= last_block)
 381                        return 0;
 382        }
 383
 384        return fat_get_mapped_cluster(inode, sector, last_block, mapped_blocks,
 385                                      phys);
 386}
 387
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.