⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bufcache.c

📁 GEEKOS是一个免费的操作系统内核
💻 C
字号:
/* * Filesystem buffer cache * Copyright (c) 2004, David H. Hovemeyer <daveho@cs.umd.edu> * $Revision: 1.13 $ * * This is free software.  You are permitted to use, * redistribute, and modify it as specified in the file "COPYING". */#include <geekos/errno.h>#include <geekos/kassert.h>#include <geekos/mem.h>#include <geekos/malloc.h>#include <geekos/blockdev.h>#include <geekos/bufcache.h>/* * Maximum number of buffers that are cached per-filesystem. */#define FS_BUFFER_CACHE_MAX_BLOCKS 128/* ---------------------------------------------------------------------- * Private functions * ---------------------------------------------------------------------- */int bufCacheDebug = 0;#define Debug(args...) if (bufCacheDebug) Print(args)/* XXX */int noEvict = 0;/* * Get number of sectors per filesystem block for given * fs buffer cache. */static uint_t Get_Num_Sectors_Per_FS_Block(struct FS_Buffer_Cache *cache){    return (cache->fsBlockSize / SECTOR_SIZE);}/* * Read or write a filesystem buffer. */static int Do_Buffer_IO(struct FS_Buffer_Cache *cache, struct FS_Buffer *buf,    int (*IO_Func)(struct Block_Device *dev, int blockNum, void *buf)){    uint_t offset;    int sectorCount = 0;    int blockNum = buf->fsBlockNum * Get_Num_Sectors_Per_FS_Block(cache);    char *ptr = (char*) buf->data;    for (offset = 0; offset < cache->fsBlockSize; offset += SECTOR_SIZE) {	int rc = IO_Func(cache->dev, blockNum, ptr + offset);	if (rc != 0)	    return rc;	++sectorCount;	++blockNum;    }    KASSERT(offset == cache->fsBlockSize);    KASSERT(sectorCount == Get_Num_Sectors_Per_FS_Block(cache));    return 0;}/* * If necessary, write back uncomitted buffer contents to block device. */static int Sync_Buffer(struct FS_Buffer_Cache *cache, struct FS_Buffer *buf){    int rc = 0;    KASSERT(IS_HELD(&cache->lock));    if (buf->flags & FS_BUFFER_DIRTY) {	if ((rc = Do_Buffer_IO(cache, buf, Block_Write)) == 0)	    buf->flags &= ~(FS_BUFFER_DIRTY);    }    return rc;}/* * Move a buffer to the front of the cache buffer list, * to indicate that it has been used recently. */static void Move_To_Front(struct FS_Buffer_Cache *cache, struct FS_Buffer *buf){    Remove_From_FS_Buffer_List(&cache->bufferList, buf);    Add_To_Front_Of_FS_Buffer_List(&cache->bufferList, buf);}/* * Get buffer for given block, and mark it in use. * Must be called with cache mutex held. */static int Get_Buffer(struct FS_Buffer_Cache *cache, ulong_t fsBlockNum, struct FS_Buffer **pBuf){    struct FS_Buffer *buf, *lru = 0;    int rc;    Debug("Request block %lu\n", fsBlockNum);    KASSERT(IS_HELD(&cache->lock));    /*     * Look for existing buffer.     * As a side-effect, finds the least recently used     * buffer that is not in use (if any).     */    buf = Get_Front_Of_FS_Buffer_List(&cache->bufferList);    while (buf != 0) {	if (buf->fsBlockNum == fsBlockNum) {	    /* If buffer is in use, wait until it is available. */	    while (buf->flags & FS_BUFFER_INUSE) {		Debug("Waiting for block %lu\n", fsBlockNum);		Cond_Wait(&cache->cond, &cache->lock);	    }	    goto done;	}	/* If buffer isn't in use, it's a candidate for LRU. */	if (!(buf->flags & FS_BUFFER_INUSE))	    lru = buf;	buf = Get_Next_In_FS_Buffer_List(buf);    }    /*     * If number of allocated buffers does not exceed the     * limit, allocate a new one.     */    if (cache->numCached < FS_BUFFER_CACHE_MAX_BLOCKS) {	buf = (struct FS_Buffer*) Malloc(sizeof(*buf));	if (buf != 0) {	    buf->data = Alloc_Page();	    if (buf->data == 0)		Free(buf);	    else {		/* Successful creation */		buf->fsBlockNum = fsBlockNum;		buf->flags = 0;		Add_To_Front_Of_FS_Buffer_List(&cache->bufferList, buf);		++cache->numCached;		goto readAndAcquire;	    }	}    }        /*     * If there is no LRU buffer, then we have exceeded     * the number of available buffers.     */    if (lru == 0)	return ENOMEM;    KASSERT(!noEvict);    /* Make sure the LRU buffer is clean. */    if ((rc = Sync_Buffer(cache, lru)) != 0)	return rc;    /* LRU buffer is clean, so we can steal it. */    buf = lru;    buf->flags = 0;    Move_To_Front(cache, buf);readAndAcquire:    /*     * The buffer selected should be clean (no uncommitted data),     * and should have been moved to the front of the buffer list     * (to show it has just been referenced).     */    KASSERT(!(buf->flags & FS_BUFFER_DIRTY));    KASSERT(Get_Front_Of_FS_Buffer_List(&cache->bufferList) == buf);    /* Read block data into buffer. */    if ((rc = Do_Buffer_IO(cache, buf, Block_Read)) != 0)	return rc;done:    /* Buffer is now in use. */    buf->flags |= FS_BUFFER_INUSE;    /* Success! */    Debug("Acquired block %lu\n", fsBlockNum);    *pBuf = buf;    return 0;}/* * Synchronize cache with disk. */static int Sync_Cache(struct FS_Buffer_Cache *cache){    int rc = 0;    struct FS_Buffer *buf;    KASSERT(IS_HELD(&cache->lock));    buf = Get_Front_Of_FS_Buffer_List(&cache->bufferList);    while (buf != 0) {	if ((rc = Sync_Buffer(cache, buf)) != 0)	    break;	buf = Get_Next_In_FS_Buffer_List(buf);    }    return rc;}/* * Free the memory used by a filesystem buffer. */static void Free_Buffer(struct FS_Buffer *buf){    KASSERT(!(buf->flags & (FS_BUFFER_DIRTY | FS_BUFFER_INUSE)));    Free_Page(buf->data);    Free(buf);}/* ---------------------------------------------------------------------- * Public functions * ---------------------------------------------------------------------- *//* * Create a cache of filesystem buffers. */struct FS_Buffer_Cache *Create_FS_Buffer_Cache(struct Block_Device *dev, uint_t fsBlockSize){    struct FS_Buffer_Cache *cache;    KASSERT(dev != 0);    KASSERT(dev->inUse);    /*     * Currently, we don't allow filesystem blocks     * larger than the hardware page size.     */    KASSERT(fsBlockSize <= PAGE_SIZE);    cache = (struct FS_Buffer_Cache*) Malloc(sizeof(*cache));    if (cache == 0)	return 0;    cache->dev = dev;    cache->fsBlockSize = fsBlockSize;    cache->numCached = 0;    Clear_FS_Buffer_List(&cache->bufferList);    Mutex_Init(&cache->lock);    Cond_Init(&cache->cond);    return cache;}/* * Synchronize contents of cache with the disk * by writing out all dirty buffers. */int Sync_FS_Buffer_Cache(struct FS_Buffer_Cache *cache){    int rc;    Mutex_Lock(&cache->lock);    rc = Sync_Cache(cache);    Mutex_Unlock(&cache->lock);    return rc;}/* * Destroy a filesystem buffer cache. * None of the buffers in the cache must be in use. * The cache must not be used after this function returns! */int Destroy_FS_Buffer_Cache(struct FS_Buffer_Cache *cache){    int rc;    struct FS_Buffer *buf;    Mutex_Lock(&cache->lock);    /* Flush all contents back to disk. */    rc = Sync_Cache(cache);    /* Free all of the buffers. */    buf = Get_Front_Of_FS_Buffer_List(&cache->bufferList);    while (buf != 0) {	struct FS_Buffer *next = Get_Next_In_FS_Buffer_List(buf);	Free_Buffer(buf);	buf = next;    }    Clear_FS_Buffer_List(&cache->bufferList);    Mutex_Unlock(&cache->lock);    /* Free the cache object itself. */    Free(cache);    return rc;}/* * Get a buffer for given filesystem block. */int Get_FS_Buffer(struct FS_Buffer_Cache *cache, ulong_t fsBlockNum, struct FS_Buffer **pBuf){    int rc;    Mutex_Lock(&cache->lock);    rc = Get_Buffer(cache, fsBlockNum, pBuf);    Mutex_Unlock(&cache->lock);    return rc;}/* * Mark the given buffer as being modified. */void Modify_FS_Buffer(struct FS_Buffer_Cache *cache, struct FS_Buffer *buf){    KASSERT(buf->flags & FS_BUFFER_INUSE);    buf->flags |= FS_BUFFER_DIRTY;}/* * Explicitly synchronize given buffer with its on-disk storage, * without releasing the buffer. */int Sync_FS_Buffer(struct FS_Buffer_Cache *cache, struct FS_Buffer *buf){    int rc;    KASSERT(buf->flags & FS_BUFFER_INUSE);    Mutex_Lock(&cache->lock);    rc = Sync_Buffer(cache, buf);    Mutex_Unlock(&cache->lock);    return rc;}/* * Release given buffer. */int Release_FS_Buffer(struct FS_Buffer_Cache *cache, struct FS_Buffer *buf){    int rc = 0;    KASSERT(buf->flags & FS_BUFFER_INUSE);    Mutex_Lock(&cache->lock);    /*     * If the buffer is OK to release,     * mark it as no longer in use and notify any     * thread waiting to use it.     */    if (rc == 0) {	buf->flags &= ~(FS_BUFFER_INUSE);	Cond_Broadcast(&cache->cond);    }    Debug("Released block %lu\n", buf->fsBlockNum);    Mutex_Unlock(&cache->lock);    return rc;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -