785 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			785 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  *  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, sector_t sec);
 | |
| static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, sector_t 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, sector_t sec);
 | |
| 
 | |
| static BUF_CACHE_T *buf_cache_find(struct super_block *sb, sector_t sec);
 | |
| static BUF_CACHE_T *buf_cache_get(struct super_block *sb, sector_t 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 _content;
 | |
| 	sector_t 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) {
 | |
| 		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;
 | |
| 	sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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:%llu).\n",
 | |
| 	     (unsigned long long)sec);
 | |
| 
 | |
| 	sm_V(&b_sem);
 | |
| } /* end of buf_modify */
 | |
| 
 | |
| void buf_lock(struct super_block *sb, sector_t 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:%llu).\n",
 | |
| 	     (unsigned long long)sec);
 | |
| 
 | |
| 	sm_V(&b_sem);
 | |
| } /* end of buf_lock */
 | |
| 
 | |
| void buf_unlock(struct super_block *sb, sector_t 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:%llu).\n",
 | |
| 	     (unsigned long long)sec);
 | |
| 
 | |
| 	sm_V(&b_sem);
 | |
| } /* end of buf_unlock */
 | |
| 
 | |
| void buf_release(struct super_block *sb, sector_t 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, sector_t 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, sector_t 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 */
 | 
