/* * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "bcache.h" #include "assert.h" #include "stdlib.h" #include "linux/delay.h" #include "disk_pri.h" #include "user_copy.h" #undef HALARC_ALIGNMENT #define DMA_ALLGN 64 #define HALARC_ALIGNMENT DMA_ALLGN #define BCACHE_MAGIC_NUM 20132016 #define BCACHE_STATCK_SIZE 0x3000 #define ASYNC_EVENT_BIT 0x01 #ifdef DEBUG #define D(args) printf args #else #define D(args) #endif #ifdef BCACHE_ANALYSE UINT32 g_memSize; volatile UINT32 g_blockNum; volatile UINT32 g_dataSize; volatile UINT8 *g_memStart; volatile UINT32 g_switchTimes[CONFIG_FS_FAT_BLOCK_NUMS] = { 0 }; volatile UINT32 g_hitTimes[CONFIG_FS_FAT_BLOCK_NUMS] = { 0 }; #endif VOID BcacheAnalyse(UINT32 level) { (VOID)level; #ifdef BCACHE_ANALYSE int i; PRINTK("Bcache information:\n"); PRINTK(" mem: %u\n", g_memSize); PRINTK(" block number: %u\n", g_blockNum); PRINTK("index, switch, hit\n"); for (i = 0; i < g_blockNum; i++) { PRINTK("%5d, %6d, %3d\n", i, g_switchTimes[i], g_hitTimes[i]); } #else PRINTK("Bcache hasn't started\n"); #endif } #ifdef LOSCFG_FS_FAT_CACHE_SYNC_THREAD UINT32 g_syncThreadPrio = CONFIG_FS_FAT_SYNC_THREAD_PRIO; UINT32 g_dirtyRatio = CONFIG_FS_FAT_DIRTY_RATIO; UINT32 g_syncInterval = CONFIG_FS_FAT_SYNC_INTERVAL; VOID LOS_SetDirtyRatioThreshold(UINT32 dirtyRatio) { if ((dirtyRatio != g_dirtyRatio) && (dirtyRatio <= 100)) { /* The ratio cannot exceed 100% */ g_dirtyRatio = dirtyRatio; } } VOID LOS_SetSyncThreadInterval(UINT32 interval) { g_syncInterval = interval; } INT32 LOS_SetSyncThreadPrio(UINT32 prio, const CHAR *name) { INT32 ret = VFS_ERROR; INT32 diskID; los_disk *disk = NULL; if ((prio == 0) || (prio >= OS_TASK_PRIORITY_LOWEST)) { /* The priority can not be zero */ return ret; } g_syncThreadPrio = prio; /* * If the name is NULL, it only sets the value of a global variable, * and takes effect the next time the thread is created. */ if (name == NULL) { return ENOERR; } /* If the name is not NULL, it shall return an error if can't find the disk corresponding to name. */ diskID = los_get_diskid_byname(name); disk = get_disk(diskID); if (disk == NULL) { return ret; } if (pthread_mutex_lock(&disk->disk_mutex) != ENOERR) { PRINT_ERR("%s %d, mutex lock fail!\n", __FUNCTION__, __LINE__); return ret; } if ((disk->disk_status == STAT_INUSED) && (disk->bcache != NULL)) { ret = LOS_TaskPriSet(disk->bcache->syncTaskId, prio); } if (pthread_mutex_unlock(&disk->disk_mutex) != ENOERR) { PRINT_ERR("%s %d, mutex unlock fail!\n", __FUNCTION__, __LINE__); return VFS_ERROR; } return ret; } #endif static OsBcacheBlock *RbFindBlock(const OsBcache *bc, UINT64 num) { OsBcacheBlock *block = NULL; struct rb_node *node = bc->rbRoot.rb_node; for (; node != NULL; node = (block->num < num) ? node->rb_right : node->rb_left) { block = rb_entry(node, OsBcacheBlock, rbNode); if (block->num == num) { return block; } } return NULL; } static VOID RbAddBlock(OsBcache *bc, OsBcacheBlock *block) { struct rb_node *node = bc->rbRoot.rb_node; struct rb_node **link = NULL; OsBcacheBlock *b = NULL; if (node == NULL) { rb_link_node(&block->rbNode, NULL, &bc->rbRoot.rb_node); } else { for (; node != NULL; link = (b->num > block->num) ? &node->rb_left : &node->rb_right, node = *link) { b = rb_entry(node, OsBcacheBlock, rbNode); if (b->num == block->num) { PRINT_ERR("RbAddBlock fail, b->num = %llu, block->num = %llu\n", b->num, block->num); return; } } rb_link_node(&block->rbNode, &b->rbNode, link); } rb_insert_color(&block->rbNode, &bc->rbRoot); } static inline VOID RbDelBlock(OsBcache *bc, OsBcacheBlock *block) { rb_erase(&block->rbNode, &bc->rbRoot); } static inline VOID ListMoveBlockToHead(OsBcache *bc, OsBcacheBlock *block) { LOS_ListDelete(&block->listNode); LOS_ListAdd(&bc->listHead, &block->listNode); } static inline VOID FreeBlock(OsBcache *bc, OsBcacheBlock *block) { block->used = FALSE; LOS_ListAdd(&bc->freeListHead, &block->listNode); } static UINT32 GetValLog2(UINT32 val) { UINT32 i, log2; i = val; log2 = 0; while ((i & 1) == 0) { /* Check if the last bit is 1 */ i >>= 1; log2++; } if (i != 1) { /* Not the power of 2 */ return 0; } return log2; } static INT32 FindFlagPos(const UINT32 *arr, UINT32 len, UINT32 *p1, UINT32 *p2) { UINT32 *start = p1; UINT32 *end = p2; UINT32 i, j, tmp; UINT32 val = 1; *start = BCACHE_MAGIC_NUM; *end = 0; for (i = 0; i < len; i++) { for (j = 0; j < UNSIGNED_INTEGER_BITS; j++) { tmp = arr[i] << j; tmp = tmp >> UNINT_MAX_SHIFT_BITS; if (tmp != val) { continue; } if (val && (*start == BCACHE_MAGIC_NUM)) { *start = (i << UNINT_LOG2_SHIFT) + j; val = 1 - val; /* Control parity by 0 and 1 */ } else if (val && (*start != BCACHE_MAGIC_NUM)) { *start = 0; return VFS_ERROR; } else { *end = (i << UNINT_LOG2_SHIFT) + j; val = 1 - val; /* Control parity by 0 and 1 */ } } } if (*start == BCACHE_MAGIC_NUM) { *start = 0; return VFS_ERROR; } if (*end == 0) { *end = len << UNINT_LOG2_SHIFT; } return ENOERR; } static INT32 BlockRead(OsBcache *bc, OsBcacheBlock *block, UINT8 *buf) { INT32 ret = bc->breadFun(bc->priv, buf, bc->sectorPerBlock, (block->num) << GetValLog2(bc->sectorPerBlock)); if (ret) { PRINT_ERR("BlockRead, brread_fn error, ret = %d\n", ret); if (block->modified == FALSE) { if (block->listNode.pstNext != NULL) { LOS_ListDelete(&block->listNode); /* list del block */ RbDelBlock(bc, block); } FreeBlock(bc, block); } return ret; } block->readFlag = TRUE; return ENOERR; } static INT32 BcacheGetFlag(OsBcache *bc, OsBcacheBlock *block) { UINT32 i, n, f, sectorPos, val, start, pos, currentSize; UINT32 flagUse = bc->sectorPerBlock >> UNINT_LOG2_SHIFT; UINT32 flag = UINT_MAX; INT32 ret, bits; if (block->readFlag == TRUE) { return ENOERR; } for (i = 0; i < flagUse; i++) { flag &= block->flag[i]; } if (flag == UINT_MAX) { return ENOERR; } ret = BlockRead(bc, block, bc->rwBuffer); if (ret != ENOERR) { return ret; } for (i = 0, sectorPos = 0; i < flagUse; i++) { val = block->flag[i]; /* use unsigned integer for bit map */ for (f = 0, bits = UNSIGNED_INTEGER_BITS; bits > 0; val = ~(val << n), f++, bits = bits - (INT32)n) { if (val == 0) { n = UNSIGNED_INTEGER_BITS; } else { n = (UINT32)CLZ(val); } sectorPos += n; if (((f % EVEN_JUDGED) != 0) || (n == 0)) { /* Number of leading zeros of n is zero */ goto LOOP; } if (sectorPos > ((i + 1) << UNINT_LOG2_SHIFT)) { start = sectorPos - n; currentSize = (((i + 1) << UNINT_LOG2_SHIFT) - start) * bc->sectorSize; } else { start = sectorPos - n; currentSize = n * bc->sectorSize; } pos = start * bc->sectorSize; if (memcpy_s(block->data + pos, bc->blockSize - pos, bc->rwBuffer + pos, currentSize) != EOK) { return VFS_ERROR; } LOOP: if (sectorPos > ((i + 1) << UNINT_LOG2_SHIFT)) { sectorPos = (i + 1) << UNINT_LOG2_SHIFT; } } } return ENOERR; } static VOID BcacheSetFlag(const OsBcache *bc, OsBcacheBlock *block, UINT32 pos, UINT32 size) { UINT32 start, num, i, j, k; if (bc->sectorSize == 0) { PRINT_ERR("BcacheSetFlag sectorSize is equal to zero! \n"); return; } start = pos / bc->sectorSize; num = size / bc->sectorSize; i = start / UNSIGNED_INTEGER_BITS; j = start % UNSIGNED_INTEGER_BITS; for (k = 0; k < num; k++) { block->flag[i] |= 1u << (UNINT_MAX_SHIFT_BITS - j); j++; if (j == UNSIGNED_INTEGER_BITS) { j = 0; i++; } } } static INT32 BcacheSyncBlock(OsBcache *bc, OsBcacheBlock *block) { INT32 ret = ENOERR; UINT32 len, start, end; if (block->modified == TRUE) { D(("bcache writing block = %llu\n", block->num)); ret = FindFlagPos(block->flag, bc->sectorPerBlock >> UNINT_LOG2_SHIFT, &start, &end); if (ret == ENOERR) { len = end - start; } else { ret = BcacheGetFlag(bc, block); if (ret != ENOERR) { return ret; } len = bc->sectorPerBlock; } ret = bc->bwriteFun(bc->priv, (const UINT8 *)(block->data + (start * bc->sectorSize)), len, (block->num * bc->sectorPerBlock) + start); if (ret == ENOERR) { block->modified = FALSE; bc->modifiedBlock--; } else { PRINT_ERR("BcacheSyncBlock fail, ret = %d, len = %u, block->num = %llu, start = %u\n", ret, len, block->num, start); } } return ret; } static void NumListAdd(OsBcache *bc, OsBcacheBlock *block) { OsBcacheBlock *temp = NULL; LOS_DL_LIST_FOR_EACH_ENTRY(temp, &bc->numHead, OsBcacheBlock, numNode) { if (temp->num > block->num) { LOS_ListTailInsert(&temp->numNode, &block->numNode); return; } } LOS_ListTailInsert(&bc->numHead, &block->numNode); } static void AddBlock(OsBcache *bc, OsBcacheBlock *block) { RbAddBlock(bc, block); NumListAdd(bc, block); bc->sumNum += block->num; bc->nBlock++; LOS_ListAdd(&bc->listHead, &block->listNode); } static void DelBlock(OsBcache *bc, OsBcacheBlock *block) { LOS_ListDelete(&block->listNode); /* lru list del */ LOS_ListDelete(&block->numNode); /* num list del */ bc->sumNum -= block->num; bc->nBlock--; RbDelBlock(bc, block); /* rb tree del */ FreeBlock(bc, block); /* free list add */ } static BOOL BlockAllDirty(const OsBcache *bc, OsBcacheBlock *block) { UINT32 start = 0; UINT32 end = 0; UINT32 len = bc->sectorPerBlock >> UNINT_LOG2_SHIFT; if (block->modified == TRUE) { if (block->allDirty) { return TRUE; } if (FindFlagPos(block->flag, len, &start, &end) == ENOERR) { if ((end - start) == bc->sectorPerBlock) { block->allDirty = TRUE; return TRUE; } } } return FALSE; } static OsBcacheBlock *GetBaseBlock(OsBcache *bc) { OsBcacheBlock *base = bc->wStart; OsBcacheBlock *end = bc->wEnd; while (base < end) { if (base->used == FALSE) { base->used = TRUE; LOS_ListDelete(&base->listNode); return base; } base++; } return NULL; } /* try get free block first, if failed free a useless block */ static OsBcacheBlock *GetSlowBlock(OsBcache *bc, BOOL read) { LOS_DL_LIST *node = NULL; OsBcacheBlock *block = NULL; LOS_DL_LIST_FOR_EACH_ENTRY(block, &bc->freeListHead, OsBcacheBlock, listNode) { if (block->readBuff == read) { block->used = TRUE; LOS_ListDelete(&block->listNode); return block; /* get free one */ } } node = bc->listHead.pstPrev; while (node != &bc->listHead) { block = LOS_DL_LIST_ENTRY(node, OsBcacheBlock, listNode); node = block->listNode.pstPrev; if (block->readBuff == read) { if (block->modified == TRUE) { BcacheSyncBlock(bc, block); } DelBlock(bc, block); block->used = TRUE; LOS_ListDelete(&block->listNode); return block; /* get used one */ } } return NULL; } /* flush combined blocks */ static VOID WriteMergedBlocks(OsBcache *bc, OsBcacheBlock *begin, int blocks) { INT32 ret; OsBcacheBlock *cur = NULL; OsBcacheBlock *next = NULL; UINT32 len = blocks * bc->sectorPerBlock; UINT64 pos = begin->num * bc->sectorPerBlock; ret = bc->bwriteFun(bc->priv, (const UINT8 *)begin->data, len, pos); if (ret != ENOERR) { PRINT_ERR("WriteMergedBlocks bwriteFun failed ret %d\n", ret); return; } bc->modifiedBlock -= blocks; cur = begin; while (blocks > 0) { next = LOS_DL_LIST_ENTRY(cur->numNode.pstNext, OsBcacheBlock, numNode); DelBlock(bc, cur); cur->modified = FALSE; blocks--; cur = next; } } /* find continue blocks and flush them */ static VOID MergeSyncBlocks(OsBcache *bc, OsBcacheBlock *start) { INT32 mergedBlock = 0; OsBcacheBlock *cur = start; OsBcacheBlock *last = NULL; while (cur <= bc->wEnd) { if (!cur->used || !BlockAllDirty(bc, cur)) { break; } if (last && (last->num + 1 != cur->num)) { break; } mergedBlock++; last = cur; cur++; } if (mergedBlock > 0) { WriteMergedBlocks(bc, start, mergedBlock); } } /* get the min write block num of block cache buffer */ static inline UINT64 GetMinWriteNum(OsBcache *bc) { UINT64 ret = 0; OsBcacheBlock *block = NULL; LOS_DL_LIST_FOR_EACH_ENTRY(block, &bc->numHead, OsBcacheBlock, numNode) { if (!block->readBuff) { ret = block->num; break; } } return ret; } static OsBcacheBlock *AllocNewBlock(OsBcache *bc, BOOL read, UINT64 num) { OsBcacheBlock *last = NULL; OsBcacheBlock *prefer = NULL; if (read) { /* read */ return GetSlowBlock(bc, TRUE); } /* fallback, this may happen when the block previously flushed, use read buffer */ if (bc->nBlock && num < GetMinWriteNum(bc)) { return GetSlowBlock(bc, TRUE); } last = RbFindBlock(bc, num - 1); /* num=0 is ok */ if (last == NULL || last->readBuff) { return GetBaseBlock(bc); /* new block */ } prefer = last + 1; if (prefer > bc->wEnd) { prefer = bc->wStart; } /* this is a sync thread synced block! */ if (prefer->used && !prefer->modified) { prefer->used = FALSE; DelBlock(bc, prefer); } if (prefer->used) { MergeSyncBlocks(bc, prefer); BcacheSyncBlock(bc, prefer); DelBlock(bc, prefer); } prefer->used = TRUE; LOS_ListDelete(&prefer->listNode); /* del from free list */ return prefer; } static INT32 BcacheSync(OsBcache *bc) { LOS_DL_LIST *node = NULL; OsBcacheBlock *block = NULL; INT32 ret = ENOERR; D(("bcache cache sync\n")); (VOID)pthread_mutex_lock(&bc->bcacheMutex); node = bc->listHead.pstPrev; while (&bc->listHead != node) { block = LOS_DL_LIST_ENTRY(node, OsBcacheBlock, listNode); ret = BcacheSyncBlock(bc, block); if (ret != ENOERR) { PRINT_ERR("BcacheSync error, ret = %d\n", ret); break; } node = node->pstPrev; } (VOID)pthread_mutex_unlock(&bc->bcacheMutex); return ret; } static VOID BlockInit(OsBcache *bc, OsBcacheBlock *block, UINT64 num) { (VOID)memset_s(block->flag, sizeof(block->flag), 0, sizeof(block->flag)); block->num = num; block->readFlag = FALSE; if (block->modified == TRUE) { block->modified = FALSE; bc->modifiedBlock--; } block->allDirty = FALSE; } static INT32 BcacheGetBlock(OsBcache *bc, UINT64 num, BOOL readData, OsBcacheBlock **dblock) { INT32 ret; OsBcacheBlock *block = NULL; OsBcacheBlock *first = NULL; /* * First check if the most recently used block is the requested block, * this can improve performance when using byte access functions. */ if (LOS_ListEmpty(&bc->listHead) == FALSE) { first = LOS_DL_LIST_ENTRY(bc->listHead.pstNext, OsBcacheBlock, listNode); block = (first->num == num) ? first : RbFindBlock(bc, num); } if (block != NULL) { D(("bcache block = %llu found in cache\n", num)); #ifdef BCACHE_ANALYSE UINT32 index = ((UINT32)(block->data - g_memStart)) / g_dataSize; PRINTK(", [HIT], %llu, %u\n", num, index); g_hitTimes[index]++; #endif if (first != block) { ListMoveBlockToHead(bc, block); } *dblock = block; if ((bc->prereadFun != NULL) && (readData == TRUE) && (block->pgHit == 1)) { block->pgHit = 0; bc->prereadFun(bc, block); } return ENOERR; } D(("bcache block = %llu NOT found in cache\n", num)); block = AllocNewBlock(bc, readData, num); if (block == NULL) { block = GetSlowBlock(bc, readData); } if (block == NULL) { return -ENOMEM; } #ifdef BCACHE_ANALYSE UINT32 index = ((UINT32)(block->data - g_memStart)) / g_dataSize; PRINTK(", [MISS], %llu, %u\n", num, index); g_switchTimes[index]++; #endif BlockInit(bc, block, num); if (readData == TRUE) { D(("bcache reading block = %llu\n", block->num)); ret = BlockRead(bc, block, block->data); if (ret != ENOERR) { return ret; } if (bc->prereadFun != NULL) { bc->prereadFun(bc, block); } } AddBlock(bc, block); *dblock = block; return ENOERR; } INT32 BcacheClearCache(OsBcache *bc) { OsBcacheBlock *block = NULL; OsBcacheBlock *next = NULL; LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(block, next, &bc->listHead, OsBcacheBlock, listNode) { DelBlock(bc, block); } return 0; } static INT32 BcacheInitCache(OsBcache *bc, UINT8 *memStart, UINT32 memSize, UINT32 blockSize) { UINT8 *blockMem = NULL; UINT8 *dataMem = NULL; OsBcacheBlock *block = NULL; UINT32 blockNum, i; LOS_ListInit(&bc->listHead); LOS_ListInit(&bc->numHead); bc->sumNum = 0; bc->nBlock = 0; if (!GetValLog2(blockSize)) { PRINT_ERR("GetValLog2(%u) return 0.\n", blockSize); return -EINVAL; } bc->rbRoot.rb_node = NULL; bc->memStart = memStart; bc->blockSize = blockSize; bc->blockSizeLog2 = GetValLog2(blockSize); bc->modifiedBlock = 0; /* init block memory pool */ LOS_ListInit(&bc->freeListHead); blockNum = (memSize - DMA_ALLGN) / (sizeof(OsBcacheBlock) + bc->blockSize); blockMem = bc->memStart; dataMem = blockMem + (sizeof(OsBcacheBlock) * blockNum); dataMem += ALIGN_DISP((UINTPTR)dataMem); #ifdef BCACHE_ANALYSE g_memSize = memSize; g_blockNum = blockNum; g_dataSize = bc->blockSize; g_memStart = dataMem; #endif for (i = 0; i < blockNum; i++) { block = (OsBcacheBlock *)(VOID *)blockMem; block->data = dataMem; block->readBuff = (i < CONFIG_FS_FAT_READ_NUMS) ? TRUE : FALSE; if (i == CONFIG_FS_FAT_READ_NUMS) { bc->wStart = block; } LOS_ListTailInsert(&bc->freeListHead, &block->listNode); blockMem += sizeof(OsBcacheBlock); dataMem += bc->blockSize; } bc->wEnd = block; return ENOERR; } static INT32 DrvBread(struct Vnode *priv, UINT8 *buf, UINT32 len, UINT64 pos) { struct block_operations *bops = (struct block_operations *)((struct drv_data *)priv->data)->ops; INT32 ret = bops->read(priv, buf, pos, len); if (ret != (INT32)len) { PRINT_ERR("%s failure\n", __FUNCTION__); return ret; } return ENOERR; } static INT32 DrvBwrite(struct Vnode *priv, const UINT8 *buf, UINT32 len, UINT64 pos) { struct block_operations *bops = (struct block_operations *)((struct drv_data *)priv->data)->ops; INT32 ret = bops->write(priv, buf, pos, len); if (ret != (INT32)len) { PRINT_ERR("%s failure\n", __FUNCTION__); return ret; } return ENOERR; } INT32 BlockCacheDrvCreate(VOID *handle, UINT8 *memStart, UINT32 memSize, UINT32 blockSize, OsBcache *bc) { INT32 ret; bc->priv = handle; bc->breadFun = DrvBread; bc->bwriteFun = DrvBwrite; ret = BcacheInitCache(bc, memStart, memSize, blockSize); if (ret != ENOERR) { return ret; } if (pthread_mutex_init(&bc->bcacheMutex, NULL) != ENOERR) { return VFS_ERROR; } bc->bcacheMutex.attr.type = PTHREAD_MUTEX_RECURSIVE; return ENOERR; } INT32 BlockCacheRead(OsBcache *bc, UINT8 *buf, UINT32 *len, UINT64 sector, BOOL useRead) { OsBcacheBlock *block = NULL; UINT8 *tempBuf = buf; UINT32 size; UINT32 currentSize; INT32 ret = ENOERR; UINT64 pos; UINT64 num; #ifdef BCACHE_ANALYSE PRINTK("bcache read:\n"); #endif if (bc == NULL || buf == NULL || len == NULL) { return -EPERM; } size = *len; pos = sector * bc->sectorSize; num = pos >> bc->blockSizeLog2; pos = pos & (bc->blockSize - 1); while (size > 0) { if ((size + pos) > bc->blockSize) { currentSize = bc->blockSize - (UINT32)pos; } else { currentSize = size; } (VOID)pthread_mutex_lock(&bc->bcacheMutex); /* useRead should be FALSE when reading large contiguous data */ ret = BcacheGetBlock(bc, num, useRead, &block); if (ret != ENOERR) { (VOID)pthread_mutex_unlock(&bc->bcacheMutex); break; } if ((block->readFlag == FALSE) && (block->modified == TRUE)) { ret = BcacheGetFlag(bc, block); if (ret != ENOERR) { (VOID)pthread_mutex_unlock(&bc->bcacheMutex); return ret; } } else if ((block->readFlag == FALSE) && (block->modified == FALSE)) { ret = BlockRead(bc, block, block->data); if (ret != ENOERR) { (VOID)pthread_mutex_unlock(&bc->bcacheMutex); return ret; } } if (LOS_CopyFromKernel((VOID *)tempBuf, size, (VOID *)(block->data + pos), currentSize) != EOK) { (VOID)pthread_mutex_unlock(&bc->bcacheMutex); return VFS_ERROR; } (VOID)pthread_mutex_unlock(&bc->bcacheMutex); tempBuf += currentSize; size -= currentSize; pos = 0; num++; } *len -= size; return ret; } INT32 BlockCacheWrite(OsBcache *bc, const UINT8 *buf, UINT32 *len, UINT64 sector) { OsBcacheBlock *block = NULL; const UINT8 *tempBuf = buf; UINT32 size = *len; INT32 ret = ENOERR; UINT32 currentSize; UINT64 pos; UINT64 num; #ifdef BCACHE_ANALYSE PRINTK("bcache write:\n"); #endif pos = sector * bc->sectorSize; num = pos >> bc->blockSizeLog2; pos = pos & (bc->blockSize - 1); D(("bcache write len = %u pos = %llu bnum = %llu\n", *len, pos, num)); while (size > 0) { if ((size + pos) > bc->blockSize) { currentSize = bc->blockSize - (UINT32)pos; } else { currentSize = size; } (VOID)pthread_mutex_lock(&bc->bcacheMutex); ret = BcacheGetBlock(bc, num, FALSE, &block); if (ret != ENOERR) { (VOID)pthread_mutex_unlock(&bc->bcacheMutex); break; } if (LOS_CopyToKernel((VOID *)(block->data + pos), bc->blockSize - (UINT32)pos, (VOID *)tempBuf, currentSize) != EOK) { (VOID)pthread_mutex_unlock(&bc->bcacheMutex); return VFS_ERROR; } if (block->modified == FALSE) { block->modified = TRUE; bc->modifiedBlock++; } if ((pos == 0) && (currentSize == bc->blockSize)) { (void)memset_s(block->flag, sizeof(block->flag), 0xFF, sizeof(block->flag)); block->allDirty = TRUE; } else { BcacheSetFlag(bc, block, (UINT32)pos, currentSize); } (VOID)pthread_mutex_unlock(&bc->bcacheMutex); tempBuf += currentSize; size -= currentSize; pos = 0; num++; } *len -= size; return ret; } INT32 BlockCacheSync(OsBcache *bc) { return BcacheSync(bc); } INT32 OsSdSync(INT32 id) { #ifdef LOSCFG_FS_FAT_CACHE INT32 ret; los_disk *disk = get_disk(id); if ((disk == NULL) || (disk->disk_status == STAT_UNUSED)) { return VFS_ERROR; } if (pthread_mutex_lock(&disk->disk_mutex) != ENOERR) { PRINT_ERR("%s %d, mutex lock fail!\n", __FUNCTION__, __LINE__); return VFS_ERROR; } if ((disk->disk_status == STAT_INUSED) && (disk->bcache != NULL)) { ret = BcacheSync(disk->bcache); } else { ret = VFS_ERROR; } if (pthread_mutex_unlock(&disk->disk_mutex) != ENOERR) { PRINT_ERR("%s %d, mutex unlock fail!\n", __FUNCTION__, __LINE__); return VFS_ERROR; } return ret; #else return VFS_ERROR; #endif } INT32 LOS_BcacheSyncByName(const CHAR *name) { INT32 diskID = los_get_diskid_byname(name); return OsSdSync(diskID); } INT32 BcacheGetDirtyRatio(INT32 id) { #ifdef LOSCFG_FS_FAT_CACHE INT32 ret; los_disk *disk = get_disk(id); if (disk == NULL) { return VFS_ERROR; } if (pthread_mutex_lock(&disk->disk_mutex) != ENOERR) { PRINT_ERR("%s %d, mutex lock fail!\n", __FUNCTION__, __LINE__); return VFS_ERROR; } if ((disk->disk_status == STAT_INUSED) && (disk->bcache != NULL)) { ret = (INT32)((disk->bcache->modifiedBlock * PERCENTAGE) / GetFatBlockNums()); } else { ret = VFS_ERROR; } if (pthread_mutex_unlock(&disk->disk_mutex) != ENOERR) { PRINT_ERR("%s %d, mutex unlock fail!\n", __FUNCTION__, __LINE__); return VFS_ERROR; } return ret; #else return VFS_ERROR; #endif } INT32 LOS_GetDirtyRatioByName(const CHAR *name) { INT32 diskID = los_get_diskid_byname(name); return BcacheGetDirtyRatio(diskID); } #ifdef LOSCFG_FS_FAT_CACHE_SYNC_THREAD static VOID BcacheSyncThread(UINT32 id) { INT32 diskID = (INT32)id; INT32 dirtyRatio; while (1) { dirtyRatio = BcacheGetDirtyRatio(diskID); if (dirtyRatio > (INT32)g_dirtyRatio) { (VOID)OsSdSync(diskID); } msleep(g_syncInterval); } } VOID BcacheSyncThreadInit(OsBcache *bc, INT32 id) { UINT32 ret; TSK_INIT_PARAM_S appTask; (VOID)memset_s(&appTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); appTask.pfnTaskEntry = (TSK_ENTRY_FUNC)BcacheSyncThread; appTask.uwStackSize = BCACHE_STATCK_SIZE; appTask.pcName = "bcache_sync_task"; appTask.usTaskPrio = g_syncThreadPrio; appTask.auwArgs[0] = (UINTPTR)id; appTask.uwResved = LOS_TASK_STATUS_DETACHED; ret = LOS_TaskCreate(&bc->syncTaskId, &appTask); if (ret != ENOERR) { PRINT_ERR("Bcache sync task create failed in %s, %d\n", __FUNCTION__, __LINE__); } } VOID BcacheSyncThreadDeinit(const OsBcache *bc) { if (bc != NULL) { if (LOS_TaskDelete(bc->syncTaskId) != ENOERR) { PRINT_ERR("Bcache sync task delete failed in %s, %d\n", __FUNCTION__, __LINE__); } } } #endif OsBcache *BlockCacheInit(struct Vnode *devNode, UINT32 sectorSize, UINT32 sectorPerBlock, UINT32 blockNum, UINT64 blockCount) { OsBcache *bcache = NULL; struct Vnode *blkDriver = devNode; UINT8 *bcacheMem = NULL; UINT8 *rwBuffer = NULL; UINT32 blockSize, memSize; if ((blkDriver == NULL) || (sectorSize * sectorPerBlock * blockNum == 0) || (blockCount == 0)) { return NULL; } blockSize = sectorSize * sectorPerBlock; if ((((UINT64)(sizeof(OsBcacheBlock) + blockSize) * blockNum) + DMA_ALLGN) > UINT_MAX) { return NULL; } memSize = ((sizeof(OsBcacheBlock) + blockSize) * blockNum) + DMA_ALLGN; bcache = (OsBcache *)zalloc(sizeof(OsBcache)); if (bcache == NULL) { PRINT_ERR("bcache_init : malloc %u Bytes failed!\n", sizeof(OsBcache)); return NULL; } bcacheMem = (UINT8 *)zalloc(memSize); if (bcacheMem == NULL) { PRINT_ERR("bcache_init : malloc %u Bytes failed!\n", memSize); goto ERROR_OUT_WITH_BCACHE; } rwBuffer = (UINT8 *)memalign(DMA_ALLGN, blockSize); if (rwBuffer == NULL) { PRINT_ERR("bcache_init : malloc %u Bytes failed!\n", blockSize); goto ERROR_OUT_WITH_MEM; } bcache->rwBuffer = rwBuffer; bcache->sectorSize = sectorSize; bcache->sectorPerBlock = sectorPerBlock; bcache->blockCount = blockCount; if (BlockCacheDrvCreate(blkDriver, bcacheMem, memSize, blockSize, bcache) != ENOERR) { goto ERROR_OUT_WITH_BUFFER; } return bcache; ERROR_OUT_WITH_BUFFER: free(rwBuffer); ERROR_OUT_WITH_MEM: free(bcacheMem); ERROR_OUT_WITH_BCACHE: free(bcache); return NULL; } VOID BlockCacheDeinit(OsBcache *bcache) { if (bcache != NULL) { (VOID)pthread_mutex_destroy(&bcache->bcacheMutex); free(bcache->memStart); bcache->memStart = NULL; free(bcache->rwBuffer); bcache->rwBuffer = NULL; free(bcache); } } static VOID BcacheAsyncPrereadThread(VOID *arg) { OsBcache *bc = (OsBcache *)arg; OsBcacheBlock *block = NULL; INT32 ret; UINT32 i; for (;;) { ret = (INT32)LOS_EventRead(&bc->bcacheEvent, PREREAD_EVENT_MASK, LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER); if (ret != ASYNC_EVENT_BIT) { PRINT_ERR("The event read in %s, %d is error!!!\n", __FUNCTION__, __LINE__); continue; } for (i = 1; i <= PREREAD_BLOCK_NUM; i++) { if ((bc->curBlockNum + i) >= bc->blockCount) { break; } (VOID)pthread_mutex_lock(&bc->bcacheMutex); ret = BcacheGetBlock(bc, bc->curBlockNum + i, TRUE, &block); if (ret != ENOERR) { PRINT_ERR("read block %llu error : %d!\n", bc->curBlockNum, ret); } (VOID)pthread_mutex_unlock(&bc->bcacheMutex); } if (block != NULL) { block->pgHit = 1; /* preread complete */ } } } VOID ResumeAsyncPreread(OsBcache *arg1, const OsBcacheBlock *arg2) { UINT32 ret; OsBcache *bc = arg1; const OsBcacheBlock *block = arg2; if (OsCurrTaskGet()->taskID != bc->prereadTaskId) { bc->curBlockNum = block->num; ret = LOS_EventWrite(&bc->bcacheEvent, ASYNC_EVENT_BIT); if (ret != ENOERR) { PRINT_ERR("Write event failed in %s, %d\n", __FUNCTION__, __LINE__); } } } UINT32 BcacheAsyncPrereadInit(OsBcache *bc) { UINT32 ret; TSK_INIT_PARAM_S appTask; ret = LOS_EventInit(&bc->bcacheEvent); if (ret != ENOERR) { PRINT_ERR("Async event init failed in %s, %d\n", __FUNCTION__, __LINE__); return ret; } (VOID)memset_s(&appTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); appTask.pfnTaskEntry = (TSK_ENTRY_FUNC)BcacheAsyncPrereadThread; appTask.uwStackSize = BCACHE_STATCK_SIZE; appTask.pcName = "bcache_async_task"; appTask.usTaskPrio = BCACHE_PREREAD_PRIO; appTask.auwArgs[0] = (UINTPTR)bc; appTask.uwResved = LOS_TASK_STATUS_DETACHED; ret = LOS_TaskCreate(&bc->prereadTaskId, &appTask); if (ret != ENOERR) { PRINT_ERR("Bcache async task create failed in %s, %d\n", __FUNCTION__, __LINE__); } return ret; } UINT32 BcacheAsyncPrereadDeinit(OsBcache *bc) { UINT32 ret = LOS_NOK; if (bc != NULL) { ret = LOS_TaskDelete(bc->prereadTaskId); if (ret != ENOERR) { PRINT_ERR("Bcache async task delete failed in %s, %d\n", __FUNCTION__, __LINE__); } ret = LOS_EventDestroy(&bc->bcacheEvent); if (ret != ENOERR) { PRINT_ERR("Async event destroy failed in %s, %d\n", __FUNCTION__, __LINE__); return ret; } } return ret; }