| /* |
| * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| /************************************************************************/ |
| /* */ |
| /* PROJECT : exFAT & FAT12/16/32 File System */ |
| /* FILE : exfat_cache.c */ |
| /* PURPOSE : exFAT Cache Manager */ |
| /* (FAT Cache & Buffer Cache) */ |
| /* */ |
| /*----------------------------------------------------------------------*/ |
| /* NOTES */ |
| /* */ |
| /*----------------------------------------------------------------------*/ |
| /* REVISION HISTORY (Ver 0.9) */ |
| /* */ |
| /* - 2010.11.15 [Sung-Kwan Kim] : first writing */ |
| /* */ |
| /************************************************************************/ |
| |
| #include "exfat_config.h" |
| #include "exfat_data.h" |
| |
| #include "exfat_cache.h" |
| #include "exfat_super.h" |
| #include "exfat_core.h" |
| |
| /*----------------------------------------------------------------------*/ |
| /* Global Variable Definitions */ |
| /*----------------------------------------------------------------------*/ |
| |
| #define sm_P(s) |
| #define sm_V(s) |
| |
| static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content); |
| static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content); |
| |
| static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, u32 sec); |
| static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, u32 sec); |
| static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); |
| static void FAT_cache_remove_hash(BUF_CACHE_T *bp); |
| |
| static u8 *__buf_getblk(struct super_block *sb, u32 sec); |
| |
| static BUF_CACHE_T *buf_cache_find(struct super_block *sb, u32 sec); |
| static BUF_CACHE_T *buf_cache_get(struct super_block *sb, u32 sec); |
| static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); |
| static void buf_cache_remove_hash(BUF_CACHE_T *bp); |
| |
| static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| |
| /*======================================================================*/ |
| /* Cache Initialization Functions */ |
| /*======================================================================*/ |
| |
| s32 buf_init(struct super_block *sb) |
| { |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| int i; |
| |
| /* LRU list */ |
| p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list; |
| |
| for (i = 0; i < FAT_CACHE_SIZE; i++) { |
| p_fs->FAT_cache_array[i].drv = -1; |
| p_fs->FAT_cache_array[i].sec = ~0; |
| p_fs->FAT_cache_array[i].flag = 0; |
| p_fs->FAT_cache_array[i].buf_bh = NULL; |
| p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL; |
| push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list); |
| } |
| |
| p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list; |
| |
| for (i = 0; i < BUF_CACHE_SIZE; i++) { |
| p_fs->buf_cache_array[i].drv = -1; |
| p_fs->buf_cache_array[i].sec = ~0; |
| p_fs->buf_cache_array[i].flag = 0; |
| p_fs->buf_cache_array[i].buf_bh = NULL; |
| p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL; |
| push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list); |
| } |
| |
| /* HASH list */ |
| for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { |
| p_fs->FAT_cache_hash_list[i].drv = -1; |
| p_fs->FAT_cache_hash_list[i].sec = ~0; |
| p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]); |
| } |
| |
| for (i = 0; i < FAT_CACHE_SIZE; i++) |
| FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i])); |
| |
| for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { |
| p_fs->buf_cache_hash_list[i].drv = -1; |
| p_fs->buf_cache_hash_list[i].sec = ~0; |
| p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]); |
| } |
| |
| for (i = 0; i < BUF_CACHE_SIZE; i++) |
| buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i])); |
| |
| return FFS_SUCCESS; |
| } /* end of buf_init */ |
| |
| s32 buf_shutdown(struct super_block *sb) |
| { |
| return FFS_SUCCESS; |
| } /* end of buf_shutdown */ |
| |
| /*======================================================================*/ |
| /* FAT Read/Write Functions */ |
| /*======================================================================*/ |
| |
| /* in : sb, loc |
| * out: content |
| * returns 0 on success |
| * -1 on error |
| */ |
| s32 FAT_read(struct super_block *sb, u32 loc, u32 *content) |
| { |
| s32 ret; |
| |
| sm_P(&f_sem); |
| |
| ret = __FAT_read(sb, loc, content); |
| |
| sm_V(&f_sem); |
| |
| return ret; |
| } /* end of FAT_read */ |
| |
| s32 FAT_write(struct super_block *sb, u32 loc, u32 content) |
| { |
| s32 ret; |
| |
| sm_P(&f_sem); |
| |
| ret = __FAT_write(sb, loc, content); |
| |
| sm_V(&f_sem); |
| |
| return ret; |
| } /* end of FAT_write */ |
| |
| static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content) |
| { |
| s32 off; |
| u32 sec, _content; |
| u8 *fat_sector, *fat_entry; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| |
| if (p_fs->vol_type == FAT12) { |
| sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); |
| off = (loc + (loc >> 1)) & p_bd->sector_size_mask; |
| |
| if (off == (p_bd->sector_size-1)) { |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| _content = (u32) fat_sector[off]; |
| |
| fat_sector = FAT_getblk(sb, ++sec); |
| if (!fat_sector) |
| return -1; |
| |
| _content |= (u32) fat_sector[0] << 8; |
| } else { |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_entry = &(fat_sector[off]); |
| _content = GET16(fat_entry); |
| } |
| |
| if (loc & 1) |
| _content >>= 4; |
| |
| _content &= 0x00000FFF; |
| |
| if (_content >= CLUSTER_16(0x0FF8)) { |
| *content = CLUSTER_32(~0); |
| return 0; |
| } else { |
| *content = CLUSTER_32(_content); |
| return 0; |
| } |
| } else if (p_fs->vol_type == FAT16) { |
| sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); |
| off = (loc << 1) & p_bd->sector_size_mask; |
| |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_entry = &(fat_sector[off]); |
| |
| _content = GET16_A(fat_entry); |
| |
| _content &= 0x0000FFFF; |
| |
| if (_content >= CLUSTER_16(0xFFF8)) { |
| *content = CLUSTER_32(~0); |
| return 0; |
| } else { |
| *content = CLUSTER_32(_content); |
| return 0; |
| } |
| } else if (p_fs->vol_type == FAT32) { |
| sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| off = (loc << 2) & p_bd->sector_size_mask; |
| |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_entry = &(fat_sector[off]); |
| |
| _content = GET32_A(fat_entry); |
| |
| _content &= 0x0FFFFFFF; |
| |
| if (_content >= CLUSTER_32(0x0FFFFFF8)) { |
| *content = CLUSTER_32(~0); |
| return 0; |
| } else { |
| *content = CLUSTER_32(_content); |
| return 0; |
| } |
| } else { |
| sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| off = (loc << 2) & p_bd->sector_size_mask; |
| |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_entry = &(fat_sector[off]); |
| _content = GET32_A(fat_entry); |
| |
| if (_content >= CLUSTER_32(0xFFFFFFF8)) { |
| *content = CLUSTER_32(~0); |
| return 0; |
| } else { |
| *content = CLUSTER_32(_content); |
| return 0; |
| } |
| } |
| |
| *content = CLUSTER_32(~0); |
| return 0; |
| } /* end of __FAT_read */ |
| |
| static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content) |
| { |
| s32 off; |
| u32 sec; |
| u8 *fat_sector, *fat_entry; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| |
| if (p_fs->vol_type == FAT12) { |
| |
| content &= 0x00000FFF; |
| |
| sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); |
| off = (loc + (loc >> 1)) & p_bd->sector_size_mask; |
| |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| if (loc & 1) { /* odd */ |
| |
| content <<= 4; |
| |
| if (off == (p_bd->sector_size-1)) { |
| fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F)); |
| FAT_modify(sb, sec); |
| |
| fat_sector = FAT_getblk(sb, ++sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_sector[0] = (u8)(content >> 8); |
| } else { |
| fat_entry = &(fat_sector[off]); |
| content |= GET16(fat_entry) & 0x000F; |
| |
| SET16(fat_entry, content); |
| } |
| } else { /* even */ |
| fat_sector[off] = (u8)(content); |
| |
| if (off == (p_bd->sector_size-1)) { |
| fat_sector[off] = (u8)(content); |
| FAT_modify(sb, sec); |
| |
| fat_sector = FAT_getblk(sb, ++sec); |
| fat_sector[0] = (u8)((fat_sector[0] & 0xF0) | (content >> 8)); |
| } else { |
| fat_entry = &(fat_sector[off]); |
| content |= GET16(fat_entry) & 0xF000; |
| |
| SET16(fat_entry, content); |
| } |
| } |
| } |
| |
| else if (p_fs->vol_type == FAT16) { |
| |
| content &= 0x0000FFFF; |
| |
| sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); |
| off = (loc << 1) & p_bd->sector_size_mask; |
| |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_entry = &(fat_sector[off]); |
| |
| SET16_A(fat_entry, content); |
| } |
| |
| else if (p_fs->vol_type == FAT32) { |
| |
| content &= 0x0FFFFFFF; |
| |
| sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| off = (loc << 2) & p_bd->sector_size_mask; |
| |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_entry = &(fat_sector[off]); |
| |
| content |= GET32_A(fat_entry) & 0xF0000000; |
| |
| SET32_A(fat_entry, content); |
| } |
| |
| else { /* p_fs->vol_type == EXFAT */ |
| |
| sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| off = (loc << 2) & p_bd->sector_size_mask; |
| |
| fat_sector = FAT_getblk(sb, sec); |
| if (!fat_sector) |
| return -1; |
| |
| fat_entry = &(fat_sector[off]); |
| |
| SET32_A(fat_entry, content); |
| } |
| |
| FAT_modify(sb, sec); |
| return 0; |
| } /* end of __FAT_write */ |
| |
| u8 *FAT_getblk(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| bp = FAT_cache_find(sb, sec); |
| if (bp != NULL) { |
| move_to_mru(bp, &p_fs->FAT_cache_lru_list); |
| return bp->buf_bh->b_data; |
| } |
| |
| bp = FAT_cache_get(sb, sec); |
| |
| FAT_cache_remove_hash(bp); |
| |
| bp->drv = p_fs->drv; |
| bp->sec = sec; |
| bp->flag = 0; |
| |
| FAT_cache_insert_hash(sb, bp); |
| |
| if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { |
| FAT_cache_remove_hash(bp); |
| bp->drv = -1; |
| bp->sec = ~0; |
| bp->flag = 0; |
| bp->buf_bh = NULL; |
| |
| move_to_lru(bp, &p_fs->FAT_cache_lru_list); |
| return NULL; |
| } |
| |
| return bp->buf_bh->b_data; |
| } /* end of FAT_getblk */ |
| |
| void FAT_modify(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| |
| bp = FAT_cache_find(sb, sec); |
| if (bp != NULL) |
| sector_write(sb, sec, bp->buf_bh, 0); |
| } /* end of FAT_modify */ |
| |
| void FAT_release_all(struct super_block *sb) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| sm_P(&f_sem); |
| |
| bp = p_fs->FAT_cache_lru_list.next; |
| while (bp != &p_fs->FAT_cache_lru_list) { |
| if (bp->drv == p_fs->drv) { |
| bp->drv = -1; |
| bp->sec = ~0; |
| bp->flag = 0; |
| |
| if (bp->buf_bh) { |
| __brelse(bp->buf_bh); |
| bp->buf_bh = NULL; |
| } |
| } |
| bp = bp->next; |
| } |
| |
| sm_V(&f_sem); |
| } /* end of FAT_release_all */ |
| |
| void FAT_sync(struct super_block *sb) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| sm_P(&f_sem); |
| |
| bp = p_fs->FAT_cache_lru_list.next; |
| while (bp != &p_fs->FAT_cache_lru_list) { |
| if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { |
| sync_dirty_buffer(bp->buf_bh); |
| bp->flag &= ~(DIRTYBIT); |
| } |
| bp = bp->next; |
| } |
| |
| sm_V(&f_sem); |
| } /* end of FAT_sync */ |
| |
| static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, u32 sec) |
| { |
| s32 off; |
| BUF_CACHE_T *bp, *hp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1); |
| |
| hp = &(p_fs->FAT_cache_hash_list[off]); |
| for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { |
| if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { |
| |
| WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. " |
| "It will make system panic.\n"); |
| |
| touch_buffer(bp->buf_bh); |
| return bp; |
| } |
| } |
| return NULL; |
| } /* end of FAT_cache_find */ |
| |
| static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| bp = p_fs->FAT_cache_lru_list.prev; |
| |
| |
| move_to_mru(bp, &p_fs->FAT_cache_lru_list); |
| return bp; |
| } /* end of FAT_cache_get */ |
| |
| static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) |
| { |
| s32 off; |
| BUF_CACHE_T *hp; |
| FS_INFO_T *p_fs; |
| |
| p_fs = &(EXFAT_SB(sb)->fs_info); |
| off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1); |
| |
| hp = &(p_fs->FAT_cache_hash_list[off]); |
| bp->hash_next = hp->hash_next; |
| bp->hash_prev = hp; |
| hp->hash_next->hash_prev = bp; |
| hp->hash_next = bp; |
| } /* end of FAT_cache_insert_hash */ |
| |
| static void FAT_cache_remove_hash(BUF_CACHE_T *bp) |
| { |
| (bp->hash_prev)->hash_next = bp->hash_next; |
| (bp->hash_next)->hash_prev = bp->hash_prev; |
| } /* end of FAT_cache_remove_hash */ |
| |
| /*======================================================================*/ |
| /* Buffer Read/Write Functions */ |
| /*======================================================================*/ |
| |
| u8 *buf_getblk(struct super_block *sb, u32 sec) |
| { |
| u8 *buf; |
| |
| sm_P(&b_sem); |
| |
| buf = __buf_getblk(sb, sec); |
| |
| sm_V(&b_sem); |
| |
| return buf; |
| } /* end of buf_getblk */ |
| |
| static u8 *__buf_getblk(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| bp = buf_cache_find(sb, sec); |
| if (bp != NULL) { |
| move_to_mru(bp, &p_fs->buf_cache_lru_list); |
| return bp->buf_bh->b_data; |
| } |
| |
| bp = buf_cache_get(sb, sec); |
| |
| buf_cache_remove_hash(bp); |
| |
| bp->drv = p_fs->drv; |
| bp->sec = sec; |
| bp->flag = 0; |
| |
| buf_cache_insert_hash(sb, bp); |
| |
| if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { |
| buf_cache_remove_hash(bp); |
| bp->drv = -1; |
| bp->sec = ~0; |
| bp->flag = 0; |
| bp->buf_bh = NULL; |
| |
| move_to_lru(bp, &p_fs->buf_cache_lru_list); |
| return NULL; |
| } |
| |
| return bp->buf_bh->b_data; |
| |
| } /* end of __buf_getblk */ |
| |
| void buf_modify(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| |
| sm_P(&b_sem); |
| |
| bp = buf_cache_find(sb, sec); |
| if (likely(bp != NULL)) |
| sector_write(sb, sec, bp->buf_bh, 0); |
| |
| WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); |
| |
| sm_V(&b_sem); |
| } /* end of buf_modify */ |
| |
| void buf_lock(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| |
| sm_P(&b_sem); |
| |
| bp = buf_cache_find(sb, sec); |
| if (likely(bp != NULL)) |
| bp->flag |= LOCKBIT; |
| |
| WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); |
| |
| sm_V(&b_sem); |
| } /* end of buf_lock */ |
| |
| void buf_unlock(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| |
| sm_P(&b_sem); |
| |
| bp = buf_cache_find(sb, sec); |
| if (likely(bp != NULL)) |
| bp->flag &= ~(LOCKBIT); |
| |
| WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); |
| |
| sm_V(&b_sem); |
| } /* end of buf_unlock */ |
| |
| void buf_release(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| sm_P(&b_sem); |
| |
| bp = buf_cache_find(sb, sec); |
| if (likely(bp != NULL)) { |
| bp->drv = -1; |
| bp->sec = ~0; |
| bp->flag = 0; |
| |
| if (bp->buf_bh) { |
| __brelse(bp->buf_bh); |
| bp->buf_bh = NULL; |
| } |
| |
| move_to_lru(bp, &p_fs->buf_cache_lru_list); |
| } |
| |
| sm_V(&b_sem); |
| } /* end of buf_release */ |
| |
| void buf_release_all(struct super_block *sb) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| sm_P(&b_sem); |
| |
| bp = p_fs->buf_cache_lru_list.next; |
| while (bp != &p_fs->buf_cache_lru_list) { |
| if (bp->drv == p_fs->drv) { |
| bp->drv = -1; |
| bp->sec = ~0; |
| bp->flag = 0; |
| |
| if (bp->buf_bh) { |
| __brelse(bp->buf_bh); |
| bp->buf_bh = NULL; |
| } |
| } |
| bp = bp->next; |
| } |
| |
| sm_V(&b_sem); |
| } /* end of buf_release_all */ |
| |
| void buf_sync(struct super_block *sb) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| sm_P(&b_sem); |
| |
| bp = p_fs->buf_cache_lru_list.next; |
| while (bp != &p_fs->buf_cache_lru_list) { |
| if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { |
| sync_dirty_buffer(bp->buf_bh); |
| bp->flag &= ~(DIRTYBIT); |
| } |
| bp = bp->next; |
| } |
| |
| sm_V(&b_sem); |
| } /* end of buf_sync */ |
| |
| static BUF_CACHE_T *buf_cache_find(struct super_block *sb, u32 sec) |
| { |
| s32 off; |
| BUF_CACHE_T *bp, *hp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1); |
| |
| hp = &(p_fs->buf_cache_hash_list[off]); |
| for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { |
| if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { |
| touch_buffer(bp->buf_bh); |
| return bp; |
| } |
| } |
| return NULL; |
| } /* end of buf_cache_find */ |
| |
| static BUF_CACHE_T *buf_cache_get(struct super_block *sb, u32 sec) |
| { |
| BUF_CACHE_T *bp; |
| FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| |
| bp = p_fs->buf_cache_lru_list.prev; |
| while (bp->flag & LOCKBIT) |
| bp = bp->prev; |
| |
| |
| move_to_mru(bp, &p_fs->buf_cache_lru_list); |
| return bp; |
| } /* end of buf_cache_get */ |
| |
| static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) |
| { |
| s32 off; |
| BUF_CACHE_T *hp; |
| FS_INFO_T *p_fs; |
| |
| p_fs = &(EXFAT_SB(sb)->fs_info); |
| off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1); |
| |
| hp = &(p_fs->buf_cache_hash_list[off]); |
| bp->hash_next = hp->hash_next; |
| bp->hash_prev = hp; |
| hp->hash_next->hash_prev = bp; |
| hp->hash_next = bp; |
| } /* end of buf_cache_insert_hash */ |
| |
| static void buf_cache_remove_hash(BUF_CACHE_T *bp) |
| { |
| (bp->hash_prev)->hash_next = bp->hash_next; |
| (bp->hash_next)->hash_prev = bp->hash_prev; |
| } /* end of buf_cache_remove_hash */ |
| |
| /*======================================================================*/ |
| /* Local Function Definitions */ |
| /*======================================================================*/ |
| |
| static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| { |
| bp->next = list->next; |
| bp->prev = list; |
| list->next->prev = bp; |
| list->next = bp; |
| } /* end of buf_cache_push_to_mru */ |
| |
| static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| { |
| bp->prev = list->prev; |
| bp->next = list; |
| list->prev->next = bp; |
| list->prev = bp; |
| } /* end of buf_cache_push_to_lru */ |
| |
| static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| { |
| bp->prev->next = bp->next; |
| bp->next->prev = bp->prev; |
| push_to_mru(bp, list); |
| } /* end of buf_cache_move_to_mru */ |
| |
| static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| { |
| bp->prev->next = bp->next; |
| bp->next->prev = bp->prev; |
| push_to_lru(bp, list); |
| } /* end of buf_cache_move_to_lru */ |