at v5.8 7.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 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 * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. 11 */ 12 13#include <linux/slab.h> 14#include <asm/unaligned.h> 15#include <linux/buffer_head.h> 16 17#include "exfat_raw.h" 18#include "exfat_fs.h" 19 20#define EXFAT_CACHE_VALID 0 21#define EXFAT_MAX_CACHE 16 22 23struct exfat_cache { 24 struct list_head cache_list; 25 unsigned int nr_contig; /* number of contiguous clusters */ 26 unsigned int fcluster; /* cluster number in the file. */ 27 unsigned int dcluster; /* cluster number on disk. */ 28}; 29 30struct exfat_cache_id { 31 unsigned int id; 32 unsigned int nr_contig; 33 unsigned int fcluster; 34 unsigned int dcluster; 35}; 36 37static struct kmem_cache *exfat_cachep; 38 39static void exfat_cache_init_once(void *c) 40{ 41 struct exfat_cache *cache = (struct exfat_cache *)c; 42 43 INIT_LIST_HEAD(&cache->cache_list); 44} 45 46int exfat_cache_init(void) 47{ 48 exfat_cachep = kmem_cache_create("exfat_cache", 49 sizeof(struct exfat_cache), 50 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, 51 exfat_cache_init_once); 52 if (!exfat_cachep) 53 return -ENOMEM; 54 return 0; 55} 56 57void exfat_cache_shutdown(void) 58{ 59 if (!exfat_cachep) 60 return; 61 kmem_cache_destroy(exfat_cachep); 62} 63 64void exfat_cache_init_inode(struct inode *inode) 65{ 66 struct exfat_inode_info *ei = EXFAT_I(inode); 67 68 spin_lock_init(&ei->cache_lru_lock); 69 ei->nr_caches = 0; 70 ei->cache_valid_id = EXFAT_CACHE_VALID + 1; 71 INIT_LIST_HEAD(&ei->cache_lru); 72} 73 74static inline struct exfat_cache *exfat_cache_alloc(void) 75{ 76 return kmem_cache_alloc(exfat_cachep, GFP_NOFS); 77} 78 79static inline void exfat_cache_free(struct exfat_cache *cache) 80{ 81 WARN_ON(!list_empty(&cache->cache_list)); 82 kmem_cache_free(exfat_cachep, cache); 83} 84 85static inline void exfat_cache_update_lru(struct inode *inode, 86 struct exfat_cache *cache) 87{ 88 struct exfat_inode_info *ei = EXFAT_I(inode); 89 90 if (ei->cache_lru.next != &cache->cache_list) 91 list_move(&cache->cache_list, &ei->cache_lru); 92} 93 94static unsigned int exfat_cache_lookup(struct inode *inode, 95 unsigned int fclus, struct exfat_cache_id *cid, 96 unsigned int *cached_fclus, unsigned int *cached_dclus) 97{ 98 struct exfat_inode_info *ei = EXFAT_I(inode); 99 static struct exfat_cache nohit = { .fcluster = 0, }; 100 struct exfat_cache *hit = &nohit, *p; 101 unsigned int offset = EXFAT_EOF_CLUSTER; 102 103 spin_lock(&ei->cache_lru_lock); 104 list_for_each_entry(p, &ei->cache_lru, cache_list) { 105 /* Find the cache of "fclus" or nearest cache. */ 106 if (p->fcluster <= fclus && hit->fcluster < p->fcluster) { 107 hit = p; 108 if (hit->fcluster + hit->nr_contig < fclus) { 109 offset = hit->nr_contig; 110 } else { 111 offset = fclus - hit->fcluster; 112 break; 113 } 114 } 115 } 116 if (hit != &nohit) { 117 exfat_cache_update_lru(inode, hit); 118 119 cid->id = ei->cache_valid_id; 120 cid->nr_contig = hit->nr_contig; 121 cid->fcluster = hit->fcluster; 122 cid->dcluster = hit->dcluster; 123 *cached_fclus = cid->fcluster + offset; 124 *cached_dclus = cid->dcluster + offset; 125 } 126 spin_unlock(&ei->cache_lru_lock); 127 128 return offset; 129} 130 131static struct exfat_cache *exfat_cache_merge(struct inode *inode, 132 struct exfat_cache_id *new) 133{ 134 struct exfat_inode_info *ei = EXFAT_I(inode); 135 struct exfat_cache *p; 136 137 list_for_each_entry(p, &ei->cache_lru, cache_list) { 138 /* Find the same part as "new" in cluster-chain. */ 139 if (p->fcluster == new->fcluster) { 140 if (new->nr_contig > p->nr_contig) 141 p->nr_contig = new->nr_contig; 142 return p; 143 } 144 } 145 return NULL; 146} 147 148static void exfat_cache_add(struct inode *inode, 149 struct exfat_cache_id *new) 150{ 151 struct exfat_inode_info *ei = EXFAT_I(inode); 152 struct exfat_cache *cache, *tmp; 153 154 if (new->fcluster == EXFAT_EOF_CLUSTER) /* dummy cache */ 155 return; 156 157 spin_lock(&ei->cache_lru_lock); 158 if (new->id != EXFAT_CACHE_VALID && 159 new->id != ei->cache_valid_id) 160 goto unlock; /* this cache was invalidated */ 161 162 cache = exfat_cache_merge(inode, new); 163 if (cache == NULL) { 164 if (ei->nr_caches < EXFAT_MAX_CACHE) { 165 ei->nr_caches++; 166 spin_unlock(&ei->cache_lru_lock); 167 168 tmp = exfat_cache_alloc(); 169 if (!tmp) { 170 spin_lock(&ei->cache_lru_lock); 171 ei->nr_caches--; 172 spin_unlock(&ei->cache_lru_lock); 173 return; 174 } 175 176 spin_lock(&ei->cache_lru_lock); 177 cache = exfat_cache_merge(inode, new); 178 if (cache != NULL) { 179 ei->nr_caches--; 180 exfat_cache_free(tmp); 181 goto out_update_lru; 182 } 183 cache = tmp; 184 } else { 185 struct list_head *p = ei->cache_lru.prev; 186 187 cache = list_entry(p, 188 struct exfat_cache, cache_list); 189 } 190 cache->fcluster = new->fcluster; 191 cache->dcluster = new->dcluster; 192 cache->nr_contig = new->nr_contig; 193 } 194out_update_lru: 195 exfat_cache_update_lru(inode, cache); 196unlock: 197 spin_unlock(&ei->cache_lru_lock); 198} 199 200/* 201 * Cache invalidation occurs rarely, thus the LRU chain is not updated. It 202 * fixes itself after a while. 203 */ 204static void __exfat_cache_inval_inode(struct inode *inode) 205{ 206 struct exfat_inode_info *ei = EXFAT_I(inode); 207 struct exfat_cache *cache; 208 209 while (!list_empty(&ei->cache_lru)) { 210 cache = list_entry(ei->cache_lru.next, 211 struct exfat_cache, cache_list); 212 list_del_init(&cache->cache_list); 213 ei->nr_caches--; 214 exfat_cache_free(cache); 215 } 216 /* Update. The copy of caches before this id is discarded. */ 217 ei->cache_valid_id++; 218 if (ei->cache_valid_id == EXFAT_CACHE_VALID) 219 ei->cache_valid_id++; 220} 221 222void exfat_cache_inval_inode(struct inode *inode) 223{ 224 struct exfat_inode_info *ei = EXFAT_I(inode); 225 226 spin_lock(&ei->cache_lru_lock); 227 __exfat_cache_inval_inode(inode); 228 spin_unlock(&ei->cache_lru_lock); 229} 230 231static inline int cache_contiguous(struct exfat_cache_id *cid, 232 unsigned int dclus) 233{ 234 cid->nr_contig++; 235 return cid->dcluster + cid->nr_contig == dclus; 236} 237 238static inline void cache_init(struct exfat_cache_id *cid, 239 unsigned int fclus, unsigned int dclus) 240{ 241 cid->id = EXFAT_CACHE_VALID; 242 cid->fcluster = fclus; 243 cid->dcluster = dclus; 244 cid->nr_contig = 0; 245} 246 247int exfat_get_cluster(struct inode *inode, unsigned int cluster, 248 unsigned int *fclus, unsigned int *dclus, 249 unsigned int *last_dclus, int allow_eof) 250{ 251 struct super_block *sb = inode->i_sb; 252 struct exfat_sb_info *sbi = EXFAT_SB(sb); 253 unsigned int limit = sbi->num_clusters; 254 struct exfat_inode_info *ei = EXFAT_I(inode); 255 struct exfat_cache_id cid; 256 unsigned int content; 257 258 if (ei->start_clu == EXFAT_FREE_CLUSTER) { 259 exfat_fs_error(sb, 260 "invalid access to exfat cache (entry 0x%08x)", 261 ei->start_clu); 262 return -EIO; 263 } 264 265 *fclus = 0; 266 *dclus = ei->start_clu; 267 *last_dclus = *dclus; 268 269 /* 270 * Don`t use exfat_cache if zero offset or non-cluster allocation 271 */ 272 if (cluster == 0 || *dclus == EXFAT_EOF_CLUSTER) 273 return 0; 274 275 cache_init(&cid, EXFAT_EOF_CLUSTER, EXFAT_EOF_CLUSTER); 276 277 if (exfat_cache_lookup(inode, cluster, &cid, fclus, dclus) == 278 EXFAT_EOF_CLUSTER) { 279 /* 280 * dummy, always not contiguous 281 * This is reinitialized by cache_init(), later. 282 */ 283 WARN_ON(cid.id != EXFAT_CACHE_VALID || 284 cid.fcluster != EXFAT_EOF_CLUSTER || 285 cid.dcluster != EXFAT_EOF_CLUSTER || 286 cid.nr_contig != 0); 287 } 288 289 if (*fclus == cluster) 290 return 0; 291 292 while (*fclus < cluster) { 293 /* prevent the infinite loop of cluster chain */ 294 if (*fclus > limit) { 295 exfat_fs_error(sb, 296 "detected the cluster chain loop (i_pos %u)", 297 (*fclus)); 298 return -EIO; 299 } 300 301 if (exfat_ent_get(sb, *dclus, &content)) 302 return -EIO; 303 304 *last_dclus = *dclus; 305 *dclus = content; 306 (*fclus)++; 307 308 if (content == EXFAT_EOF_CLUSTER) { 309 if (!allow_eof) { 310 exfat_fs_error(sb, 311 "invalid cluster chain (i_pos %u, last_clus 0x%08x is EOF)", 312 *fclus, (*last_dclus)); 313 return -EIO; 314 } 315 316 break; 317 } 318 319 if (!cache_contiguous(&cid, *dclus)) 320 cache_init(&cid, *fclus, *dclus); 321 } 322 323 exfat_cache_add(inode, &cid); 324 return 0; 325}