├── .gitmodules ├── example ├── Makefile └── rw_example.c ├── include ├── module.h ├── ramdisk.h ├── raspberry.h ├── bluedbm.h ├── zone.h ├── log.h ├── lru.h ├── flash.h ├── bits.h ├── page.h └── device.h ├── docker └── Dockerfile ├── LICENSE ├── .github └── workflows │ └── build.yml ├── .gitignore ├── interface ├── flash.c └── module.c ├── test ├── bits-test.c ├── lru-test.c ├── ramdisk-test.c └── zone-test.c ├── ftl └── page │ ├── page-map.c │ ├── page-read.c │ ├── page-write.c │ ├── page-gc.c │ ├── page-interface.c │ └── page-core.c ├── device ├── device.c ├── ramdisk │ └── ramdisk.c ├── raspberry │ └── raspberry.c ├── zone │ └── zone.c └── bluedbm │ └── bluedbm.c ├── README.md ├── integration-test.c ├── util └── lru.c ├── Makefile └── .clang-format /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "unity"] 2 | path = unity 3 | url = https://github.com/ThrowTheSwitch/Unity.git 4 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES : .c .cpp .o 2 | FTL_INCLUDE_PATH = /usr/local/include/ftl 3 | 4 | CXX = g++ 5 | LIBS = -lftl -lpthread $(shell pkg-config --libs glib-2.0) 6 | CFLAGS = $(shell pkg-config --cflags glib-2.0) -I$(FTL_INCLUDE_PATH) 7 | TARGET = rw_example 8 | 9 | all: $(TARGET) 10 | 11 | rw_example: rw_example.o 12 | $(CXX) $(CFLAGS) -o $@ $^ $(LIBS) 13 | 14 | clean: 15 | rm -rf $(TARGET) *.o 16 | -------------------------------------------------------------------------------- /include/module.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file module.h 3 | * @brief creation and deletion of the module's interface 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-09-22 7 | */ 8 | #ifndef MODULE_H 9 | #define MODULE_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | 17 | #include "flash.h" 18 | 19 | enum { 20 | PAGE_FTL_MODULE = 0 /**< page FTL number*/, 21 | }; 22 | 23 | int module_init(const int modnum, struct flash_device **, uint64_t flags); 24 | int module_exit(struct flash_device *); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN apt-get update && apt-get clean 4 | RUN apt-get install --no-install-recommends -y git make gcc g++ libglib2.0-dev libiberty-dev 5 | RUN apt-get install --no-install-recommends -y cppcheck flawfinder cflow 6 | RUN apt-get install --no-install-recommends -y python3-pip 7 | RUN apt-get install --no-install-recommends -y bear 8 | RUN apt-get install --no-install-recommends -y zsh 9 | RUN apt-get clean 10 | RUN rm -rf /var/lib/apt/lists/* 11 | 12 | RUN chsh -s $(which zsh) 13 | RUN pip3 install lizard==1.17.10 14 | RUN pip3 install compdb==0.2.0 15 | 16 | RUN mkdir -p /ftl 17 | WORKDIR /ftl 18 | 19 | CMD [ "make" ] 20 | -------------------------------------------------------------------------------- /include/ramdisk.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ramdisk.h 3 | * @brief ramdisk's header file 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-03 7 | */ 8 | #ifndef RAMDISK_H 9 | #define RAMDISK_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "device.h" 17 | 18 | /** 19 | * @brief structure for manage the ramdisk 20 | */ 21 | struct ramdisk { 22 | size_t size; 23 | char *buffer; 24 | uint64_t *is_used; 25 | int o_flags; 26 | }; 27 | 28 | int ramdisk_open(struct device *, const char *name, int flags); 29 | ssize_t ramdisk_write(struct device *, struct device_request *); 30 | ssize_t ramdisk_read(struct device *, struct device_request *); 31 | int ramdisk_erase(struct device *, struct device_request *); 32 | int ramdisk_close(struct device *); 33 | 34 | int ramdisk_device_init(struct device *, uint64_t flags); 35 | int ramdisk_device_exit(struct device *); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/raspberry.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file raspberry.h 3 | * @brief raspberry's header file 4 | * @author Gijun Oh 5 | * @date 2023-05-29 6 | */ 7 | #ifndef RASPBERRY_H 8 | #define RASPBERRY_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "device.h" 17 | 18 | /** 19 | * @brief structure for manage the raspberry 20 | */ 21 | struct raspberry { 22 | size_t size; 23 | int o_flags; 24 | pthread_spinlock_t lock; 25 | }; 26 | 27 | int raspberry_open(struct device *, const char *name, int flags); 28 | ssize_t raspberry_write(struct device *, struct device_request *); 29 | ssize_t raspberry_read(struct device *, struct device_request *); 30 | int raspberry_erase(struct device *, struct device_request *); 31 | int raspberry_close(struct device *); 32 | 33 | int raspberry_device_init(struct device *, uint64_t flags); 34 | int raspberry_device_exit(struct device *); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Gijun Oh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/rw_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "module.h" 7 | #include "flash.h" 8 | #include "page.h" 9 | #include "log.h" 10 | #include "device.h" 11 | 12 | int main(void) 13 | { 14 | struct flash_device *flash = NULL; 15 | char buffer[8192]; 16 | assert(0 == module_init(PAGE_FTL_MODULE, &flash, RAMDISK_MODULE)); 17 | pr_info("module initialize\n"); 18 | flash->f_op->open(flash, NULL, O_CREAT | O_RDWR); 19 | for (int i = 0; i < 8192 * 10; i++) { 20 | int num; 21 | size_t sector; 22 | num = i * 2; 23 | memset(buffer, 0, 8192); 24 | *(int *)buffer = num; 25 | sector = rand() % (1 << 31); 26 | flash->f_op->write(flash, buffer, sizeof(int), sector); 27 | pr_info("write value: %d\n", *(int *)buffer); 28 | memset(buffer, 0, 8192); 29 | flash->f_op->read(flash, buffer, sizeof(int), sector); 30 | pr_info("read value: %d\n", *(int *)buffer); 31 | if (i % 8192 * 5 == 0) { 32 | flash->f_op->ioctl(flash, PAGE_FTL_IOCTL_TRIM); 33 | } 34 | } 35 | flash->f_op->close(flash); 36 | assert(0 == module_exit(flash)); 37 | pr_info("module deallcation\n"); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /include/bluedbm.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bluedbm.h 3 | * @brief bluedbm module's header file 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-20 7 | */ 8 | #ifndef BLUEDBM_H 9 | #define BLUEDBM_H 10 | 11 | #include 12 | #include 13 | 14 | #include "device.h" 15 | 16 | #define BLUEDBM_NR_BLOCKS \ 17 | (4096) /**< number of blocks(segments) in the flash board */ 18 | 19 | /** 20 | * @brief structure for manage the dma 21 | */ 22 | typedef struct { 23 | uint32_t tag; 24 | char *data; 25 | void *d_private; 26 | } bluedbm_dma_t; 27 | 28 | /** 29 | * @brief structure for manage the bluedbm device 30 | */ 31 | struct bluedbm { 32 | size_t size; 33 | memio_t *mio; 34 | int o_flags; 35 | }; 36 | 37 | int bluedbm_open(struct device *, const char *name, int flags); 38 | ssize_t bluedbm_write(struct device *, struct device_request *); 39 | ssize_t bluedbm_read(struct device *, struct device_request *); 40 | int bluedbm_erase(struct device *, struct device_request *); 41 | int bluedbm_close(struct device *); 42 | 43 | int bluedbm_device_init(struct device *, uint64_t flags); 44 | int bluedbm_device_exit(struct device *); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Flash Translation Layer CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build-and-test-x86_64: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | with: 15 | submodules: recursive 16 | - name: prepare environment 17 | run: | 18 | sudo apt-get update -y && sudo apt-get clean 19 | sudo apt-get install --no-install-recommends -y git make gcc g++ libglib2.0-dev libiberty-dev 20 | sudo apt-get install --no-install-recommends -y cppcheck flawfinder cflow 21 | sudo apt-get install --no-install-recommends -y python3-pip 22 | sudo pip3 install lizard==1.17.0 23 | - name: make check 24 | run: make check 25 | - name: make flow 26 | run: make flow 27 | - name: make 28 | run: make 29 | - name: make test 30 | run: make clean && make test USE_LOG_SILENT=1 31 | - name: make integration-test 32 | run: make clean && make integration-test 33 | - name: make benchmark.out 34 | run: make clean && make benchmark.out 35 | - name: make benchmark.out with legacy random 36 | run: make clean && make benchmark.out USE_LEGACY_RANDOM=1 37 | -------------------------------------------------------------------------------- /include/zone.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file zone.h 3 | * @brief zbd's header file 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-09 7 | */ 8 | #ifndef ZONE_H 9 | #define ZONE_H 10 | 11 | #include 12 | 13 | #include "device.h" 14 | 15 | /** 16 | * @brief containing a zone file's metadata 17 | */ 18 | struct zone_file_descriptor { 19 | int fd; 20 | }; 21 | 22 | /** 23 | * @brief containing a zoned block device's metadata 24 | */ 25 | struct zone_meta { 26 | size_t total_size; 27 | uint64_t zone_size; 28 | uint64_t nr_zones; 29 | uint64_t block_size; 30 | int o_flags; 31 | 32 | struct zone_file_descriptor read; 33 | struct zone_file_descriptor write; 34 | struct zbd_info info; 35 | struct zbd_zone *zones; 36 | }; 37 | 38 | int zone_open(struct device *, const char *name, int flags); 39 | ssize_t zone_write(struct device *, struct device_request *); 40 | ssize_t zone_read(struct device *, struct device_request *); 41 | int zone_erase(struct device *, struct device_request *); 42 | int zone_close(struct device *); 43 | 44 | int zone_device_init(struct device *, uint64_t flags); 45 | int zone_device_exit(struct device *); 46 | 47 | static inline uint64_t zone_get_zone_number(struct device *dev, 48 | struct device_address paddr) 49 | { 50 | (void)dev; 51 | return paddr.format.block; 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | #define STRINGIFY(x) #x 11 | #define TOSTRING(x) STRINGIFY(x) 12 | 13 | #ifdef DEBUG 14 | #define pr_debug(fmt, ...) \ 15 | fprintf(stdout, \ 16 | "DEBUG:[" __FILE__ ":%s(" TOSTRING(__LINE__) ")] " fmt, \ 17 | __func__, ##__VA_ARGS__) 18 | #else 19 | #define pr_debug(...) (void)0 20 | #endif 21 | 22 | #ifdef ENABLE_LOG_SILENT 23 | #define pr_err(...) (void)0 24 | #define pr_warn(...) (void)0 25 | #define pr_info(...) (void)0 26 | #else 27 | #define pr_info(fmt, ...) \ 28 | fprintf(stdout, "INFO:[" __FILE__ ":%s(" TOSTRING(__LINE__) ")] " fmt, \ 29 | __func__, ##__VA_ARGS__) 30 | 31 | #define pr_warn(fmt, ...) \ 32 | fprintf(stderr, \ 33 | "WARNING:[" __FILE__ ":%s(" TOSTRING(__LINE__) ")] " fmt, \ 34 | __func__, ##__VA_ARGS__) 35 | 36 | #define pr_err(fmt, ...) \ 37 | fprintf(stderr, \ 38 | "ERROR:[" __FILE__ ":%s(" TOSTRING(__LINE__) ")] " fmt, \ 39 | __func__, ##__VA_ARGS__) 40 | #endif 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE! Don't add files that are generated in specific 3 | # subdirectories here. Add them in the ".gitignore" file 4 | # in that subdirectory instead. 5 | # 6 | # NOTE! Please use 'git ls-files -i --exclude-standard' 7 | # command after changing this file, to see if there are 8 | # any tracked files which get ignored after the change. 9 | # 10 | # Normal rules 11 | # 12 | .* 13 | *.o 14 | *.o.* 15 | *.out 16 | *.a 17 | *.s 18 | *.ko 19 | *.so 20 | *.so.dbg 21 | *.mod.c 22 | *.i 23 | *.lst 24 | *.symtypes 25 | *.order 26 | modules.builtin 27 | *.elf 28 | *.bin 29 | *.gz 30 | *.bz2 31 | *.lzma 32 | *.xz 33 | *.lzo 34 | *.patch 35 | *.gcno 36 | 37 | # 38 | # Top-level generic files 39 | # 40 | /tags 41 | /TAGS 42 | /linux 43 | /vmlinux 44 | /vmlinuz 45 | /System.map 46 | /Module.markers 47 | /Module.symvers 48 | 49 | # 50 | # git files that we don't want to ignore even it they are dot-files 51 | # 52 | !.gitignore 53 | !.mailmap 54 | 55 | # 56 | # Generated include files 57 | # 58 | include/config 59 | include/linux/version.h 60 | include/generated 61 | arch/*/include/generated 62 | 63 | # stgit generated dirs 64 | patches-* 65 | 66 | # quilt's files 67 | patches 68 | series 69 | 70 | # cscope files 71 | cscope.* 72 | ncscope.* 73 | 74 | # gnu global files 75 | GPATH 76 | GRTAGS 77 | GSYMS 78 | GTAGS 79 | 80 | *.orig 81 | *~ 82 | \#*# 83 | 84 | # doxygen result directory 85 | doxygen/ 86 | 87 | # compile commands 88 | compile_commands.json 89 | 90 | # coverage results 91 | *.gcov 92 | *.gcda 93 | -------------------------------------------------------------------------------- /interface/flash.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file flash.c 3 | * @brief generic flash control interfaces 4 | * @author Gijun Oh 5 | * @version 0.1 6 | * @date 2021-09-22 7 | */ 8 | #include 9 | #include 10 | 11 | #include "module.h" 12 | #include "flash.h" 13 | #include "log.h" 14 | 15 | /** 16 | * @brief initialize the flash module 17 | * 18 | * @param __flash double pointer of the flash device information 19 | * @param flags flags for flash module and submodule 20 | * 21 | * @return zero to success, error number to fail 22 | * 23 | * @note 24 | * This function allocates the memory to the __flash 25 | */ 26 | int flash_module_init(struct flash_device **__flash, uint64_t flags) 27 | { 28 | int err; 29 | struct flash_device *flash; 30 | 31 | (void)flags; 32 | 33 | flash = (struct flash_device *)malloc(sizeof(struct flash_device)); 34 | if (flash == NULL) { 35 | err = -errno; 36 | pr_err("fail to allocate the flash information pointer\n"); 37 | goto exception; 38 | } 39 | flash->f_op = NULL; 40 | flash->f_private = NULL; 41 | flash->f_submodule_exit = NULL; 42 | 43 | *__flash = flash; 44 | 45 | return 0; 46 | 47 | exception: 48 | flash_module_exit(flash); 49 | return err; 50 | } 51 | 52 | /** 53 | * @brief free resources in the flash module 54 | * 55 | * @param flash pointer of the flash device information 56 | * 57 | * @return zero to success, error number to fail 58 | */ 59 | int flash_module_exit(struct flash_device *flash) 60 | { 61 | if (flash == NULL) { 62 | pr_err("flash pointer is null detected\n"); 63 | return 0; 64 | } 65 | 66 | free(flash); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /include/lru.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lru.h 3 | * @brief data structures and interfaces for the lru cache 4 | * @author Gijun Oh 5 | * @version 0.1 6 | * @date 2021-09-30 7 | * @note 8 | * This is not thread-safe. 9 | */ 10 | #ifndef LRU_H 11 | #define LRU_H 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | #include "log.h" 21 | 22 | /** 23 | * @brief deallocate the value for eviction function type. 0 means successfully evicted 24 | */ 25 | typedef int (*lru_dealloc_fn)(const uint64_t, uintptr_t); 26 | 27 | /** 28 | * @brief doubly-linked list data structure 29 | */ 30 | struct lru_node { 31 | uint64_t key; 32 | uintptr_t value; 33 | struct lru_node *next; 34 | struct lru_node *prev; 35 | }; 36 | 37 | /** 38 | * @brief main LRU cache data structure 39 | */ 40 | struct lru_cache { 41 | size_t capacity; /**< total number of the lru_node */ 42 | size_t size; /**< current number of the lru_node */ 43 | struct lru_node *head; 44 | lru_dealloc_fn deallocate; 45 | struct lru_node nil; /**< don't access this directly */ 46 | }; 47 | 48 | struct lru_cache *lru_init(const size_t capacity, lru_dealloc_fn deallocate); 49 | int lru_put(struct lru_cache *cache, const uint64_t key, uintptr_t value); 50 | uintptr_t lru_get(struct lru_cache *cache, const uint64_t key); 51 | int lru_free(struct lru_cache *cache); 52 | 53 | /** 54 | * @brief get evict size of the LRU cache 55 | * 56 | * @param cache LRU cache structrue pointer 57 | * 58 | * @return number of the eviction entries 59 | * 60 | * @note 61 | * Default LRU cache's eviction size is 30% of its capacity 62 | */ 63 | static inline size_t lru_get_evict_size(struct lru_cache *cache) 64 | { 65 | pr_debug("evict size ==> %zu\n", (size_t)(cache->capacity)); 66 | return cache->capacity; 67 | } 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /include/flash.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file flash.h 3 | * @brief generic flash control interfaces' header 4 | * @author Gijun Oh 5 | * @version 0.1 6 | * @date 2021-09-22 7 | */ 8 | #ifndef FLASH_H 9 | #define FLASH_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | /** 20 | * @brief flags related on the flash and submodule 21 | */ 22 | enum { 23 | FLASH_DEFAULT_FLAG = 0 /**< flash default flags */, 24 | }; 25 | 26 | struct flash_device; 27 | struct flash_operations; 28 | 29 | /** 30 | * @brief contain the flash device information 31 | */ 32 | struct flash_device { 33 | const struct flash_operations *f_op; /**< contain the flash operations */ 34 | void *f_private; /**< device specific information contained */ 35 | int (*f_submodule_exit)(struct flash_device *); /**< deallocate */ 36 | }; 37 | 38 | /** 39 | * @brief generic interface for communicate with the flash 40 | * 41 | * @note 42 | * `struct flash_device *` means flash control information 43 | * - count: length of the buffer (bytes) 44 | * - offset: offset of the write position (bytes, NOT sector(512 bytes)) 45 | */ 46 | struct flash_operations { 47 | int (*open)(struct flash_device *, const char *name, 48 | int flags); /**< open the flash device */ 49 | ssize_t (*write)(struct flash_device *, void *buffer, size_t count, 50 | off_t offset); /**< write to the flash */ 51 | ssize_t (*read)(struct flash_device *, void *buffer, size_t count, 52 | off_t offset); /**< read from the flash */ 53 | int (*ioctl)(struct flash_device *, unsigned int request, 54 | ...); /**< for other instruction sets (barely use) */ 55 | int (*close)(struct flash_device *); /** close the flash device */ 56 | }; 57 | 58 | int flash_module_init(struct flash_device **, uint64_t flags); 59 | int flash_module_exit(struct flash_device *); 60 | 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /interface/module.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file module.c 3 | * @brief this deal the initialization and removing of the module 4 | * @author Gijun Oh 5 | * @version 0.1 6 | * @date 2021-09-22 7 | */ 8 | #include 9 | 10 | #include "flash.h" 11 | #include "module.h" 12 | #include "page.h" 13 | #include "log.h" 14 | 15 | /** 16 | * @brief submodule list table 17 | * 18 | * @param flash pointer of the flash device information 19 | * @param int flags for flash module 20 | * 21 | * @note 22 | * You must follow the submodule index in the `module.h` 23 | */ 24 | static int (*submodule_init[])(struct flash_device *, uint64_t) = { 25 | /* [PAGE_FTL_MODULE] = */ page_ftl_module_init, 26 | }; 27 | 28 | /** 29 | * @brief generic initializer for initialize the module 30 | * 31 | * @param modnum module number described in the `module.h` 32 | * @param __flash double pointer of the flash device information 33 | * @param flags flags for flash and submodule 34 | * 35 | * @return zero to success, error number to fail 36 | * 37 | * @note 38 | * This function allocates the memory to the __flash. 39 | * And you must not change this function!! 40 | */ 41 | int module_init(const int modnum, struct flash_device **__flash, uint64_t flags) 42 | { 43 | int err; 44 | err = flash_module_init(__flash, flags); 45 | if (err) { 46 | pr_err("flash initialize failed\n"); 47 | return err; 48 | } 49 | pr_info("flash initialize success\n"); 50 | err = submodule_init[modnum](*__flash, flags); 51 | if (err) { 52 | pr_err("submodule initialize failed\n"); 53 | return err; 54 | } 55 | pr_info("submodule initialize success\n"); 56 | return 0; 57 | } 58 | 59 | /** 60 | * @brief free resources in the flash module and submodule 61 | * 62 | * @param flash pointer of the flash device information 63 | * 64 | * @return zero to success, error number to fail 65 | */ 66 | int module_exit(struct flash_device *flash) 67 | { 68 | int err; 69 | if (flash->f_submodule_exit) { 70 | err = flash->f_submodule_exit(flash); 71 | if (err) { 72 | pr_err("submodule resources free failed\n"); 73 | return err; 74 | } 75 | pr_info("submodule deallocates success\n"); 76 | } 77 | 78 | err = flash_module_exit(flash); 79 | if (err) { 80 | pr_err("flash resources free failed\n"); 81 | return err; 82 | } 83 | pr_info("flash deallocates success\n"); 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /test/bits-test.c: -------------------------------------------------------------------------------- 1 | #include "bits.h" 2 | #include "unity.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void setUp(void) 12 | { 13 | } 14 | 15 | void tearDown(void) 16 | { 17 | } 18 | 19 | void test_bits(void) 20 | { 21 | const uint64_t nr_bits = 4096; 22 | uint64_t *bits; 23 | uint64_t zero, one; 24 | bits = (uint64_t *)malloc(BITS_TO_UINT64_ALIGN(nr_bits)); 25 | memset(bits, 0, BITS_TO_UINT64_ALIGN(nr_bits)); 26 | one = find_first_one_bit(bits, nr_bits, 0); 27 | zero = find_first_zero_bit(bits, nr_bits, 0); 28 | for (uint64_t i = 0; i < nr_bits; i++) { 29 | set_bit(bits, i); 30 | one = find_first_one_bit(bits, nr_bits, 0); 31 | zero = find_first_zero_bit(bits, nr_bits, 0); 32 | TEST_ASSERT_EQUAL_UINT(0, (uint)one); 33 | if (i + 1 < nr_bits) { 34 | TEST_ASSERT_EQUAL_UINT(i + 1, (uint)zero); 35 | } else { 36 | TEST_ASSERT_EQUAL_INT(-1, (int)zero); 37 | } 38 | } 39 | for (uint64_t i = 0; i < nr_bits; i++) { 40 | reset_bit(bits, i); 41 | one = find_first_one_bit(bits, nr_bits, 0); 42 | zero = find_first_zero_bit(bits, nr_bits, 0); 43 | if (i + 1 < nr_bits) { 44 | TEST_ASSERT_EQUAL_UINT(i + 1, (uint)one); 45 | } else { 46 | TEST_ASSERT_EQUAL_INT(-1, (int)one); 47 | } 48 | TEST_ASSERT_EQUAL_UINT(0, (uint)zero); 49 | } 50 | 51 | // For arm machine 52 | (void)zero; 53 | (void)one; 54 | 55 | free(bits); 56 | } 57 | 58 | void test_get_bits(void) 59 | { 60 | int counter = 20; 61 | while (counter) { 62 | const uint64_t nr_bits = (0x1 << counter); 63 | uint64_t i; 64 | char *setbit; 65 | uint64_t *bits; 66 | setbit = (char *)malloc((size_t)nr_bits); 67 | memset(setbit, 0, (size_t)nr_bits); 68 | bits = (uint64_t *)malloc( 69 | (size_t)BITS_TO_UINT64_ALIGN(nr_bits)); 70 | memset(bits, 0, (size_t)BITS_TO_UINT64_ALIGN(nr_bits)); 71 | srand((unsigned int)time(NULL) + (counter * rand()) % INT_MAX); 72 | for (i = 0; i < nr_bits; i++) { 73 | setbit[i] = (char)(rand() % 2); 74 | } 75 | for (i = 0; i < nr_bits; i++) { 76 | if (setbit[i] == 0) { 77 | continue; 78 | } 79 | set_bit(bits, i); 80 | } 81 | for (i = 0; i < nr_bits; i++) { 82 | int bit = get_bit(bits, i); 83 | TEST_ASSERT_EQUAL_INT(setbit[i], bit); 84 | } 85 | free(bits); 86 | free(setbit); 87 | counter -= 1; 88 | } 89 | } 90 | 91 | int main(void) 92 | { 93 | UNITY_BEGIN(); 94 | RUN_TEST(test_bits); 95 | RUN_TEST(test_get_bits); 96 | return UNITY_END(); 97 | } 98 | -------------------------------------------------------------------------------- /test/lru-test.c: -------------------------------------------------------------------------------- 1 | #include "lru.h" 2 | #include "unity.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | void setUp(void) 9 | { 10 | } 11 | 12 | void tearDown(void) 13 | { 14 | } 15 | 16 | void test_lru_init(void) 17 | { 18 | struct lru_cache *cache; 19 | cache = lru_init(0, NULL); 20 | TEST_ASSERT_NULL(cache); 21 | cache = lru_init(10, NULL); 22 | TEST_ASSERT_NOT_NULL(cache); 23 | lru_free(cache); 24 | } 25 | 26 | void test_lru_fill(void) 27 | { 28 | struct lru_cache *cache; 29 | cache = lru_init(10, NULL); 30 | for (size_t i = 1; i <= 10; i++) { 31 | TEST_ASSERT_EQUAL_INT(0, lru_put(cache, i, i * 2)); 32 | } 33 | for (size_t i = 1; i <= 10; i++) { 34 | TEST_ASSERT_EQUAL_INT(i * 2, lru_get(cache, i)); 35 | } 36 | for (size_t i = 1; i <= 10; i++) { 37 | TEST_ASSERT_EQUAL_INT(0, lru_put(cache, i + 10, (i + 10) * 2)); 38 | } 39 | lru_free(cache); 40 | } 41 | 42 | static int dealloc_data(const uint64_t key, uintptr_t value) 43 | { 44 | int *data = (int *)value; 45 | (void)key; 46 | TEST_ASSERT_NOT_NULL(data); 47 | free(data); 48 | return 0; 49 | } 50 | 51 | void test_lru_big_fill(void) 52 | { 53 | const int cache_size = 1024; 54 | const int total_size = cache_size * 100; 55 | size_t counter = 0; 56 | size_t last_size = 0; 57 | struct lru_cache *cache; 58 | cache = lru_init(cache_size, dealloc_data); 59 | for (int i = 0; i < total_size; i++) { 60 | int *data; 61 | data = (int *)malloc(sizeof(int)); 62 | *data = i; 63 | lru_put(cache, (uint64_t)i, (uintptr_t)data); 64 | } 65 | last_size = cache->size; 66 | for (ssize_t i = total_size - 1; i >= 0; i--) { 67 | if (counter >= last_size) { 68 | TEST_ASSERT_NULL((void *)lru_get(cache, (size_t)i)); 69 | } else { 70 | uintptr_t data = lru_get(cache, (size_t)i); 71 | TEST_ASSERT_NOT_NULL((void *)data); 72 | TEST_ASSERT_EQUAL_INT(i, *(int *)data); 73 | } 74 | counter += 1; 75 | } 76 | TEST_ASSERT_EQUAL_INT(0, lru_free(cache)); 77 | } 78 | 79 | void test_lru_small_fill(void) 80 | { 81 | const int cache_size = 2; 82 | const int total_size = 100; 83 | struct lru_cache *cache; 84 | cache = lru_init(cache_size, dealloc_data); 85 | for (size_t i = 0; i < total_size; i++) { 86 | int *data; 87 | uintptr_t test; 88 | data = (int *)malloc(sizeof(int)); 89 | *data = (int)i; 90 | lru_put(cache, i, (uintptr_t)data); 91 | test = lru_get(cache, i); 92 | TEST_ASSERT_EQUAL_INT(i, *(int *)test); 93 | } 94 | TEST_ASSERT_EQUAL_INT(0, lru_free(cache)); 95 | } 96 | 97 | int main(void) 98 | { 99 | UNITY_BEGIN(); 100 | RUN_TEST(test_lru_init); 101 | RUN_TEST(test_lru_fill); 102 | RUN_TEST(test_lru_big_fill); 103 | RUN_TEST(test_lru_small_fill); 104 | return UNITY_END(); 105 | } 106 | -------------------------------------------------------------------------------- /include/bits.h: -------------------------------------------------------------------------------- 1 | #ifndef BITS_H 2 | #define BITS_H 3 | 4 | #include 5 | #include 6 | 7 | #define BITS_NOT_FOUND ((uint64_t)UINT64_MAX) 8 | 9 | #define BITS_PER_BYTE (8) 10 | #define BITS_PER_UINT64 (BITS_PER_BYTE * sizeof(uint64_t)) 11 | 12 | #define BITS_TO_UINT64_ALIGN(x) \ 13 | (((uint64_t)x / BITS_PER_UINT64 + 1) * sizeof(uint64_t)) 14 | #define BITS_TO_UINT64(x) ((uint64_t)x / BITS_PER_UINT64) 15 | 16 | /** 17 | * @brief set the index position bit in the array(uint64_t) 18 | * 19 | * @param bits array which contains the bitmap 20 | * @param index set position (bit position NOT byte or uint64_t position) 21 | */ 22 | static inline void set_bit(uint64_t *bits, uint64_t index) 23 | { 24 | bits[BITS_TO_UINT64(index)] |= 25 | ((uint64_t)0x1 << (index % BITS_PER_UINT64)); 26 | } 27 | 28 | /** 29 | * @brief get the value at the index position bit in the array(uint64_t) 30 | * 31 | * @param bits array which contains the bitmap 32 | * @param index get position (bit position NOT byte or uint64_t position) 33 | * 34 | * @return bit status at the index position 35 | */ 36 | static inline int get_bit(uint64_t *bits, uint64_t index) 37 | { 38 | return (bits[BITS_TO_UINT64(index)] & 39 | ((uint64_t)0x1 << (index % BITS_PER_UINT64))) > 0; 40 | } 41 | 42 | /** 43 | * @brief reset the index position bit in the array(uint64_t) 44 | * 45 | * @param bits array which contains the bitmap 46 | * @param index reset position (bit position NOT byte or uint64_t position) 47 | */ 48 | static inline void reset_bit(uint64_t *bits, uint64_t index) 49 | { 50 | bits[BITS_TO_UINT64(index)] &= 51 | ~((uint64_t)0x1 << (index % BITS_PER_UINT64)); 52 | } 53 | 54 | /** 55 | * @brief find first zero bit in the array(uint64_t) 56 | * 57 | * @param bits array which contains the bitmap 58 | * @param size bitmap's size (the number of bits NOT bytes) 59 | * @param idx start position bit 60 | * 61 | * @return first zero bit position 62 | */ 63 | static inline uint64_t find_first_zero_bit(uint64_t *bits, uint64_t size, 64 | uint64_t idx) 65 | { 66 | while (idx < size) { 67 | uint64_t bucket = bits[BITS_TO_UINT64(idx)]; 68 | if (bucket < (uint64_t)UINT64_MAX) { 69 | uint64_t diff = 0; 70 | for (diff = 0; diff < BITS_PER_UINT64; diff++) { 71 | if ((bucket & (uint64_t)((uint64_t)0x1 72 | << diff)) == 0x0) { 73 | break; 74 | } 75 | } 76 | return idx + diff; 77 | } 78 | idx += BITS_PER_UINT64; 79 | } 80 | return BITS_NOT_FOUND; 81 | } 82 | 83 | /** 84 | * @brief find first one bit in the array(uint64_t) 85 | * 86 | * @param bits array which contains the bitmap 87 | * @param size bitmap's size (the number of bits NOT bytes) 88 | * @param idx start position bit 89 | * 90 | * @return first one bit position 91 | */ 92 | static inline uint64_t find_first_one_bit(uint64_t *bits, uint64_t size, 93 | uint64_t idx) 94 | { 95 | while (idx < size) { 96 | uint64_t bucket = bits[BITS_TO_UINT64(idx)]; 97 | if (bucket > (uint64_t)0x0) { 98 | uint64_t diff = 0; 99 | for (diff = 0; diff < BITS_PER_UINT64; diff++) { 100 | if ((bucket & 101 | (uint64_t)((uint64_t)0x1 << diff)) > 0) { 102 | break; 103 | } 104 | } 105 | return idx + diff; 106 | } 107 | idx += BITS_PER_UINT64; 108 | } 109 | return BITS_NOT_FOUND; 110 | } 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /ftl/page/page-map.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file page-map.c 3 | * @brief manage the mapping information 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-05 7 | */ 8 | #include "bits.h" 9 | #include "page.h" 10 | #include "device.h" 11 | #include "log.h" 12 | 13 | #include 14 | #include 15 | 16 | /** 17 | * @brief get page from the segment 18 | * 19 | * @param pgftl pointer of the page-ftl structure 20 | * 21 | * @return free space's device address 22 | */ 23 | struct device_address page_ftl_get_free_page(struct page_ftl *pgftl) 24 | { 25 | struct device_address paddr; 26 | struct device *dev; 27 | 28 | struct page_ftl_segment *segment; 29 | 30 | size_t nr_segments; 31 | size_t pages_per_segment; 32 | size_t segnum; 33 | size_t idx; 34 | 35 | uint64_t nr_free_pages; 36 | uint64_t nr_valid_pages; 37 | uint32_t page; 38 | 39 | dev = pgftl->dev; 40 | nr_segments = device_get_nr_segments(dev); 41 | pages_per_segment = device_get_pages_per_segment(dev); 42 | 43 | paddr.lpn = PADDR_EMPTY; 44 | idx = 0; 45 | 46 | retry: 47 | if (idx == nr_segments) { 48 | pr_err("cannot find the free page in the device\n"); 49 | paddr.lpn = PADDR_EMPTY; 50 | return paddr; 51 | } 52 | segnum = ((size_t)pgftl->alloc_segnum + idx) % nr_segments; 53 | idx += 1; 54 | 55 | if (dev->badseg_bitmap && get_bit(dev->badseg_bitmap, segnum)) { 56 | goto retry; 57 | } 58 | 59 | segment = &pgftl->segments[segnum]; 60 | if (segment == NULL) { 61 | pr_err("fatal error detected: cannot find the segnum %zu\n", 62 | segnum); 63 | paddr.lpn = PADDR_EMPTY; 64 | return paddr; 65 | } 66 | nr_free_pages = (uint64_t)g_atomic_int_get(&segment->nr_free_pages); 67 | if (nr_free_pages == 0) { 68 | goto retry; 69 | } 70 | pgftl->alloc_segnum = segnum; 71 | 72 | page = (uint32_t)find_first_zero_bit(segment->use_bits, 73 | pages_per_segment, 0); 74 | if (page == (uint32_t)BITS_NOT_FOUND) { 75 | pr_warn("nr_free_pages and use_bits bitmap are not synchronized(nr_free_pages: %" PRIu64 76 | ", page: %u)\n", 77 | nr_free_pages, page); 78 | goto retry; 79 | } 80 | paddr.lpn = 0; 81 | paddr.format.block = (uint16_t)segnum; 82 | paddr.lpn |= page; 83 | 84 | set_bit(segment->use_bits, page); 85 | g_atomic_int_set(&segment->nr_free_pages, (gint)nr_free_pages - 1); 86 | 87 | nr_valid_pages = (uint64_t)g_atomic_int_get(&segment->nr_valid_pages); 88 | g_atomic_int_set(&segment->nr_valid_pages, (gint)nr_valid_pages + 1); 89 | 90 | return paddr; 91 | } 92 | 93 | /** 94 | * @brief update the mapping information 95 | * 96 | * @param pgftl pointer of the page FTL structure 97 | * @param sector logical address for mapping table 98 | * @param ppn physical address for mapping table 99 | * 100 | * @return 0 to success, negative number to fail 101 | */ 102 | int page_ftl_update_map(struct page_ftl *pgftl, size_t sector, uint32_t ppn) 103 | { 104 | uint32_t *trans_map; 105 | uint64_t lpn; 106 | size_t map_size; 107 | 108 | lpn = page_ftl_get_lpn(pgftl, sector); 109 | 110 | map_size = page_ftl_get_map_size(pgftl) / sizeof(uint32_t); 111 | if (lpn >= (uint64_t)map_size) { 112 | pr_err("lpn value overflow detected (max: %zu, cur: %" PRIu64 113 | ")\n", 114 | map_size, lpn); 115 | return -EINVAL; 116 | } 117 | 118 | trans_map = pgftl->trans_map; 119 | trans_map[lpn] = ppn; 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /ftl/page/page-read.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file page-read.c 3 | * @brief read logic for page ftl 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-09-22 7 | */ 8 | #include "page.h" 9 | #include "log.h" 10 | #include "lru.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | /** 17 | * @brief read's end request function 18 | * 19 | * @param request the request which is submitted before 20 | */ 21 | static void page_ftl_read_end_rq(struct device_request *read_rq) 22 | { 23 | struct device_request *request; 24 | struct page_ftl *pgftl; 25 | size_t offset; 26 | 27 | request = (struct device_request *)read_rq->rq_private; 28 | pgftl = (struct page_ftl *)request->rq_private; 29 | offset = page_ftl_get_page_offset(pgftl, request->sector); 30 | 31 | memcpy(request->data, &((char *)read_rq->data)[offset], 32 | request->data_len); 33 | free(read_rq->data); 34 | device_free_request(read_rq); 35 | 36 | pthread_mutex_lock(&request->mutex); 37 | if (g_atomic_int_get(&request->is_finish) == 0) { 38 | pthread_cond_signal(&request->cond); 39 | } 40 | g_atomic_int_set(&request->is_finish, 1); 41 | pthread_mutex_unlock(&request->mutex); 42 | } 43 | 44 | /** 45 | * @brief the core logic for reading the request to the device. 46 | * 47 | * @param pgftl pointer of the page FTL structure 48 | * @param request user's request pointer 49 | * 50 | * @return reading data size. a negative number means fail to read. 51 | * @note 52 | * if paddr.lpn doesn't exist, this function returns the buffer filled 0 value. 53 | */ 54 | ssize_t page_ftl_read(struct page_ftl *pgftl, struct device_request *request) 55 | { 56 | struct device *dev; 57 | struct device_request *read_rq; 58 | struct device_address paddr; 59 | 60 | char *buffer; 61 | 62 | size_t page_size; 63 | size_t lpn, offset; 64 | 65 | ssize_t ret = 0; 66 | ssize_t data_len; 67 | 68 | buffer = NULL; 69 | read_rq = NULL; 70 | 71 | dev = pgftl->dev; 72 | page_size = device_get_page_size(dev); 73 | lpn = page_ftl_get_lpn(pgftl, request->sector); 74 | offset = page_ftl_get_page_offset(pgftl, request->sector); 75 | 76 | pthread_mutex_lock(&pgftl->mutex); 77 | paddr.lpn = pgftl->trans_map[lpn]; 78 | pthread_mutex_unlock(&pgftl->mutex); 79 | 80 | if (paddr.lpn == PADDR_EMPTY) { /**< YOU MUST TAKE CARE OF THIS LINE */ 81 | pr_warn("cannot find the mapping information (lpn: %zu)\n", 82 | lpn); 83 | memset(request->data, 0, request->data_len); 84 | ret = request->data_len; 85 | device_free_request(request); 86 | goto exception; 87 | } 88 | 89 | request->rq_private = pgftl; 90 | 91 | if (offset + request->data_len > page_size) { 92 | pr_err("overflow the read data (offset: %zu, length: %zu)\n", 93 | offset, request->data_len); 94 | ret = -EINVAL; 95 | goto exception; 96 | } 97 | 98 | buffer = (char *)malloc(page_size); 99 | if (buffer == NULL) { 100 | pr_err("memory allocation failed\n"); 101 | ret = -ENOMEM; 102 | goto exception; 103 | } 104 | memset(buffer, 0, page_size); 105 | 106 | read_rq = device_alloc_request(DEVICE_DEFAULT_REQUEST); 107 | if (read_rq == NULL) { 108 | pr_err("request allocation failed\n"); 109 | ret = -ENOMEM; 110 | goto exception; 111 | } 112 | 113 | read_rq->flag = DEVICE_READ; 114 | read_rq->data = buffer; 115 | read_rq->data_len = page_size; 116 | read_rq->paddr = paddr; 117 | read_rq->rq_private = request; 118 | read_rq->end_rq = page_ftl_read_end_rq; 119 | 120 | data_len = request->data_len; 121 | ret = dev->d_op->read(dev, read_rq); 122 | if (ret < 0) { 123 | pr_err("device read failed (ppn: %u)\n", request->paddr.lpn); 124 | read_rq = NULL; 125 | buffer = NULL; 126 | goto exception; 127 | } 128 | 129 | pthread_mutex_lock(&request->mutex); 130 | while (g_atomic_int_get(&request->is_finish) == 0) { 131 | pthread_cond_wait(&request->cond, &request->mutex); 132 | } 133 | pthread_mutex_unlock(&request->mutex); 134 | 135 | device_free_request(request); 136 | 137 | ret = data_len; 138 | return ret; 139 | exception: 140 | if (buffer) { 141 | free(buffer); 142 | } 143 | if (read_rq) { 144 | device_free_request(read_rq); 145 | } 146 | return ret; 147 | } 148 | -------------------------------------------------------------------------------- /device/device.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file device.c 3 | * @brief implementation of the device 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-01 7 | */ 8 | #include "device.h" 9 | #include "log.h" 10 | #include "ramdisk.h" 11 | #ifdef DEVICE_USE_BLUEDBM 12 | #include "bluedbm.h" 13 | #endif 14 | #ifdef DEVICE_USE_ZONED 15 | #include "zone.h" 16 | #endif 17 | #ifdef DEVICE_USE_RASPBERRY 18 | #include "raspberry.h" 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /** 29 | * @brief initialize the submodule 30 | * 31 | * @param device pointer of the device structure 32 | * @param uint64_t contain the flag information 33 | */ 34 | static int (*submodule_init[])(struct device *, uint64_t) = { 35 | /* [RAMDISK_MODULE] = */ ramdisk_device_init, 36 | #ifdef DEVICE_USE_BLUEDBM 37 | /* [BULEDBM_MODULE] = */ bluedbm_device_init, 38 | #else 39 | /* [BULEDBM_MODULE] = */ NULL, 40 | #endif 41 | #ifdef DEVICE_USE_ZONED 42 | /* [ZONE_MODULE] = */ zone_device_init, 43 | #else 44 | /* [ZONE_MODULE] = */ NULL, 45 | #endif 46 | #ifdef DEVICE_USE_RASPBERRY 47 | /* [RASPBERRY_MODULE] = */ raspberry_device_init, 48 | #else 49 | /* [RASPBERRY_MODULE] = */ NULL, 50 | #endif 51 | }; 52 | 53 | /** 54 | * @brief dynamic allocate the device request 55 | * 56 | * @param flags flags for allocate the device request 57 | * 58 | * @return device_request pointer when it is allocated or NULL when it is not allocated 59 | */ 60 | struct device_request *device_alloc_request(uint64_t flags) 61 | { 62 | struct device_request *request; 63 | int ret = 0; 64 | (void)flags; 65 | 66 | request = 67 | (struct device_request *)malloc(sizeof(struct device_request)); 68 | if (request == NULL) { 69 | pr_err("request allocation failed\n"); 70 | return NULL; 71 | } 72 | memset(request, 0, sizeof(struct device_request)); 73 | ret = pthread_mutex_init(&request->mutex, NULL); 74 | if (ret) { 75 | pr_err("pthread mutex initialize failed\n"); 76 | errno = ret; 77 | return NULL; 78 | } 79 | 80 | ret = pthread_cond_init(&request->cond, NULL); 81 | if (ret) { 82 | pr_err("pthread conditional variable initialize failed\n"); 83 | errno = ret; 84 | return NULL; 85 | } 86 | 87 | g_atomic_int_set(&request->is_finish, 0); 88 | 89 | return request; 90 | } 91 | 92 | /** 93 | * @brief free pre-allocated device_request resource 94 | * 95 | * @param request pointer of the device request 96 | */ 97 | void device_free_request(struct device_request *request) 98 | { 99 | pthread_cond_destroy(&request->cond); 100 | pthread_mutex_destroy(&request->mutex); 101 | free(request); 102 | } 103 | 104 | /** 105 | * @brief initialize the device module 106 | * 107 | * @param modnum module's number 108 | * @param __dev device structure pointer (will be allocated) 109 | * @param flags initializing flag 110 | * 111 | * @return 0 for success, negative value for fail 112 | */ 113 | int device_module_init(const uint64_t modnum, struct device **__dev, 114 | uint64_t flags) 115 | { 116 | int ret; 117 | struct device *dev; 118 | dev = (struct device *)malloc(sizeof(struct device)); 119 | if (dev == NULL) { 120 | pr_err("memory allocation failed\n"); 121 | ret = -ENOMEM; 122 | goto exception; 123 | } 124 | memset(dev, 0, sizeof(struct device)); 125 | pthread_mutex_init(&dev->mutex, NULL); 126 | (void)flags; 127 | ret = submodule_init[modnum](dev, flags); 128 | if (ret) { 129 | pr_err("initialize the submodule failed(modnum:%" PRIu64 ")\n", 130 | modnum); 131 | goto exception; 132 | } 133 | 134 | dev->badseg_bitmap = NULL; 135 | 136 | *__dev = dev; 137 | return ret; 138 | exception: 139 | device_module_exit(dev); 140 | return ret; 141 | } 142 | 143 | /** 144 | * @brief deallocate the device module 145 | * 146 | * @param dev pointer of the device module's structure 147 | * 148 | * @return 0 for success, negative value for fail 149 | */ 150 | int device_module_exit(struct device *dev) 151 | { 152 | int ret = 0; 153 | assert(NULL != dev); 154 | if (dev->d_submodule_exit) { 155 | dev->d_submodule_exit(dev); 156 | dev->d_submodule_exit = NULL; 157 | } 158 | pthread_mutex_destroy(&dev->mutex); 159 | free(dev); 160 | return ret; 161 | } 162 | -------------------------------------------------------------------------------- /include/page.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file page.h 3 | * @brief declaration of data structures and macros for page ftl 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-09-22 7 | */ 8 | #ifndef PAGE_H 9 | #define PAGE_H 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "flash.h" 21 | #include "device.h" 22 | 23 | // #define PAGE_FTL_USE_CACHE 24 | #define PAGE_FTL_CACHE_SIZE ((1 << 10)) 25 | #define PAGE_FTL_GC_RATIO \ 26 | ((double)10 / \ 27 | 100) /**< maximum the number of segments garbage collected */ 28 | #define PAGE_FTL_GC_ALL ((double)1) /**< collect all dirty segments */ 29 | #define PAGE_FTL_GC_THRESHOLD \ 30 | ((double)20 / \ 31 | 100) /**< gc triggered when number of the free pages under threshold */ 32 | 33 | enum { 34 | PAGE_FTL_IOCTL_TRIM = 0, 35 | }; 36 | 37 | /** 38 | * @brief segment information structure 39 | * @note 40 | * Segment number is same as block number 41 | */ 42 | struct page_ftl_segment { 43 | gint nr_free_pages; 44 | gint nr_valid_pages; 45 | gint is_gc; 46 | 47 | uint64_t *use_bits; /**< contain the use page information */ 48 | GList *lpn_list; /**< lba_list which contains the valid data */ 49 | }; 50 | 51 | /** 52 | * @brief contain the page flash translation layer information 53 | */ 54 | struct page_ftl { 55 | uint32_t *trans_map; /**< page-level mapping table */ 56 | uint64_t alloc_segnum; /**< last allocated segment number */ 57 | struct page_ftl_segment *segments; 58 | struct device *dev; 59 | pthread_mutex_t mutex; 60 | pthread_mutex_t gc_mutex; 61 | pthread_rwlock_t *bus_rwlock; 62 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 63 | pthread_rwlock_t rwlock; 64 | #endif 65 | pthread_t gc_thread; 66 | int o_flags; 67 | 68 | GList *gc_list; /**< garbage collection target list */ 69 | uint64_t *gc_seg_bits; /**< to find segnum is in gc list or not */ 70 | }; 71 | 72 | /* page-interface.c */ 73 | int page_ftl_open(struct page_ftl *, const char *name, int flags); 74 | int page_ftl_close(struct page_ftl *); 75 | 76 | ssize_t page_ftl_submit_request(struct page_ftl *, struct device_request *); 77 | ssize_t page_ftl_write(struct page_ftl *, struct device_request *); 78 | ssize_t page_ftl_read(struct page_ftl *, struct device_request *); 79 | 80 | int page_ftl_module_init(struct flash_device *, uint64_t flags); 81 | int page_ftl_module_exit(struct flash_device *); 82 | 83 | /* page-map.c */ 84 | struct device_address page_ftl_get_free_page(struct page_ftl *); 85 | int page_ftl_update_map(struct page_ftl *, size_t sector, uint32_t ppn); 86 | 87 | /* page-core.c */ 88 | int page_ftl_segment_data_init(struct page_ftl *, struct page_ftl_segment *); 89 | 90 | /* page-gc.c */ 91 | ssize_t page_ftl_do_gc(struct page_ftl *); 92 | ssize_t page_ftl_gc_from_list(struct page_ftl *, struct device_request *, 93 | double gc_ratio); 94 | 95 | static inline size_t page_ftl_get_map_size(struct page_ftl *pgftl) 96 | { 97 | struct device *dev = pgftl->dev; 98 | return ((device_get_total_size(dev) / device_get_page_size(dev)) + 1) * 99 | sizeof(uint32_t); 100 | } 101 | static inline size_t page_ftl_get_lpn(struct page_ftl *pgftl, size_t sector) 102 | { 103 | return sector / device_get_page_size(pgftl->dev); 104 | } 105 | 106 | static inline size_t page_ftl_get_page_offset(struct page_ftl *pgftl, 107 | size_t sector) 108 | { 109 | return sector % device_get_page_size(pgftl->dev); 110 | } 111 | 112 | static inline size_t page_ftl_get_segment_number(struct page_ftl *pgftl, 113 | uintptr_t segment) 114 | { 115 | return (segment - (uintptr_t)pgftl->segments) / 116 | sizeof(struct page_ftl_segment); 117 | } 118 | 119 | static inline size_t page_ftl_get_free_pages(struct page_ftl *pgftl) 120 | { 121 | size_t free_pages; 122 | size_t nr_segments, segnum; 123 | struct page_ftl_segment *segment; 124 | 125 | nr_segments = device_get_nr_segments(pgftl->dev); 126 | 127 | free_pages = 0; 128 | for (segnum = 0; segnum < nr_segments; segnum++) { 129 | segment = &pgftl->segments[segnum]; 130 | assert(NULL != segment); 131 | free_pages += (size_t)g_atomic_int_get(&segment->nr_free_pages); 132 | } 133 | return free_pages; 134 | } 135 | #endif 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flash Translation Layer 2 | 3 | ![build-and-test](https://github.com/BlaCkinkGJ/Flash-Translation-Layer/actions/workflows//build.yml/badge.svg)[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9b16f37d8a314e14a049312b5cfad674)](https://www.codacy.com/gh/BlaCkinkGJ/Flash-Translation-Layer/dashboard?utm_source=github.com&utm_medium=referral&utm_content=BlaCkinkGJ/Flash-Translation-Layer&utm_campaign=Badge_Grade) 4 | 5 | 6 | ## Overview 7 | 8 | This repository contains the simple Flash Translation Layers made on the Linux Environments. Furthermore, this repository works on the Ramdisk and various devices(e.g., Zoned Block Device, bluedbm, etc.). 9 | 10 | If you have any questions about this project, don't hesitate to contact the maintainer (ss5kijun@gmail.com). 11 | 12 | ## Build 13 | 14 | > These instructions are based on the Ubuntu(>=16.04) environments 15 | 16 | ### Prerequisite 17 | 18 | Before you build this repository, you must install some packages from the package manager. 19 | 20 | ```bash 21 | sudo apt update -y 22 | sudo apt install -y git make gcc g++ libglib2.0-dev libiberty-dev 23 | ``` 24 | 25 | After you download the packages, you must receive this project code using `git clone` like below. 26 | 27 | ``` 28 | git clone --recursive ${REPOSITORY_URL} 29 | ``` 30 | 31 | Now you move to this repository's project directory root by using `mv`. 32 | 33 | > If you want to use a module like the Zoned Block Device module, you must set the value 34 | > of the `USE_ZONE_DEVICE` variable to 1 in the `Makefile`. 35 | 36 | Additionally, you must install a valid library for each module. Each module's requirements are as follows. 37 | 38 | - zone: [libzbd](https://github.com/westerndigitalcorporation/libzbd) 39 | - bluedbm: [libmemio](https://github.com/pnuoslab/Flash-Board-Tester) 40 | 41 | > Moreover, before you run the Zoned Block Device-based program, 42 | > You must check that you give the super-user privileges to run 43 | > the Zoned Block Device-based programs. 44 | 45 | ### Test 46 | 47 | Before you execute the test and related things, you must install the below tools. 48 | 49 | - [flawfinder](https://dwheeler.com/flawfinder/) 50 | - [cppcheck](https://cppcheck.sourceforge.io/) 51 | - [lizard](https://github.com/terryyin/lizard) 52 | 53 | You can check the source code status by using `make check`. If you want to generate test files, execute the below command. 54 | 55 | ```bash 56 | make clean && make -j$(nproc) test USE_LOG_SILENT=1 57 | ``` 58 | 59 | After the build finish, you can get the various test files from the results. Run those files to test whether the project's module works correctly. 60 | 61 | ### Execution 62 | 63 | Suppose you want to generate a release program through the `main.c`, then you must execute the below commands. 64 | 65 | ```bash 66 | make clean && make -j$(nproc) 67 | ``` 68 | 69 | Now, you can run that program by using `./a.out`. Note that this repository is `main.c` file conducts the integration test of this project. 70 | 71 | ### Installation 72 | 73 | This project also supports generating the static library for using the external project. If you want to use it, please follow the below commands. 74 | 75 | ```bash 76 | make clean 77 | make -j$(nproc) 78 | sudo make install 79 | ``` 80 | ## For building in the macOS 81 | 82 | We are not providing native support for the macOS but providing Docker-based support for the macOS. 83 | 84 | First, you need to create the builder image for building this project. 85 | 86 | ```bash 87 | make docker-builder 88 | ``` 89 | 90 | After you create a docker image for building, run commands using `make docker-make-${TARGET_RULE}`. For example, you can use like: 91 | 92 | ```bash 93 | make docker-make-test # same as `make test` in the Linux 94 | make docker-make-integration-test # same as `make integration-test` in the Linux 95 | make docker-make-all 96 | make docker-make-check 97 | make docker-make-flow 98 | ``` 99 | 100 | ## Example 101 | 102 | After installing our shared library on your system, you can make your own program. You can refer to making your program from the `example` directory. 103 | 104 | You can run this example as follows: 105 | 106 | ```bash 107 | pushd example 108 | make 109 | ./rw_example 110 | make clean 111 | popd 112 | ``` 113 | 114 | ## Benchmark 115 | 116 | Build benchmark program by using: 117 | 118 | ```bash 119 | make benchmark.out 120 | ``` 121 | 122 | See its usage by using: 123 | 124 | ```bash 125 | ./benchmark.out -h 126 | ``` 127 | 128 | For example, if you want to see sequential write performance on the ramdisk, type like: 129 | 130 | ```bash 131 | ./benchmark.out -m pgftl -d ramdisk -t write -j 4 -b 1048576 -n 100 132 | ``` 133 | 134 | It shows results like: 135 | 136 | ```bash 137 | INFO:[interface/module.c:module_init(49)] flash initialize success 138 | INFO:[interface/module.c:module_init(55)] submodule initialize success 139 | INFO:[device/ramdisk/ramdisk.c:ramdisk_open(60)] ramdisk generated (size: 1073741824 bytes) 140 | INFO:[device/ramdisk/ramdisk.c:ramdisk_open(77)] bitmap generated (size: 16392 bytes) 141 | [parameters] 142 | - modules pgftl 143 | - devices ramdisk 144 | - workloads write 145 | - jobs 4 146 | - block size 1048576 147 | - # of block 100 148 | - io size 100MiB 149 | - path (null) 150 | fill data start! 151 | ready to read! 152 | Processing: 100.00% [1829.49 MiB/s] 153 | finish thread 0 154 | finish thread 1 155 | finish thread 2 156 | finish thread 3 157 | [job information] 158 | id time(s) bw(MiB/s) iops avg(ms) max(ms) min(ms) 159 | ===== 160 | 0 0.0644 1552.6847 100 0.6440 2.0855 0.1686 161 | 1 0.0530 1886.6408 100 0.5300 2.0855 0.1624 162 | 2 0.0522 1915.5273 100 0.5220 2.1562 0.1530 163 | 3 0.0541 1847.9744 100 0.5411 2.1562 0.1530 164 | [crc status] 165 | crc check success 166 | INFO:[interface/module.c:module_exit(75)] submodule deallocates success 167 | INFO:[interface/module.c:module_exit(83)] flash deallocates success 168 | [parameters] 169 | - modules pgftl 170 | - devices ramdisk 171 | - workloads write 172 | - jobs 4 173 | - block size 1048576 174 | - # of block 100 175 | - io size 100MiB 176 | - path (null) 177 | ``` 178 | 179 | If you encounter a random-related error, please run commands as follows: 180 | 181 | ```bash 182 | make benchmark.out USE_LEGACY_RANDOM=1 183 | ``` 184 | 185 | ## How to get this project's documents 186 | 187 | You can get this program's documentation file by using `doxygen -s Doxyfile`. Also, you can get the flow of each function using `make flow`. 188 | -------------------------------------------------------------------------------- /ftl/page/page-write.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file page-write.c 3 | * @brief write logic for page ftl 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-09-22 7 | */ 8 | #include "module.h" 9 | #include "page.h" 10 | #include "device.h" 11 | #include "log.h" 12 | #include "lru.h" 13 | #include "bits.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | /** 24 | * @brief invalidate a segment that including to the given LPN 25 | * 26 | * @param pgftl pointer of the page FTL structure 27 | * @param lpn logical page address to invalidate 28 | */ 29 | static void page_ftl_invalidate(struct page_ftl *pgftl, size_t lpn) 30 | { 31 | struct page_ftl_segment *segment; 32 | struct device_address paddr; 33 | 34 | uint32_t segnum; 35 | size_t nr_valid_pages, nr_free_pages; 36 | 37 | /**< segment information update */ 38 | paddr.lpn = pgftl->trans_map[lpn]; 39 | segnum = paddr.format.block; 40 | segment = &pgftl->segments[segnum]; 41 | 42 | segment->lpn_list = 43 | g_list_remove(segment->lpn_list, GSIZE_TO_POINTER(lpn)); 44 | 45 | nr_valid_pages = g_atomic_int_get(&segment->nr_valid_pages); 46 | nr_free_pages = g_atomic_int_get(&segment->nr_free_pages); 47 | g_atomic_int_set(&segment->nr_valid_pages, 48 | (unsigned int)(nr_valid_pages - 1)); 49 | 50 | /**< global information update */ 51 | pgftl->trans_map[lpn] = PADDR_EMPTY; 52 | if (nr_free_pages == 0 && get_bit(pgftl->gc_seg_bits, segnum) != 1) { 53 | pgftl->gc_list = g_list_prepend(pgftl->gc_list, segment); 54 | set_bit(pgftl->gc_seg_bits, segnum); 55 | } 56 | } 57 | 58 | /** 59 | * @brief write's end request function 60 | * 61 | * @param request the request which is submitted before 62 | */ 63 | static void page_ftl_write_end_rq(struct device_request *request) 64 | { 65 | free(request->data); 66 | device_free_request(request); 67 | } 68 | 69 | /** 70 | * @brief read sequence for overwrite 71 | * 72 | * @param pgftl pointer of the page FTL 73 | * @param lpn logical page address which wants to overwrite 74 | * @param buffer a pointer to a buffer containing the result of the read 75 | * 76 | * @return reading data size. a negative number means fail to read 77 | * 78 | * @note 79 | * NAND-based storage doesn't allow to do overwrite. 80 | * Therefore, you must use the out-of-place update. So this logic is necessary. 81 | */ 82 | static ssize_t page_ftl_read_for_overwrite(struct page_ftl *pgftl, size_t lpn, 83 | void *buffer) 84 | { 85 | struct device *dev; 86 | struct device_request *read_rq; 87 | 88 | size_t page_size; 89 | ssize_t ret; 90 | 91 | dev = pgftl->dev; 92 | page_size = device_get_page_size(dev); 93 | 94 | read_rq = device_alloc_request(DEVICE_DEFAULT_REQUEST); 95 | if (read_rq == NULL) { 96 | pr_err("crete read request failed\n"); 97 | return -ENOMEM; 98 | } 99 | read_rq->flag = DEVICE_READ; 100 | read_rq->sector = lpn * page_size; 101 | read_rq->data_len = page_size; 102 | read_rq->data = buffer; 103 | ret = page_ftl_read(pgftl, read_rq); 104 | if (ret < 0) { 105 | pr_err("previous buffer read failed\n"); 106 | return -EFAULT; 107 | } 108 | return ret; 109 | } 110 | 111 | /** 112 | * @brief update the translation mapping table 113 | * 114 | * @param pgftl pointer of the page FTL 115 | * @param paddr written device address 116 | * @param sector logical sector number 117 | */ 118 | static void page_ftl_write_update_metadata(struct page_ftl *pgftl, 119 | struct device_address paddr, 120 | size_t sector) 121 | { 122 | struct page_ftl_segment *segment; 123 | 124 | size_t lpn; 125 | lpn = page_ftl_get_lpn(pgftl, sector); 126 | if (pgftl->trans_map[lpn] != PADDR_EMPTY) { 127 | page_ftl_invalidate(pgftl, lpn); 128 | pr_debug("invalidate address: %zu => %u\n", lpn, 129 | pgftl->trans_map[lpn]); 130 | } 131 | /**< segment information update */ 132 | segment = &pgftl->segments[paddr.format.block]; 133 | segment->lpn_list = 134 | g_list_prepend(segment->lpn_list, GSIZE_TO_POINTER(lpn)); 135 | 136 | /**< global information update */ 137 | page_ftl_update_map(pgftl, sector, paddr.lpn); 138 | 139 | pr_debug("new address: %zu => %u (seg: %u)\n", lpn, 140 | pgftl->trans_map[lpn], pgftl->trans_map[lpn] >> 13); 141 | pr_debug("%u/%u(free/valid)\n", 142 | g_atomic_int_get(&segment->nr_free_pages), 143 | g_atomic_int_get(&segment->nr_valid_pages)); 144 | } 145 | 146 | /** 147 | * @brief the core logic for writing the request to the device. 148 | * 149 | * @param pgftl pointer of the page FTL structure 150 | * @param request user's request pointer 151 | * 152 | * @return writing data size. a negative number means fail to write. 153 | */ 154 | ssize_t page_ftl_write(struct page_ftl *pgftl, struct device_request *request) 155 | { 156 | struct device *dev; 157 | struct device_address paddr; 158 | char *buffer; 159 | ssize_t ret; 160 | size_t page_size; 161 | 162 | size_t lpn, offset; 163 | size_t nr_entries; 164 | 165 | size_t write_size; 166 | size_t sector; 167 | 168 | int is_exist; 169 | 170 | dev = pgftl->dev; 171 | page_size = device_get_page_size(dev); 172 | write_size = request->data_len; 173 | 174 | sector = request->sector; 175 | lpn = page_ftl_get_lpn(pgftl, sector); 176 | offset = page_ftl_get_page_offset(pgftl, sector); 177 | 178 | nr_entries = page_ftl_get_map_size(pgftl) / sizeof(uint32_t); 179 | if (lpn > nr_entries) { 180 | pr_err("invalid lpn detected (lpn: %zu, max: %zu)\n", lpn, 181 | nr_entries); 182 | return -EINVAL; 183 | } 184 | 185 | if (offset + request->data_len > page_size) { 186 | pr_err("overflow the write data (offset: %zu, length: %zu)\n", 187 | offset, request->data_len); 188 | return -EINVAL; 189 | } 190 | 191 | pthread_mutex_lock(&pgftl->mutex); 192 | paddr = page_ftl_get_free_page(pgftl); /**< global data retrieve */ 193 | pthread_mutex_unlock(&pgftl->mutex); 194 | if (paddr.lpn == PADDR_EMPTY) { 195 | pr_err("cannot allocate the valid page from device\n"); 196 | return -EFAULT; 197 | } 198 | 199 | buffer = (char *)malloc(page_size); 200 | if (buffer == NULL) { 201 | pr_err("memory allocation failed\n"); 202 | return -ENOMEM; 203 | } 204 | memset(buffer, 0, page_size); 205 | pthread_mutex_lock(&pgftl->mutex); 206 | is_exist = pgftl->trans_map[lpn] != PADDR_EMPTY; 207 | pthread_mutex_unlock(&pgftl->mutex); 208 | if (is_exist) { 209 | ret = page_ftl_read_for_overwrite(pgftl, lpn, buffer); 210 | if (ret < 0) { 211 | pr_err("read failed (lpn:%zu)\n", lpn); 212 | return ret; 213 | } 214 | } 215 | memcpy(&buffer[offset], request->data, write_size); 216 | 217 | request->flag = DEVICE_WRITE; 218 | request->data = buffer; 219 | request->paddr = paddr; 220 | request->rq_private = (void *)pgftl; 221 | request->data_len = page_size; 222 | request->end_rq = page_ftl_write_end_rq; 223 | 224 | ret = dev->d_op->write(dev, request); 225 | if (ret != (ssize_t)device_get_page_size(dev)) { 226 | pr_err("device write failed (ppn: %u)\n", request->paddr.lpn); 227 | return ret; 228 | } 229 | 230 | pthread_mutex_lock(&pgftl->mutex); 231 | page_ftl_write_update_metadata(pgftl, paddr, sector); 232 | pthread_mutex_unlock(&pgftl->mutex); 233 | 234 | return write_size; 235 | } 236 | -------------------------------------------------------------------------------- /integration-test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.c 3 | * @brief main program for test the interface 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-09-22 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "module.h" 18 | #include "flash.h" 19 | #include "page.h" 20 | #include "log.h" 21 | #include "device.h" 22 | 23 | #define USE_FORCE_ERASE 24 | #define USE_RANDOM_WAIT 25 | // #define USE_DEBUG_PRINT 26 | 27 | // #define BLOCKSIZE_1MB 28 | #define BLOCKSIZE_4KB 29 | 30 | #define DEVICE_PATH "/dev/nvme0n2" 31 | #ifdef DEVICE_USE_RASPBERRY 32 | #define WRITE_SIZE ((size_t)64 * (size_t)(1 << 20)) // 64 MiB 33 | #else 34 | #define WRITE_SIZE ((size_t)128 * (size_t)(1 << 20)) // 128 MiB 35 | #endif 36 | #define NR_ERASE (10) 37 | #if defined(BLOCKSIZE_4KB) 38 | #define BLOCK_SIZE ((size_t)4 * (size_t)(1 << 10)) // 4 KiB 39 | #elif defined(BLOCKSIZE_1MB) 40 | #define BLOCK_SIZE ((size_t)(1 << 20)) // 1 MiB 41 | #endif 42 | 43 | int is_check[WRITE_SIZE / BLOCK_SIZE]; 44 | 45 | void *read_thread(void *data) 46 | { 47 | ssize_t buffer[BLOCK_SIZE / sizeof(size_t)]; 48 | struct flash_device *flash; 49 | off_t offset; 50 | 51 | offset = 0; 52 | flash = (struct flash_device *)data; 53 | 54 | while (offset < (off_t)WRITE_SIZE) { 55 | ssize_t ret; 56 | srand(((unsigned int)time(NULL) * (unsigned int)offset) % 57 | UINT_MAX); 58 | memset(buffer, 0, sizeof(buffer)); 59 | ret = flash->f_op->read(flash, (void *)buffer, BLOCK_SIZE, 60 | offset); 61 | if (ret < 0 || (offset > 0 && buffer[0] == 0)) { 62 | continue; 63 | } 64 | if (buffer[0] == -1) { 65 | continue; 66 | } 67 | assert(ret == BLOCK_SIZE); 68 | #ifdef USE_DEBUG_PRINT 69 | printf("%-12s: %-16zd(offset: %zu)\n", "read", buffer[0], 70 | offset); 71 | #endif 72 | is_check[(size_t)(*(ssize_t *)buffer) / BLOCK_SIZE] = 1; 73 | offset += BLOCK_SIZE; 74 | #ifdef USE_RANDOM_WAIT 75 | usleep((rand() % 500) + 1000); 76 | #endif 77 | } 78 | return NULL; 79 | } 80 | 81 | void *write_thread(void *data) 82 | { 83 | ssize_t buffer[BLOCK_SIZE / sizeof(ssize_t)]; 84 | struct flash_device *flash; 85 | off_t offset; 86 | 87 | offset = 0; 88 | flash = (struct flash_device *)data; 89 | 90 | while (offset < (off_t)WRITE_SIZE) { 91 | ssize_t ret; 92 | srand((((unsigned int)time(NULL) * (unsigned int)offset) + 1) % 93 | UINT_MAX); 94 | memset(buffer, 0, sizeof(buffer)); 95 | buffer[0] = (ssize_t)offset; 96 | ret = flash->f_op->write(flash, (void *)buffer, BLOCK_SIZE, 97 | offset); 98 | if (ret < 0) { 99 | pr_err("write failed (offset: %zu)\n", (size_t)offset); 100 | } 101 | #ifdef USE_DEBUG_PRINT 102 | printf("%-12s: %-16zd(offset: %zu)\n", "write", buffer[0], 103 | (size_t)offset); 104 | #endif 105 | assert(ret == BLOCK_SIZE); 106 | offset += BLOCK_SIZE; 107 | #ifdef USE_RANDOM_WAIT 108 | usleep((rand() % 500) + 100); 109 | #endif 110 | } 111 | return NULL; 112 | } 113 | 114 | gint is_overwrite = 0; 115 | 116 | void *overwrite_thread(void *data) 117 | { 118 | ssize_t buffer[BLOCK_SIZE / sizeof(ssize_t)]; 119 | struct flash_device *flash; 120 | off_t offset; 121 | 122 | offset = 0; 123 | flash = (struct flash_device *)data; 124 | 125 | g_atomic_int_set(&is_overwrite, 1); 126 | 127 | sleep(2); 128 | while (offset < (off_t)WRITE_SIZE) { 129 | ssize_t ret; 130 | srand((((unsigned int)time(NULL) * (unsigned int)offset) + 2) % 131 | UINT_MAX); 132 | memset(buffer, 0, sizeof(buffer)); 133 | buffer[0] = (ssize_t)offset; 134 | ret = flash->f_op->write(flash, (void *)buffer, BLOCK_SIZE, 135 | offset); 136 | if (ret < 0) { 137 | pr_err("overwrite failed (offset: %zu)\n", 138 | (size_t)offset); 139 | } 140 | #ifdef USE_DEBUG_PRINT 141 | printf("%-12s: %-16zd(offset: %zu)\n", "overwrite", buffer[0], 142 | (size_t)offset); 143 | #endif 144 | offset += BLOCK_SIZE; 145 | #ifdef USE_RANDOM_WAIT 146 | usleep((rand() % 500) + 100); 147 | #endif 148 | } 149 | return NULL; 150 | } 151 | 152 | void *erase_thread(void *data) 153 | { 154 | #ifdef USE_FORCE_ERASE 155 | struct flash_device *flash; 156 | int i; 157 | flash = (struct flash_device *)data; 158 | while (!g_atomic_int_get(&is_overwrite)) { 159 | usleep(100); 160 | } 161 | for (i = 0; i < NR_ERASE; i++) { 162 | usleep(1000 * 1000); 163 | flash->f_op->ioctl(flash, PAGE_FTL_IOCTL_TRIM); 164 | #ifdef USE_DEBUG_PRINT 165 | printf("\tforced garbage collection!\n"); 166 | #endif 167 | } 168 | #endif 169 | (void)data; 170 | 171 | return NULL; 172 | } 173 | 174 | int main(void) 175 | { 176 | pthread_t threads[6]; // write, write, read, read, overwrite, erase; 177 | int thread_id; 178 | int is_all_valid; 179 | size_t status; 180 | size_t i; 181 | struct flash_device *flash = NULL; 182 | 183 | memset(is_check, 0, sizeof(is_check)); 184 | #if defined(DEVICE_USE_ZONED) 185 | assert(0 == module_init(PAGE_FTL_MODULE, &flash, ZONE_MODULE)); 186 | #elif defined(DEVICE_USE_BLUEDBM) 187 | assert(0 == module_init(PAGE_FTL_MODULE, &flash, BLUEDBM_MODULE)); 188 | #elif defined(DEVICE_USE_RASPBERRY) 189 | assert(0 == module_init(PAGE_FTL_MODULE, &flash, RASPBERRY_MODULE)); 190 | #else 191 | assert(0 == module_init(PAGE_FTL_MODULE, &flash, RAMDISK_MODULE)); 192 | #endif 193 | assert(0 == flash->f_op->open(flash, DEVICE_PATH, O_CREAT | O_RDWR)); 194 | thread_id = 195 | pthread_create(&threads[0], NULL, write_thread, (void *)flash); 196 | if (thread_id < 0) { 197 | perror("thread create failed"); 198 | exit(errno); 199 | } 200 | thread_id = 201 | pthread_create(&threads[1], NULL, write_thread, (void *)flash); 202 | if (thread_id < 0) { 203 | perror("thread create failed"); 204 | exit(errno); 205 | } 206 | thread_id = 207 | pthread_create(&threads[2], NULL, read_thread, (void *)flash); 208 | if (thread_id < 0) { 209 | perror("thread create failed"); 210 | exit(errno); 211 | } 212 | thread_id = 213 | pthread_create(&threads[3], NULL, read_thread, (void *)flash); 214 | if (thread_id < 0) { 215 | perror("thread create failed"); 216 | exit(errno); 217 | } 218 | thread_id = pthread_create(&threads[4], NULL, overwrite_thread, 219 | (void *)flash); 220 | if (thread_id < 0) { 221 | perror("thread create failed"); 222 | exit(errno); 223 | } 224 | thread_id = 225 | pthread_create(&threads[5], NULL, erase_thread, (void *)flash); 226 | if (thread_id < 0) { 227 | perror("thread create failed"); 228 | exit(errno); 229 | } 230 | 231 | pthread_join(threads[0], (void **)&status); 232 | pthread_join(threads[1], (void **)&status); 233 | pthread_join(threads[2], (void **)&status); 234 | pthread_join(threads[3], (void **)&status); 235 | pthread_join(threads[4], (void **)&status); 236 | pthread_join(threads[5], (void **)&status); 237 | 238 | flash->f_op->close(flash); 239 | assert(0 == module_exit(flash)); 240 | 241 | is_all_valid = 1; 242 | for (i = 0; i < sizeof(is_check) / sizeof(int); i++) { 243 | if (!is_check[i]) { 244 | printf("read failed offset: %-20zu\n", 245 | (i * BLOCK_SIZE)); 246 | is_all_valid = 0; 247 | } 248 | } 249 | if (is_all_valid) { 250 | printf("all data exist => FINISH\n"); 251 | } 252 | 253 | return 0; 254 | } 255 | -------------------------------------------------------------------------------- /util/lru.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lru.c 3 | * @brief implementation of the lru cache 4 | * @author Gijun Oh 5 | * @version 0.1 6 | * @date 2021-09-30 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "log.h" 15 | #include "lru.h" 16 | 17 | /** 18 | * @brief initialize the LRU cache data strcture 19 | * 20 | * @param capacity number of the entries to insert the LRU 21 | * @param deallocate deallcation function for LRU's value 22 | * 23 | * @return initialized LRU cache data structure pointer 24 | */ 25 | struct lru_cache *lru_init(const size_t capacity, lru_dealloc_fn deallocate) 26 | { 27 | struct lru_cache *cache = NULL; 28 | if (capacity == 0) { 29 | pr_err("capacity is zero\n"); 30 | goto exception; 31 | } 32 | 33 | cache = (struct lru_cache *)malloc(sizeof(struct lru_cache)); 34 | if (cache == NULL) { 35 | pr_err("memory allocation failed\n"); 36 | goto exception; 37 | } 38 | 39 | cache->head = &cache->nil; 40 | cache->deallocate = deallocate; 41 | cache->capacity = capacity; 42 | cache->size = 0; 43 | 44 | cache->nil.next = &cache->nil; 45 | cache->nil.prev = &cache->nil; 46 | cache->nil.key = (uint64_t)-1; 47 | cache->nil.value = (uintptr_t)NULL; 48 | 49 | return cache; 50 | exception: 51 | lru_free(cache); 52 | return NULL; 53 | } 54 | 55 | /** 56 | * @brief allocate the single node 57 | * 58 | * @param key key for identify the node 59 | * @param value value for data in the node 60 | * 61 | * @return allocated node structure pointer 62 | */ 63 | static struct lru_node *lru_alloc_node(const uint64_t key, uintptr_t value) 64 | { 65 | struct lru_node *node; 66 | node = (struct lru_node *)malloc(sizeof(struct lru_node)); 67 | if (node == NULL) { 68 | pr_err("node allocation failed\n"); 69 | return NULL; 70 | } 71 | node->key = key; 72 | node->value = value; 73 | return node; 74 | } 75 | 76 | /** 77 | * @brief deallcate the allocated node 78 | * 79 | * @param node node which wants to deallocate 80 | */ 81 | static void lru_dealloc_node(struct lru_node *node) 82 | { 83 | assert(NULL != node); 84 | #if 0 85 | pr_debug("deallcate the node (key: %ld, value: %ld)\n", node->key, 86 | node->value); /**< Not recommend to print this line */ 87 | #endif 88 | free(node); 89 | } 90 | 91 | /** 92 | * @brief delete the node from the list 93 | * 94 | * @param head list's head pointer 95 | * @param deleted target which wants to delete 96 | * 97 | * @return 0 for successfully delete, -EINVAL means there is no data in list 98 | * 99 | * @note 100 | * This function doesn't deallocate the node and its data 101 | */ 102 | static int lru_delete_node(struct lru_node *head, struct lru_node *deleted) 103 | { 104 | assert(NULL != head); 105 | assert(NULL != deleted); 106 | 107 | if (head == deleted) { 108 | pr_debug( 109 | "deletion of head node is not permitted (node:%p, deleted:%p)\n", 110 | head, deleted); 111 | return -EINVAL; 112 | } 113 | deleted->prev->next = deleted->next; 114 | deleted->next->prev = deleted->prev; 115 | return 0; 116 | } 117 | 118 | /** 119 | * @brief implementation of the LRU eviction function 120 | * 121 | * @param cache LRU cache data structure pointer 122 | * 123 | * @return 0 for successfully evict 124 | */ 125 | static int __lru_do_evict(struct lru_cache *cache) 126 | { 127 | struct lru_node *head = cache->head; 128 | struct lru_node *target = head->prev; 129 | int ret = 0; 130 | lru_delete_node(head, target); 131 | if (cache->deallocate) { 132 | ret = cache->deallocate(target->key, target->value); 133 | } 134 | lru_dealloc_node(target); 135 | return ret; 136 | } 137 | 138 | /** 139 | * @brief interfaces for execute the eviction process 140 | * 141 | * @param cache LRU cache data structure pointer 142 | * @param nr_evict number of the entries to evict 143 | * 144 | * @return 0 for successfully evict 145 | */ 146 | static int lru_do_evict(struct lru_cache *cache, const uint64_t nr_evict) 147 | { 148 | int ret = 0; 149 | uint64_t i; 150 | for (i = 0; i < nr_evict; i++) { 151 | ret = __lru_do_evict(cache); 152 | if (ret) { 153 | return ret; 154 | } 155 | cache->size -= 1; 156 | } 157 | return ret; 158 | } 159 | 160 | /** 161 | * @brief insert the lru node to the list 162 | * 163 | * @param node pointer of the node, node->next will indicate the newnode 164 | * @param newnode newly allocated node to insert 165 | */ 166 | static void lru_node_insert(struct lru_node *node, struct lru_node *newnode) 167 | { 168 | assert(NULL != newnode); 169 | assert(NULL != node); 170 | 171 | newnode->prev = node; 172 | newnode->next = node->next; 173 | node->next->prev = newnode; 174 | node->next = newnode; 175 | } 176 | 177 | /** 178 | * @brief inser the key, value to the LRU cache 179 | * 180 | * @param cache LRU cache data structure pointer 181 | * @param key key which identifies the node 182 | * @param value value which contains the data 183 | * 184 | * @return 0 to success 185 | */ 186 | int lru_put(struct lru_cache *cache, const uint64_t key, uintptr_t value) 187 | { 188 | struct lru_node *head = cache->head; 189 | struct lru_node *node = NULL; 190 | assert(NULL != head); 191 | 192 | if (cache->size >= cache->capacity) { 193 | pr_debug("eviction is called (size: %zu, cap: %zu)\n", 194 | cache->size, cache->capacity); 195 | lru_do_evict(cache, lru_get_evict_size(cache)); 196 | } 197 | 198 | node = lru_alloc_node(key, value); 199 | if (node == NULL) { 200 | pr_err("memory allocation failed\n"); 201 | return -ENOMEM; 202 | } 203 | lru_node_insert(head, node); 204 | cache->size += 1; 205 | return 0; 206 | } 207 | 208 | /** 209 | * @brief find the node based on the key 210 | * 211 | * @param cache LRU cache data structure pointer 212 | * @param key key which identifies the node 213 | * 214 | * @return pointer of the node 215 | */ 216 | static struct lru_node *lru_find_node(struct lru_cache *cache, 217 | const uint64_t key) 218 | { 219 | struct lru_node *head = cache->head; 220 | struct lru_node *it = head->next; 221 | while (it != head) { 222 | if (it->key == key) { 223 | return it; 224 | } 225 | it = it->next; 226 | } 227 | return NULL; 228 | } 229 | 230 | /** 231 | * @brief get data from the LRU cache 232 | * 233 | * @param cache LRU cache data structrue pointer 234 | * @param key key which identifies the node 235 | * 236 | * @return data in the node's value 237 | */ 238 | uintptr_t lru_get(struct lru_cache *cache, const uint64_t key) 239 | { 240 | struct lru_node *head = cache->head; 241 | struct lru_node *node; 242 | 243 | uintptr_t value = (uintptr_t)NULL; 244 | 245 | node = lru_find_node(cache, key); 246 | if (node) { 247 | lru_delete_node(head, node); 248 | lru_node_insert(head, node); 249 | value = node->value; 250 | } 251 | return value; 252 | } 253 | 254 | /** 255 | * @brief deallocate the LRU cache structure 256 | * 257 | * @param cache LRU cache data structure pointer 258 | * 259 | * @return 0 to success 260 | */ 261 | int lru_free(struct lru_cache *cache) 262 | { 263 | int ret = 0; 264 | struct lru_node *head = NULL; 265 | struct lru_node *node = NULL; 266 | struct lru_node *next = NULL; 267 | if (!cache) { 268 | return ret; 269 | } 270 | head = cache->head; 271 | node = head->next; 272 | while (node != head) { 273 | assert(NULL != node); 274 | next = node->next; 275 | if (cache->deallocate) { 276 | ret = cache->deallocate(node->key, node->value); 277 | if (ret) { 278 | pr_err("deallocate failed (key: %" PRIu64 279 | ", value: %" PRIuPTR ")\n", 280 | node->key, node->value); 281 | return ret; 282 | } 283 | } 284 | free(node); 285 | node = next; 286 | } 287 | free(cache); 288 | return ret; 289 | } 290 | -------------------------------------------------------------------------------- /include/device.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file device.h 3 | * @brief contain the device information header 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-01 7 | */ 8 | #ifndef DEVICE_H 9 | #define DEVICE_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "log.h" /**< to use the TOSTRING */ 17 | 18 | #define PADDR_EMPTY ((uint32_t)UINT32_MAX) 19 | 20 | struct device_request; 21 | struct device_operations; 22 | 23 | #ifndef DEVICE_PAGE_SIZE 24 | #define DEVICE_PAGE_SIZE (8192) 25 | #endif 26 | 27 | /** 28 | * @brief request allocation flags 29 | */ 30 | enum { 31 | DEVICE_DEFAULT_REQUEST = 0, 32 | }; 33 | 34 | /** 35 | * @brief flash board I/O direction 36 | */ 37 | enum { 38 | DEVICE_WRITE = 0 /**< write flag */, 39 | DEVICE_READ /**< read flag */, 40 | DEVICE_ERASE /**< erase flag */, 41 | }; 42 | 43 | /** 44 | * @brief support module list 45 | */ 46 | enum { 47 | RAMDISK_MODULE = 0 /**< select the ramdisk module */, 48 | BLUEDBM_MODULE /**< select the bluedbm module */, 49 | ZONE_MODULE /**< select the zone module */, 50 | RASPBERRY_MODULE /**< select the raspberry module */, 51 | }; 52 | 53 | /** 54 | * @brief device address information 55 | * 56 | * @note 57 | * If you want to use the ZONE_MODULE, you must change the 58 | * DEVICE_NR_PAGES_BITS and DEVICE_NR_BLOCKS_BITS based on the zone's 59 | * size. 60 | */ 61 | #ifndef DEVICE_NR_BUS_BITS 62 | #define DEVICE_NR_BUS_BITS (3) 63 | #endif 64 | 65 | #ifndef DEVICE_NR_CHIPS_BITS 66 | #define DEVICE_NR_CHIPS_BITS (3) 67 | #endif 68 | 69 | #ifndef DEVICE_NR_PAGES_BITS 70 | #define DEVICE_NR_PAGES_BITS (7) 71 | #endif 72 | 73 | #ifndef DEVICE_NR_BLOCKS_BITS 74 | #define DEVICE_NR_BLOCKS_BITS (19) 75 | #endif 76 | 77 | #if 0 78 | #pragma message("DEVICE_NR_BUS_BITS = " TOSTRING(DEVICE_NR_BUS_BITS)) 79 | #pragma message("DEVICE_NR_CHIPS_BITS = " TOSTRING(DEVICE_NR_CHIPS_BITS)) 80 | #pragma message("DEVICE_NR_PAGES_BITS = " TOSTRING(DEVICE_NR_PAGES_BITS)) 81 | #pragma message("DEVICE_NR_BLOCKS_BITS = " TOSTRING(DEVICE_NR_BLOCKS_BITS)) 82 | #endif 83 | 84 | /** 85 | * @brief I/O end request function 86 | * 87 | * @param request device request structure's pointer 88 | * 89 | * @note 90 | * You must specify the call routine of this function in your custom device 91 | * module 92 | */ 93 | typedef void (*device_end_req_fn)(struct device_request *); 94 | 95 | /** 96 | * @brief generic device address format 97 | * 98 | * @note 99 | * `seqnum` is used for distinguish the each host pages in a device page 100 | */ 101 | struct device_address { 102 | union { 103 | struct { 104 | uint32_t bus : DEVICE_NR_BUS_BITS; 105 | uint32_t chip : DEVICE_NR_CHIPS_BITS; 106 | uint32_t page : DEVICE_NR_PAGES_BITS; 107 | uint32_t block : DEVICE_NR_BLOCKS_BITS; 108 | } format; 109 | struct { 110 | uint32_t bus : DEVICE_NR_BUS_BITS; 111 | uint32_t chip : DEVICE_NR_CHIPS_BITS; 112 | uint32_t page : DEVICE_NR_PAGES_BITS; 113 | uint32_t block : DEVICE_NR_BLOCKS_BITS; 114 | } raspberry_converter; 115 | struct { 116 | uint32_t page 117 | : (DEVICE_NR_PAGES_BITS + DEVICE_NR_CHIPS_BITS + 118 | DEVICE_NR_BUS_BITS); 119 | uint32_t block : 32 - 120 | (DEVICE_NR_PAGES_BITS + DEVICE_NR_CHIPS_BITS + 121 | DEVICE_NR_BUS_BITS); 122 | } raspberry; 123 | uint32_t lpn; 124 | }; 125 | }; 126 | 127 | /** 128 | * @brief request for device 129 | */ 130 | struct device_request { 131 | unsigned int flag; /**< flag describes the bio's direction */ 132 | 133 | size_t data_len; /**< data length (bytes) */ 134 | size_t sector; /**< sector cursor (bytes) */ 135 | struct device_address paddr; /**< this contains the ppa */ 136 | 137 | void *data; /**< pointer of the data */ 138 | device_end_req_fn end_rq; /**< end request function */ 139 | 140 | gint is_finish; 141 | 142 | pthread_mutex_t mutex; 143 | pthread_cond_t cond; 144 | 145 | void *rq_private; /**< contain the request's private data */ 146 | }; 147 | 148 | /** 149 | * @brief flash board's page information 150 | */ 151 | struct device_page { 152 | size_t size; /**< byte */ 153 | }; 154 | 155 | /** 156 | * @brief flash board's block information 157 | */ 158 | struct device_block { 159 | struct device_page page; 160 | size_t nr_pages; 161 | }; 162 | 163 | /** 164 | * @brief flash board's package(nand chip) information 165 | */ 166 | struct device_package { 167 | struct device_block block; 168 | size_t nr_blocks; 169 | }; 170 | 171 | /** 172 | * @brief flash board's architecture information 173 | */ 174 | struct device_info { 175 | struct device_package package; 176 | size_t nr_bus; /**< bus equal to channel */ 177 | size_t nr_chips; /**< chip equal to way */ 178 | }; 179 | 180 | /** 181 | * @brief metadata of the device 182 | */ 183 | struct device { 184 | pthread_mutex_t mutex; 185 | const struct device_operations *d_op; 186 | struct device_info info; 187 | uint64_t *badseg_bitmap; 188 | void *d_private; /**< generally contain the sub-layer's data structure */ 189 | int (*d_submodule_exit)(struct device *); 190 | }; 191 | 192 | /** 193 | * @brief operations for device 194 | */ 195 | struct device_operations { 196 | int (*open)(struct device *, const char *name, int flags); 197 | ssize_t (*write)(struct device *, struct device_request *); 198 | ssize_t (*read)(struct device *, struct device_request *); 199 | int (*erase)(struct device *, struct device_request *); 200 | int (*close)(struct device *); 201 | }; 202 | 203 | struct device_request *device_alloc_request(uint64_t flags); 204 | void device_free_request(struct device_request *); 205 | 206 | int device_module_init(const uint64_t modnum, struct device **, uint64_t flags); 207 | int device_module_exit(struct device *); 208 | 209 | /** 210 | * @brief get the number of segments in a flash board 211 | * 212 | * @param dev device structure pointer 213 | * 214 | * @return the number of segments in a flash board 215 | */ 216 | static inline size_t device_get_nr_segments(struct device *dev) 217 | { 218 | struct device_info *info = &dev->info; 219 | struct device_package *package = &info->package; 220 | return package->nr_blocks; 221 | } 222 | 223 | static inline size_t device_get_blocks_per_segment(struct device *dev) 224 | { 225 | struct device_info *info = &dev->info; 226 | return (info->nr_bus * info->nr_chips); 227 | } 228 | 229 | /** 230 | * @brief get the number of pages in a segment 231 | * 232 | * @param dev device structure pointer 233 | * 234 | * @return the number of pages in a segment 235 | */ 236 | static inline size_t device_get_pages_per_segment(struct device *dev) 237 | { 238 | struct device_info *info = &dev->info; 239 | struct device_package *package = &info->package; 240 | struct device_block *block = &package->block; 241 | 242 | return device_get_blocks_per_segment(dev) * block->nr_pages; 243 | } 244 | 245 | /** 246 | * @brief get flash board's NAND page size 247 | * 248 | * @param dev device structure pointer 249 | * 250 | * @return NAND page size (generally, 8192 or 4096) 251 | */ 252 | static inline size_t device_get_page_size(struct device *dev) 253 | { 254 | struct device_info *info = &dev->info; 255 | struct device_package *package = &info->package; 256 | struct device_block *block = &package->block; 257 | struct device_page *page = &block->page; 258 | return page->size; 259 | } 260 | 261 | /** 262 | * @brief total size of a flash board 263 | * 264 | * @param dev device structure pointer 265 | * 266 | * @return flash board's total size (byte) 267 | */ 268 | static inline size_t device_get_total_size(struct device *dev) 269 | { 270 | size_t nr_segments = device_get_nr_segments(dev); 271 | size_t nr_pages_per_segment = device_get_pages_per_segment(dev); 272 | size_t page_size = device_get_page_size(dev); 273 | 274 | return nr_segments * nr_pages_per_segment * page_size; 275 | } 276 | 277 | /** 278 | * @brief get total the number of pages in a flash board 279 | * 280 | * @param dev device structure pointer 281 | * 282 | * @return the number of pages in a flash board. 283 | */ 284 | static inline size_t device_get_total_pages(struct device *dev) 285 | { 286 | return device_get_total_size(dev) / device_get_page_size(dev); 287 | } 288 | 289 | #endif 290 | -------------------------------------------------------------------------------- /test/ramdisk-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ramdisk.h" 8 | #include "device.h" 9 | #include "unity.h" 10 | 11 | struct device *dev; 12 | 13 | void setUp(void) 14 | { 15 | int ret; 16 | ret = device_module_init(RAMDISK_MODULE, &dev, 0); 17 | TEST_ASSERT_EQUAL_INT(0, ret); 18 | } 19 | 20 | void tearDown(void) 21 | { 22 | TEST_ASSERT_NOT_NULL(dev); 23 | device_module_exit(dev); 24 | } 25 | 26 | void test_open_and_close(void) 27 | { 28 | TEST_ASSERT_NOT_NULL(dev->d_op); 29 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, NULL, O_CREAT | O_RDWR)); 30 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 31 | } 32 | 33 | void test_full_write(void) 34 | { 35 | struct device_request request; 36 | struct device_address addr; 37 | char *buffer; 38 | size_t page_size; 39 | size_t total_pages; 40 | 41 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, NULL, O_CREAT | O_RDWR)); 42 | page_size = device_get_page_size(dev); 43 | total_pages = device_get_total_pages(dev); 44 | 45 | buffer = (char *)malloc(page_size); 46 | TEST_ASSERT_NOT_NULL(buffer); 47 | memset(buffer, 0, page_size); 48 | 49 | /**< note that all I/O functions run synchronously */ 50 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 51 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 52 | request.paddr = addr; 53 | request.data_len = page_size; 54 | request.end_rq = NULL; 55 | request.flag = DEVICE_WRITE; 56 | request.sector = 0; 57 | request.data = buffer; 58 | TEST_ASSERT_EQUAL_INT(request.data_len, 59 | dev->d_op->write(dev, &request)); 60 | } 61 | 62 | memset(buffer, 0, page_size); 63 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 64 | request.paddr = addr; 65 | request.data_len = page_size; 66 | request.end_rq = NULL; 67 | request.flag = DEVICE_READ; 68 | request.sector = 0; 69 | request.data = buffer; 70 | TEST_ASSERT_EQUAL_INT(request.data_len, 71 | dev->d_op->read(dev, &request)); 72 | TEST_ASSERT_EQUAL_UINT32(addr.lpn, *(uint32_t *)request.data); 73 | } 74 | 75 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 76 | free(buffer); 77 | } 78 | 79 | void test_overwrite(void) 80 | { 81 | struct device_request request; 82 | struct device_address addr; 83 | char *buffer; 84 | size_t page_size; 85 | size_t total_pages; 86 | 87 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, NULL, O_CREAT | O_RDWR)); 88 | page_size = device_get_page_size(dev); 89 | total_pages = device_get_total_pages(dev); 90 | buffer = (char *)malloc(page_size); 91 | TEST_ASSERT_NOT_NULL(buffer); 92 | memset(buffer, 0, page_size); 93 | 94 | /**< note that all I/O functions run synchronously */ 95 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 96 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 97 | request.paddr = addr; 98 | request.data_len = page_size; 99 | request.end_rq = NULL; 100 | request.flag = DEVICE_WRITE; 101 | request.sector = 0; 102 | request.data = buffer; 103 | TEST_ASSERT_EQUAL_INT(request.data_len, 104 | dev->d_op->write(dev, &request)); 105 | } 106 | 107 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 108 | request.paddr = addr; 109 | request.data_len = page_size; 110 | request.end_rq = NULL; 111 | request.flag = DEVICE_WRITE; 112 | request.sector = 0; 113 | request.data = buffer; 114 | TEST_ASSERT_EQUAL_INT(-EINVAL, dev->d_op->write(dev, &request)); 115 | } 116 | 117 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 118 | free(buffer); 119 | } 120 | 121 | void test_erase(void) 122 | { 123 | struct device_request request; 124 | struct device_address addr; 125 | char *buffer; 126 | size_t page_size; 127 | size_t total_pages; 128 | size_t nr_segments; 129 | size_t nr_pages_per_segment; 130 | uint16_t segnum; 131 | 132 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, NULL, O_CREAT | O_RDWR)); 133 | page_size = device_get_page_size(dev); 134 | total_pages = device_get_total_pages(dev); 135 | buffer = (char *)malloc(page_size); 136 | TEST_ASSERT_NOT_NULL(buffer); 137 | memset(buffer, 0, page_size); 138 | 139 | /**< note that all I/O functions run synchronously */ 140 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 141 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 142 | request.paddr = addr; 143 | request.data_len = page_size; 144 | request.end_rq = NULL; 145 | request.flag = DEVICE_WRITE; 146 | request.sector = 0; 147 | request.data = buffer; 148 | TEST_ASSERT_EQUAL_INT(request.data_len, 149 | dev->d_op->write(dev, &request)); 150 | } 151 | 152 | nr_segments = device_get_nr_segments(dev); 153 | for (segnum = 0; segnum < nr_segments - 1; segnum++) { 154 | addr.lpn = 0; 155 | addr.format.block = segnum; 156 | request.paddr = addr; 157 | request.flag = DEVICE_ERASE; 158 | request.end_rq = NULL; 159 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->erase(dev, &request)); 160 | }; 161 | 162 | nr_pages_per_segment = device_get_pages_per_segment(dev); 163 | for (addr.lpn = 0; addr.lpn < total_pages - nr_pages_per_segment; 164 | addr.lpn++) { 165 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 166 | request.paddr = addr; 167 | request.data_len = page_size; 168 | request.end_rq = NULL; 169 | request.flag = DEVICE_WRITE; 170 | request.sector = 0; 171 | request.data = buffer; 172 | TEST_ASSERT_EQUAL_INT(request.data_len, 173 | dev->d_op->write(dev, &request)); 174 | } 175 | 176 | for (; addr.lpn < total_pages; addr.lpn++) { 177 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 178 | request.paddr = addr; 179 | request.data_len = page_size; 180 | request.end_rq = NULL; 181 | request.flag = DEVICE_WRITE; 182 | request.sector = 0; 183 | request.data = buffer; 184 | TEST_ASSERT_EQUAL_INT(-EINVAL, dev->d_op->write(dev, &request)); 185 | } 186 | 187 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 188 | free(buffer); 189 | } 190 | 191 | static void end_rq(struct device_request *request) 192 | { 193 | struct device_address paddr = request->paddr; 194 | uint8_t *is_check = (uint8_t *)request->rq_private; 195 | is_check[paddr.lpn] = 1; 196 | } 197 | 198 | void test_end_rq_works(void) 199 | { 200 | struct device_request request; 201 | struct device_address addr; 202 | char *buffer; 203 | uint8_t *is_check; 204 | size_t page_size; 205 | size_t total_pages; 206 | uint16_t segnum; 207 | size_t nr_segments; 208 | 209 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, NULL, O_CREAT | O_RDWR)); 210 | page_size = device_get_page_size(dev); 211 | total_pages = device_get_total_pages(dev); 212 | nr_segments = device_get_nr_segments(dev); 213 | 214 | buffer = (char *)malloc(page_size); 215 | TEST_ASSERT_NOT_NULL(buffer); 216 | memset(buffer, 0, page_size); 217 | 218 | is_check = (uint8_t *)malloc(total_pages); 219 | TEST_ASSERT_NOT_NULL(is_check); 220 | memset(is_check, 0, total_pages); 221 | 222 | /**< note that all I/O functions run synchronously */ 223 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 224 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 225 | request.paddr = addr; 226 | request.data_len = page_size; 227 | request.end_rq = end_rq; 228 | request.flag = DEVICE_WRITE; 229 | request.sector = 0; 230 | request.data = buffer; 231 | request.rq_private = (void *)is_check; 232 | TEST_ASSERT_EQUAL_INT(request.data_len, 233 | dev->d_op->write(dev, &request)); 234 | } 235 | 236 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 237 | TEST_ASSERT_EQUAL_INT(1, is_check[addr.lpn]); 238 | } 239 | 240 | memset(is_check, 0, total_pages); 241 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 242 | request.paddr = addr; 243 | request.data_len = page_size; 244 | request.end_rq = end_rq; 245 | request.flag = DEVICE_READ; 246 | request.sector = 0; 247 | request.data = buffer; 248 | TEST_ASSERT_EQUAL_INT(request.data_len, 249 | dev->d_op->read(dev, &request)); 250 | TEST_ASSERT_EQUAL_UINT32(addr.lpn, *(uint32_t *)request.data); 251 | } 252 | 253 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 254 | TEST_ASSERT_EQUAL_INT(1, is_check[addr.lpn]); 255 | } 256 | 257 | memset(is_check, 0, total_pages); 258 | for (segnum = 0; segnum < nr_segments; segnum++) { 259 | addr.lpn = 0; 260 | addr.format.block = segnum; 261 | request.paddr = addr; 262 | request.flag = DEVICE_ERASE; 263 | request.end_rq = end_rq; 264 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->erase(dev, &request)); 265 | }; 266 | 267 | for (segnum = 0; segnum < nr_segments; segnum++) { 268 | addr.lpn = 0; 269 | addr.format.block = segnum; 270 | TEST_ASSERT_EQUAL_INT(1, is_check[addr.lpn]); 271 | } 272 | 273 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 274 | free(buffer); 275 | free(is_check); 276 | } 277 | 278 | int main(void) 279 | { 280 | UNITY_BEGIN(); 281 | RUN_TEST(test_open_and_close); 282 | RUN_TEST(test_full_write); 283 | RUN_TEST(test_overwrite); 284 | RUN_TEST(test_erase); 285 | RUN_TEST(test_end_rq_works); 286 | return UNITY_END(); 287 | } 288 | -------------------------------------------------------------------------------- /test/zone-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "device.h" 4 | #include "zone.h" 5 | #include "unity.h" 6 | 7 | #define ZBD_FILE_NAME "/dev/nvme0n2" 8 | 9 | // #define WRITE_PAGE_SIZE(x) (device_get_total_pages(x)) 10 | #define WRITE_PAGE_SIZE(x) (device_get_pages_per_segment(x) * 5) 11 | 12 | struct device *dev; 13 | 14 | void setUp(void) 15 | { 16 | int ret; 17 | ret = device_module_init(ZONE_MODULE, &dev, 0); 18 | TEST_ASSERT_EQUAL_INT(0, ret); 19 | } 20 | 21 | void tearDown(void) 22 | { 23 | TEST_ASSERT_NOT_NULL(dev); 24 | device_module_exit(dev); 25 | } 26 | 27 | void test_open(void) 28 | { 29 | int ret; 30 | ret = dev->d_op->open(dev, ZBD_FILE_NAME, O_CREAT | O_RDWR); 31 | TEST_ASSERT_EQUAL_INT(0, ret); 32 | ret = dev->d_op->close(dev); 33 | TEST_ASSERT_EQUAL_INT(0, ret); 34 | } 35 | 36 | void test_full_write(void) 37 | { 38 | struct device_request request; 39 | struct device_address addr; 40 | char *buffer; 41 | size_t page_size; 42 | size_t total_pages; 43 | 44 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, ZBD_FILE_NAME, 45 | O_CREAT | O_RDWR)); 46 | page_size = device_get_page_size(dev); 47 | total_pages = WRITE_PAGE_SIZE(dev); 48 | 49 | buffer = (char *)malloc(page_size); 50 | TEST_ASSERT_NOT_NULL(buffer); 51 | memset(buffer, 0, page_size); 52 | 53 | /**< note that all I/O functions run synchronously */ 54 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 55 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 56 | request.paddr = addr; 57 | request.data_len = page_size; 58 | request.end_rq = NULL; 59 | request.flag = DEVICE_WRITE; 60 | request.sector = 0; 61 | request.data = buffer; 62 | TEST_ASSERT_EQUAL_INT(request.data_len, 63 | dev->d_op->write(dev, &request)); 64 | } 65 | 66 | memset(buffer, 0, page_size); 67 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 68 | request.paddr = addr; 69 | request.data_len = page_size; 70 | request.end_rq = NULL; 71 | request.flag = DEVICE_READ; 72 | request.sector = 0; 73 | request.data = buffer; 74 | TEST_ASSERT_EQUAL_INT(request.data_len, 75 | dev->d_op->read(dev, &request)); 76 | TEST_ASSERT_EQUAL_UINT32(addr.lpn, *(uint32_t *)request.data); 77 | } 78 | 79 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 80 | free(buffer); 81 | } 82 | 83 | void test_overwrite(void) 84 | { 85 | struct device_request request; 86 | struct device_address addr; 87 | char *buffer; 88 | size_t page_size; 89 | size_t total_pages; 90 | 91 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, ZBD_FILE_NAME, 92 | O_CREAT | O_RDWR)); 93 | page_size = device_get_page_size(dev); 94 | total_pages = WRITE_PAGE_SIZE(dev); 95 | buffer = (char *)malloc(page_size); 96 | TEST_ASSERT_NOT_NULL(buffer); 97 | memset(buffer, 0, page_size); 98 | 99 | /**< note that all I/O functions run synchronously */ 100 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 101 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 102 | request.paddr = addr; 103 | request.data_len = page_size; 104 | request.end_rq = NULL; 105 | request.flag = DEVICE_WRITE; 106 | request.sector = 0; 107 | request.data = buffer; 108 | TEST_ASSERT_EQUAL_INT(request.data_len, 109 | dev->d_op->write(dev, &request)); 110 | } 111 | 112 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 113 | request.paddr = addr; 114 | request.data_len = page_size; 115 | request.end_rq = NULL; 116 | request.flag = DEVICE_WRITE; 117 | request.sector = 0; 118 | request.data = buffer; 119 | TEST_ASSERT_EQUAL_INT(-EINVAL, dev->d_op->write(dev, &request)); 120 | } 121 | 122 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 123 | free(buffer); 124 | } 125 | 126 | void test_erase(void) 127 | { 128 | struct device_request request; 129 | struct device_address addr; 130 | char *buffer; 131 | size_t page_size; 132 | size_t total_pages; 133 | size_t nr_segments; 134 | size_t nr_pages_per_segment; 135 | size_t segnum; 136 | 137 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, ZBD_FILE_NAME, 138 | O_CREAT | O_RDWR)); 139 | page_size = device_get_page_size(dev); 140 | total_pages = WRITE_PAGE_SIZE(dev); 141 | buffer = (char *)malloc(page_size); 142 | TEST_ASSERT_NOT_NULL(buffer); 143 | memset(buffer, 0, page_size); 144 | 145 | /**< note that all I/O functions run synchronously */ 146 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 147 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 148 | request.paddr = addr; 149 | request.data_len = page_size; 150 | request.end_rq = NULL; 151 | request.flag = DEVICE_WRITE; 152 | request.sector = 0; 153 | request.data = buffer; 154 | TEST_ASSERT_EQUAL_INT(request.data_len, 155 | dev->d_op->write(dev, &request)); 156 | } 157 | 158 | nr_segments = WRITE_PAGE_SIZE(dev) / device_get_pages_per_segment(dev); 159 | for (segnum = 0; segnum < nr_segments - 1; segnum++) { 160 | addr.lpn = 0; 161 | addr.format.block = segnum; 162 | request.paddr = addr; 163 | request.flag = DEVICE_ERASE; 164 | request.end_rq = NULL; 165 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->erase(dev, &request)); 166 | }; 167 | 168 | nr_pages_per_segment = device_get_pages_per_segment(dev); 169 | for (addr.lpn = 0; addr.lpn < total_pages - nr_pages_per_segment; 170 | addr.lpn++) { 171 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 172 | request.paddr = addr; 173 | request.data_len = page_size; 174 | request.end_rq = NULL; 175 | request.flag = DEVICE_WRITE; 176 | request.sector = 0; 177 | request.data = buffer; 178 | TEST_ASSERT_EQUAL_INT(request.data_len, 179 | dev->d_op->write(dev, &request)); 180 | } 181 | 182 | for (; addr.lpn < total_pages; addr.lpn++) { 183 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 184 | request.paddr = addr; 185 | request.data_len = page_size; 186 | request.end_rq = NULL; 187 | request.flag = DEVICE_WRITE; 188 | request.sector = 0; 189 | request.data = buffer; 190 | TEST_ASSERT_EQUAL_INT(-EINVAL, dev->d_op->write(dev, &request)); 191 | } 192 | 193 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 194 | free(buffer); 195 | } 196 | 197 | static void end_rq(struct device_request *request) 198 | { 199 | struct device_address paddr = request->paddr; 200 | uint8_t *is_check = (uint8_t *)request->rq_private; 201 | is_check[paddr.lpn] = 1; 202 | } 203 | 204 | void test_end_rq_works(void) 205 | { 206 | struct device_request request; 207 | struct device_address addr; 208 | char *buffer; 209 | uint8_t *is_check; 210 | size_t page_size; 211 | size_t total_pages; 212 | size_t segnum; 213 | size_t nr_segments; 214 | 215 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->open(dev, ZBD_FILE_NAME, 216 | O_CREAT | O_RDWR)); 217 | page_size = device_get_page_size(dev); 218 | total_pages = WRITE_PAGE_SIZE(dev); 219 | nr_segments = WRITE_PAGE_SIZE(dev) / device_get_pages_per_segment(dev); 220 | 221 | buffer = (char *)malloc(page_size); 222 | TEST_ASSERT_NOT_NULL(buffer); 223 | memset(buffer, 0, page_size); 224 | 225 | is_check = (uint8_t *)malloc(total_pages); 226 | TEST_ASSERT_NOT_NULL(is_check); 227 | memset(is_check, 0, total_pages); 228 | 229 | /**< note that all I/O functions run synchronously */ 230 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 231 | memcpy(buffer, &addr.lpn, sizeof(uint32_t)); 232 | request.paddr = addr; 233 | request.data_len = page_size; 234 | request.end_rq = end_rq; 235 | request.flag = DEVICE_WRITE; 236 | request.sector = 0; 237 | request.data = buffer; 238 | request.rq_private = (void *)is_check; 239 | TEST_ASSERT_EQUAL_INT(request.data_len, 240 | dev->d_op->write(dev, &request)); 241 | } 242 | 243 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 244 | TEST_ASSERT_EQUAL_INT(1, is_check[addr.lpn]); 245 | } 246 | 247 | memset(is_check, 0, total_pages); 248 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 249 | request.paddr = addr; 250 | request.data_len = page_size; 251 | request.end_rq = end_rq; 252 | request.flag = DEVICE_READ; 253 | request.sector = 0; 254 | request.data = buffer; 255 | TEST_ASSERT_EQUAL_INT(request.data_len, 256 | dev->d_op->read(dev, &request)); 257 | TEST_ASSERT_EQUAL_UINT32(addr.lpn, *(uint32_t *)request.data); 258 | } 259 | 260 | for (addr.lpn = 0; addr.lpn < total_pages; addr.lpn++) { 261 | TEST_ASSERT_EQUAL_INT(1, is_check[addr.lpn]); 262 | } 263 | 264 | memset(is_check, 0, total_pages); 265 | for (segnum = 0; segnum < nr_segments; segnum++) { 266 | addr.lpn = 0; 267 | addr.format.block = segnum; 268 | request.paddr = addr; 269 | request.flag = DEVICE_ERASE; 270 | request.end_rq = end_rq; 271 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->erase(dev, &request)); 272 | }; 273 | 274 | for (segnum = 0; segnum < nr_segments; segnum++) { 275 | addr.lpn = 0; 276 | addr.format.block = segnum; 277 | TEST_ASSERT_EQUAL_INT(1, is_check[addr.lpn]); 278 | } 279 | 280 | TEST_ASSERT_EQUAL_INT(0, dev->d_op->close(dev)); 281 | free(buffer); 282 | free(is_check); 283 | } 284 | 285 | int main(void) 286 | { 287 | UNITY_BEGIN(); 288 | RUN_TEST(test_open); 289 | RUN_TEST(test_full_write); 290 | RUN_TEST(test_overwrite); 291 | RUN_TEST(test_erase); 292 | RUN_TEST(test_end_rq_works); 293 | return UNITY_END(); 294 | } 295 | -------------------------------------------------------------------------------- /ftl/page/page-gc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file page-gc.c 3 | * @brief garbage collection logic for page ftl 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-06 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "page.h" 15 | #include "log.h" 16 | #include "bits.h" 17 | 18 | /** 19 | * @brief page ftl gc list compare function 20 | * 21 | * @param a compare target 1 22 | * @param b compare target 2 23 | * 24 | * @return to make precede a segment that contains the less valid pages 25 | */ 26 | gint page_ftl_gc_list_cmp(gconstpointer a, gconstpointer b) 27 | { 28 | struct page_ftl_segment *segment[2]; 29 | uint64_t nr_valid_pages[2]; 30 | segment[0] = (struct page_ftl_segment *)a; 31 | segment[1] = (struct page_ftl_segment *)b; 32 | nr_valid_pages[0] = 33 | (uint64_t)g_atomic_int_get(&segment[0]->nr_valid_pages); 34 | nr_valid_pages[1] = 35 | (uint64_t)g_atomic_int_get(&segment[1]->nr_valid_pages); 36 | return (gint)(nr_valid_pages[0] - nr_valid_pages[1]); 37 | } 38 | 39 | /** 40 | * @brief erase's end request function 41 | * 42 | * @param request the request which is submitted before 43 | */ 44 | static void page_ftl_erase_end_rq(struct device_request *request) 45 | { 46 | device_free_request(request); 47 | } 48 | 49 | /** 50 | * @brief the function which chooses the appropriate garbage collection target. 51 | * 52 | * @param pgftl pointer of the page FTL structure 53 | * 54 | * @return garbage collection target segment's pointer 55 | */ 56 | static struct page_ftl_segment *page_ftl_pick_gc_target(struct page_ftl *pgftl) 57 | { 58 | struct page_ftl_segment *segment; 59 | if (pgftl->gc_list == NULL) { 60 | return NULL; 61 | } 62 | pgftl->gc_list = g_list_sort(pgftl->gc_list, page_ftl_gc_list_cmp); 63 | segment = (struct page_ftl_segment *)pgftl->gc_list->data; 64 | pr_debug("gc target: %zu (valid: %d) => %p\n", 65 | page_ftl_get_segment_number(pgftl, (uintptr_t)segment), 66 | g_atomic_int_get(&segment->nr_valid_pages), segment); 67 | pgftl->gc_list = g_list_remove(pgftl->gc_list, segment); 68 | g_atomic_int_set(&segment->nr_free_pages, 0); 69 | return segment; 70 | } 71 | 72 | /** 73 | * @brief erase the garbage collection target segment 74 | * 75 | * @param pgftl pointer of the page FTL structure 76 | * @param paddr the address containing the segment number which wants to erase 77 | * 78 | * @return 0 for success, negative number for fail 79 | */ 80 | static int page_ftl_segment_erase(struct page_ftl *pgftl, 81 | struct device_address paddr) 82 | { 83 | struct device *dev; 84 | struct device_request *request; 85 | int ret; 86 | 87 | dev = pgftl->dev; 88 | 89 | request = device_alloc_request(DEVICE_DEFAULT_REQUEST); 90 | if (request == NULL) { 91 | pr_err("request allocation failed\n"); 92 | return -ENOMEM; 93 | } 94 | 95 | request->flag = DEVICE_ERASE; 96 | request->paddr = paddr; 97 | request->end_rq = page_ftl_erase_end_rq; 98 | ret = dev->d_op->erase(dev, request); 99 | if (ret) { 100 | pr_err("erase error detected(errno: %d)\n", ret); 101 | return ret; 102 | } 103 | return 0; 104 | } 105 | 106 | /** 107 | * @brief read the valid pages from the garbage collection target segment 108 | * 109 | * @param pgftl pointer of the page FTL structure 110 | * @param lpn read position which contains the logical page number 111 | * @param __buffer buffer pointer's address which dynamically allocated by this function 112 | * 113 | * @return reading data size. a negative number means fail to read 114 | */ 115 | static ssize_t page_ftl_read_valid_page(struct page_ftl *pgftl, size_t lpn, 116 | char **__buffer) 117 | { 118 | struct device *dev; 119 | struct device_request *request; 120 | char *buffer; 121 | size_t page_size; 122 | ssize_t ret; 123 | 124 | dev = pgftl->dev; 125 | page_size = device_get_page_size(dev); 126 | request = NULL; 127 | 128 | buffer = (char *)malloc(page_size); 129 | if (buffer == NULL) { 130 | pr_err("memory allocation failed\n"); 131 | ret = -ENOMEM; 132 | goto exception; 133 | } 134 | memset(buffer, 0, page_size); 135 | 136 | request = device_alloc_request(DEVICE_DEFAULT_REQUEST); 137 | if (request == NULL) { 138 | pr_err("request allocation failed\n"); 139 | ret = -ENOMEM; 140 | goto exception; 141 | } 142 | 143 | request->flag = DEVICE_READ; 144 | request->data_len = page_size; 145 | request->sector = lpn * page_size; 146 | request->data = buffer; 147 | 148 | ret = page_ftl_read(pgftl, request); 149 | if (ret != (ssize_t)page_size) { 150 | pr_err("invalid read size detected (expected: %zd, acutal: %zd)\n", 151 | page_size, ret); 152 | return -EFAULT; 153 | } 154 | 155 | *__buffer = buffer; 156 | return ret; 157 | exception: 158 | if (buffer) { 159 | free(buffer); 160 | } 161 | if (request) { 162 | device_free_request(request); 163 | } 164 | return ret; 165 | } 166 | 167 | /** 168 | * @brief write valid page to the other segment. 169 | * 170 | * @param pgftl pointer of the page FTL structure 171 | * @param lpn write position which contains the logical page number 172 | * @param buffer buffer pointer containing the valid page 173 | * 174 | * @return writing data size. a negative number means fail to write 175 | */ 176 | static ssize_t page_ftl_write_valid_page(struct page_ftl *pgftl, size_t lpn, 177 | char *buffer) 178 | { 179 | struct device *dev; 180 | struct device_request *request; 181 | size_t page_size; 182 | ssize_t ret; 183 | 184 | dev = pgftl->dev; 185 | page_size = device_get_page_size(dev); 186 | 187 | request = device_alloc_request(DEVICE_DEFAULT_REQUEST); 188 | if (request == NULL) { 189 | pr_err("request allocation failed\n"); 190 | return -ENOMEM; 191 | } 192 | 193 | request->flag = DEVICE_WRITE; 194 | request->data_len = page_size; 195 | request->sector = lpn * page_size; 196 | request->data = buffer; 197 | 198 | ret = page_ftl_write(pgftl, request); 199 | if (ret != (ssize_t)page_size) { 200 | pr_err("invalid write size detected (expected: %zd, acutal: %zd)\n", 201 | page_size, ret); 202 | return -EFAULT; 203 | } 204 | free(buffer); 205 | return ret; 206 | } 207 | 208 | /** 209 | * @brief core logic of the valid page copy 210 | * 211 | * @param pgftl pointer of the page FTL structure 212 | * @param segment segment which wants to copy the valid pages 213 | * 214 | * @return 0 for success, negative number for fail 215 | */ 216 | static ssize_t page_ftl_valid_page_copy(struct page_ftl *pgftl, 217 | struct page_ftl_segment *segment) 218 | { 219 | ssize_t ret = 0; 220 | GList *list; 221 | 222 | (void)pgftl; 223 | list = segment->lpn_list; 224 | 225 | while (list) { 226 | size_t lpn; 227 | char *buffer; 228 | 229 | GList *next = list->next; 230 | lpn = GPOINTER_TO_SIZE(list->data); 231 | ret = page_ftl_read_valid_page(pgftl, lpn, &buffer); 232 | if (ret < 0) { 233 | pr_err("read valid page failed\n"); 234 | return ret; 235 | } 236 | ret = page_ftl_write_valid_page(pgftl, lpn, buffer); 237 | if (ret < 0) { 238 | pr_err("write valid page failed\n"); 239 | return ret; 240 | } 241 | list = next; 242 | } 243 | return ret; 244 | } 245 | 246 | /** 247 | * @brief core logic of the garbage collection 248 | * 249 | * @param pgftl pointer of the page FTL structure 250 | * 251 | * @return 0 for success, negative number for fail 252 | */ 253 | ssize_t page_ftl_do_gc(struct page_ftl *pgftl) 254 | { 255 | struct device_address paddr; 256 | struct page_ftl_segment *segment; 257 | ssize_t ret; 258 | size_t segnum; 259 | 260 | pthread_mutex_lock(&pgftl->mutex); 261 | segment = page_ftl_pick_gc_target(pgftl); 262 | pthread_mutex_unlock(&pgftl->mutex); 263 | if (segment == NULL) { 264 | pr_debug("gc target segment doesn't exist\n"); 265 | return 0; 266 | } 267 | segnum = page_ftl_get_segment_number(pgftl, (uintptr_t)segment); 268 | pr_debug("current segnum: %zu\n", segnum); 269 | 270 | ret = page_ftl_valid_page_copy(pgftl, segment); 271 | if (ret < 0) { 272 | pr_err("valid page copy failed\n"); 273 | return ret; 274 | } 275 | 276 | paddr.lpn = 0; 277 | paddr.format.block = (uint16_t)segnum; 278 | ret = page_ftl_segment_erase(pgftl, paddr); 279 | if (ret) { 280 | pr_err("do erase failed\n"); 281 | return ret; 282 | } 283 | 284 | pthread_mutex_lock(&pgftl->mutex); 285 | ret = page_ftl_segment_data_init(pgftl, segment); 286 | if (ret) { 287 | pr_err("initialize the segment data failed\n"); 288 | return ret; 289 | } 290 | reset_bit(pgftl->gc_seg_bits, segnum); 291 | pthread_mutex_unlock(&pgftl->mutex); 292 | 293 | return 0; 294 | } 295 | 296 | /** 297 | * @brief do garbage collection from the gc list 298 | * 299 | * @param pgftl pointer of the page ftl 300 | * @param request pointer of the request 301 | * @param gc_ratio ratio for garbage collecting targets 302 | * 303 | * @return number of erased segments 304 | */ 305 | ssize_t page_ftl_gc_from_list(struct page_ftl *pgftl, 306 | struct device_request *request, double gc_ratio) 307 | { 308 | ssize_t ret = 0; 309 | size_t nr_segments, nr_gc_segments, idx; 310 | nr_segments = device_get_nr_segments(pgftl->dev); 311 | nr_gc_segments = (size_t)((double)nr_segments * gc_ratio); 312 | for (idx = 0; (ssize_t)idx >= 0 && idx < nr_gc_segments; idx++) { 313 | ret = page_ftl_submit_request(pgftl, request); 314 | if (ret) { 315 | pr_err("garbage collection from list failed\n"); 316 | return ret; 317 | } 318 | if (pgftl->gc_list == NULL) { 319 | break; 320 | } 321 | } 322 | ret = (ssize_t)idx; 323 | return ret; 324 | } 325 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # You can generate the compile_commands.json file by using 2 | # `make clean -j$(nproc)` 3 | # `bear make all -j$(nproc)` 4 | # `compdb -p ./ list > ../compile_commands.json` 5 | # `cp ../compile_commands.json ./ 6 | # or 7 | # `make compiledb` 8 | # 9 | # You can get `bear` from [link](https://github.com/rizsotto/Bear) 10 | # You can get `compdb` from [link](https://github.com/Sarcasm/compdb) 11 | 12 | # `-fsanitize=address` and `-lasan` for memory leakage checking 13 | # `-g` and `-pg` for tracing the program 14 | # So, you must delete all when you release the program 15 | 16 | .SUFFIXES : .c .cpp .o 17 | CC = gcc 18 | AR = ar 19 | CXX = g++ 20 | INTEGRATION_TEST_TARGET = integration-test.out 21 | BENCHMARK_TARGET = benchmark.out 22 | LIBRARY_TARGET = libftl.a 23 | 24 | GLIB_INCLUDES = $(shell pkg-config --cflags glib-2.0) 25 | DEVICE_INCLUDES = 26 | 27 | GLIB_LIBS = $(shell pkg-config --libs glib-2.0) 28 | DOCKER_TAG_ROOT = ftl 29 | 30 | # Device Module Setting 31 | USE_ZONE_DEVICE = 0 32 | USE_BLUEDBM_DEVICE = 0 33 | USE_RASPBERRY_DEVICE = 0 34 | # Debug Setting 35 | USE_DEBUG = 0 36 | USE_LOG_SILENT = 0 37 | # Random Generator Setting 38 | USE_LEGACY_RANDOM = 0 39 | 40 | ifeq ($(USE_DEBUG), 1) 41 | DEBUG_FLAGS = -g -pg \ 42 | -DCONFIG_ENABLE_MSG \ 43 | -DCONFIG_ENABLE_DEBUG 44 | MACROS = -DUSE_GC_MESSAGE -DDEBUG 45 | MEMORY_CHECK_LIBS = -lasan 46 | MEMORY_CHECK_CFLAGS = 47 | MEMORY_CHECK_LIBS += -fsanitize=address 48 | else 49 | DEBUG_FLAGS = 50 | MACROS = -DUSE_GC_MESSAGE 51 | MEMORY_CHECK_LIBS = 52 | MEMORY_CHECK_CFLAGS = 53 | endif 54 | 55 | ifeq ($(USE_LOG_SILENT), 1) 56 | DEBUG_FLAGS += -DENABLE_LOG_SILENT 57 | endif 58 | 59 | ifeq ($(USE_LEGACY_RANDOM), 1) 60 | MACROS += -DUSE_LEGACY_RANDOM 61 | endif 62 | 63 | TEST_TARGET := lru-test.out \ 64 | bits-test.out \ 65 | ramdisk-test.out 66 | 67 | DEVICE_LIBS = 68 | 69 | ifeq ($(USE_ZONE_DEVICE), 1) 70 | # Zoned Device's Setting 71 | DEVICE_INFO := -DDEVICE_NR_BUS_BITS=3 \ 72 | -DDEVICE_NR_CHIPS_BITS=3 \ 73 | -DDEVICE_NR_PAGES_BITS=5 \ 74 | -DDEVICE_NR_BLOCKS_BITS=21 75 | 76 | TEST_TARGET += zone-test.out 77 | DEVICE_LIBS += -lzbd 78 | else ifeq ($(USE_BLUEDBM_DEVICE), 1) 79 | # BlueDBM Device's Setting 80 | DEVICE_INFO := -DDEVICE_NR_BUS_BITS=3 \ 81 | -DDEVICE_NR_CHIPS_BITS=3 \ 82 | -DDEVICE_NR_PAGES_BITS=7 \ 83 | -DDEVICE_NR_BLOCKS_BITS=19 \ 84 | -DUSER_MODE \ 85 | -DUSE_PMU \ 86 | -DUSE_KTIMER \ 87 | -DUSE_NEW_RMW \ 88 | -D_LARGEFILE64_SOURCE \ 89 | -D_GNU_SOURCE \ 90 | -DNOHOST 91 | 92 | DEVICE_LIBS += -lmemio 93 | DEVICE_INCLUDES += -I/usr/local/include/memio 94 | else ifeq ($(USE_RASPBERRY_DEVICE), 1) 95 | # Configuration for RaspberryPi Device 96 | # https://www.waveshare.com/wiki/File:K9F1G08U0D.pdf 97 | # 98 | # RaspberryPi does not support the multiple chips and pages. 99 | # For this reason, it does not match with conventional physical address format. 100 | # 101 | # Therefore, I translated conventional physical address format to RaspberryPi compatible format. 102 | # +----------------+--------------+--------------+-------------+ 103 | # Compatible => | Block(24 bits) | Page(4 bits) | Chip(1 bits) | Bus(1 bits) | 104 | # +----------------+-------------------------------------------+ 105 | # Actual => | Block(24 bits) | Page(6 bits) | 106 | # +----------------+-------------------------------------------+ 107 | DEVICE_INFO := -DDEVICE_NR_BUS_BITS=1 \ 108 | -DDEVICE_NR_CHIPS_BITS=1 \ 109 | -DDEVICE_NR_PAGES_BITS=4 \ 110 | -DDEVICE_NR_BLOCKS_BITS=24 \ 111 | -DDEVICE_PAGE_SIZE=2048 112 | DEVICE_LIBS += -lnand -lwiringPi 113 | DEVICE_INCLUDES += -I/usr/local/include/nand 114 | else 115 | # Ramdisk Setting (1GiB) 116 | DEVICE_INFO := -DDEVICE_NR_BUS_BITS=2 \ 117 | -DDEVICE_NR_CHIPS_BITS=2 \ 118 | -DDEVICE_NR_PAGES_BITS=7 \ 119 | -DDEVICE_NR_BLOCKS_BITS=19 120 | endif 121 | 122 | ifeq ($(USE_ZONE_DEVICE), 1) 123 | DEVICE_INFO += -DDEVICE_USE_ZONED \ 124 | -DPAGE_FTL_USE_GLOBAL_RWLOCK 125 | endif 126 | 127 | ifeq ($(USE_BLUEDBM_DEVICE), 1) 128 | DEVICE_INFO += -DDEVICE_USE_BLUEDBM 129 | endif 130 | 131 | ifeq ($(USE_RASPBERRY_DEVICE), 1) 132 | DEVICE_INFO += -DDEVICE_USE_RASPBERRY 133 | endif 134 | 135 | ARFLAGS := rcs 136 | CFLAGS := -Wall \ 137 | -Wextra \ 138 | -Wpointer-arith \ 139 | -Wcast-align \ 140 | -Wwrite-strings \ 141 | -Wswitch-default \ 142 | -Wunreachable-code \ 143 | -Winit-self \ 144 | -Wmissing-field-initializers \ 145 | -Wno-unknown-pragmas \ 146 | -Wundef \ 147 | -Wconversion \ 148 | -Werror \ 149 | $(DEVICE_INFO) \ 150 | $(DEBUG_FLAGS) \ 151 | $(MEMORY_CHECK_CFLAGS) \ 152 | -O3 153 | 154 | CXXFLAGS := $(CFLAGS) \ 155 | -std=c++11 156 | 157 | UNITY_ROOT := ./unity 158 | LIBS := -lm -lpthread $(GLIB_LIBS) $(DEVICE_LIBS) $(MEMORY_CHECK_LIBS) 159 | 160 | INCLUDES := -I./include -I./unity/src $(GLIB_INCLUDES) $(DEVICE_INCLUDES) 161 | 162 | RAMDISK_SRCS = device/ramdisk/*.c 163 | ZONED_SRCS = 164 | BLUEDBM_SRCS = 165 | RASPBERRY_SRCS = 166 | 167 | ifeq ($(USE_ZONE_DEVICE), 1) 168 | ZONED_SRCS += device/zone/*.c 169 | endif 170 | 171 | ifeq ($(USE_BLUEDBM_DEVICE), 1) 172 | BLUEDBM_SRCS += device/bluedbm/*.c 173 | endif 174 | 175 | ifeq ($(USE_RASPBERRY_DEVICE), 1) 176 | RASPBERRY_SRCS += device/raspberry/*.c 177 | endif 178 | 179 | DEVICE_SRCS := $(RAMDISK_SRCS) \ 180 | $(BLUEDBM_SRCS) \ 181 | $(ZONED_SRCS) \ 182 | $(RASPBERRY_SRCS) \ 183 | device/*.c 184 | 185 | UTIL_SRCS := util/*.c 186 | 187 | FTL_SRCS := ftl/page/*.c 188 | 189 | INTERFACE_SRCS := interface/*.c 190 | 191 | SRCS := $(DEVICE_SRCS) \ 192 | $(UTIL_SRCS) \ 193 | $(FTL_SRCS) \ 194 | $(INTERFACE_SRCS) 195 | 196 | OBJS := *.o 197 | 198 | ifeq ($(PREFIX),) 199 | PREFIX := /usr/local 200 | endif 201 | 202 | all: $(INTEGRATION_TEST_TARGET) $(BENCHMARK_TARGET) 203 | 204 | test: $(TEST_TARGET) 205 | @for target in $(TEST_TARGET) ; do \ 206 | ./$$target ; \ 207 | done 208 | # show coverage 209 | @for target in $(TEST_TARGET) ; do \ 210 | gcov ./$$target ; \ 211 | done 212 | 213 | integration-test: $(INTEGRATION_TEST_TARGET) 214 | ./$(INTEGRATION_TEST_TARGET) 215 | 216 | install: $(LIBRARY_TARGET) 217 | install -d $(DESTDIR)$(PREFIX)/lib/ 218 | install -m 644 $(LIBRARY_TARGET) $(DESTDIR)$(PREFIX)/lib 219 | install -d $(DESTDIR)$(PREFIX)/include/ftl 220 | install -m 644 include/*.h $(DESTDIR)$(PREFIX)/include/ftl 221 | 222 | $(INTEGRATION_TEST_TARGET): integration-test.c $(LIBRARY_TARGET) 223 | $(CXX) $(MACROS) $(CXXFLAGS) -c integration-test.c $(INCLUDES) $(LIBS) 224 | $(CXX) $(MACROS) $(CXXFLAGS) -o $@ integration-test.o -L. -lftl -lpthread $(LIBS) $(INCLUDES) 225 | 226 | $(BENCHMARK_TARGET): benchmark.c $(LIBRARY_TARGET) 227 | $(CXX) $(MACROS) $(CFLAGS) -c benchmark.c $(INCLUDES) $(LIBS) 228 | $(CXX) $(MACROS) $(CFLAGS) -o $@ benchmark.o -L. -lftl -lpthread -liberty $(INCLUDES) $(LIBS) 229 | 230 | $(LIBRARY_TARGET): $(OBJS) 231 | $(AR) $(ARFLAGS) $@ $^ 232 | 233 | $(OBJS): $(SRCS) 234 | $(CXX) $(MACROS) $(CFLAGS) -c $^ $(LIBS) $(INCLUDES) 235 | 236 | lru-test.out: unity.o ./util/lru.c ./test/lru-test.c 237 | $(CXX) $(MACROS) $(CFLAGS) $(INCLUDES) -o $@ --coverage $^ $(LIBS) 238 | 239 | bits-test.out: unity.o ./test/bits-test.c 240 | $(CXX) $(MACROS) $(CFLAGS) $(INCLUDES) -o $@ --coverage $^ $(LIBS) 241 | 242 | ramdisk-test.out: $(OBJS) ./test/ramdisk-test.c 243 | $(CXX) $(MACROS) $(CFLAGS) $(INCLUDES) -o $@ --coverage $^ $(LIBS) 244 | 245 | ifeq ($(USE_ZONE_DEVICE), 1) 246 | zone-test.out: $(OBJS) ./test/zone-test.c 247 | $(CXX) $(MACROS) $(CFLAGS) -DENABLE_LOG_SILENT $(INCLUDES) -o $@ --coverage $^ $(LIBS) 248 | endif 249 | 250 | unity.o: $(UNITY_ROOT)/src/unity.c 251 | $(CXX) $(MACROS) $(CFLAGS) -DENABLE_LOG_SILENT $(INCLUDES) -c $^ $(LIBS) 252 | 253 | docker-builder: 254 | docker build -t $(DOCKER_TAG_ROOT)/ftl-builder \ 255 | -f docker/Dockerfile ./docker 256 | 257 | docker-make-%: 258 | docker run --rm -v $(PWD):/ftl \ 259 | $(DOCKER_TAG_ROOT)/ftl-builder /bin/bash -c "make clean && make $*" 260 | 261 | docker-console: 262 | docker run --rm -it -v $(PWD):/ftl \ 263 | -v ${HOME}/.zshrc:/root/.zshrc \ 264 | -v ${HOME}/.vim:/root/.vim \ 265 | -v ${HOME}/.vimrc:/root/.vimrc \ 266 | $(DOCKER_TAG_ROOT)/ftl-builder /bin/bash 267 | 268 | check: 269 | @echo "[[ CPPCHECK ROUTINE ]]" 270 | cppcheck --quiet --error-exitcode=0 --enable=all --inconclusive -I include/ $(SRCS) *.c 271 | @echo "[[ FLAWFINDER ROUTINE ]]" 272 | flawfinder $(SRCS) include/*.h 273 | @echo "[[ STATIC ANALYSIS ROUTINE ]]" 274 | lizard $(SRCS) include/*.h 275 | 276 | documents: 277 | doxygen -s Doxyfile 278 | 279 | flow: 280 | find . -type f -name '*.[ch]' ! -path "./unity/*" ! -path "./test/*" | xargs -i cflow {} 281 | 282 | format: 283 | find . -type f -name '*.[ch]' ! -path "./unity/*" ! -path "./test/*" | xargs -i clang-format -i {} 284 | 285 | compiledb: 286 | bear -- $(MAKE) all 287 | compdb -p ./ list > ../compile_commands.json 288 | mv ../compile_commands.json ./ 289 | 290 | clean: 291 | find . -name '*.o' -exec rm -f {} + 292 | find . -name '*.gcov' -exec rm -f {} + 293 | find . -name '*.gcda' -exec rm -f {} + 294 | find . -name '*.gcno' -exec rm -f {} + 295 | rm -f $(TARGET) $(INTEGRATION_TEST_TARGET) $(TEST_TARGET) $(LIBRARY_TARGET) $(BENCHMARK_TARGET) 296 | -------------------------------------------------------------------------------- /device/ramdisk/ramdisk.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ramdisk.c 3 | * @brief implementation of the ramdisk which is inherited by the device 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-03 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "flash.h" 17 | #include "ramdisk.h" 18 | #include "device.h" 19 | #include "log.h" 20 | #include "bits.h" 21 | 22 | /** 23 | * @brief open the ramdisk (allocate the device resources) 24 | * 25 | * @param dev pointer of the device structure 26 | * @param name this does not use in this module 27 | * @param flags open flags for ramdisk 28 | * 29 | * @return 0 for success, negative value to fail 30 | */ 31 | int ramdisk_open(struct device *dev, const char *name, int flags) 32 | { 33 | int ret = 0; 34 | char *buffer; 35 | size_t bitmap_size; 36 | uint64_t *is_used; 37 | struct ramdisk *ramdisk; 38 | 39 | struct device_info *info = &dev->info; 40 | struct device_package *package = &info->package; 41 | struct device_block *block = &package->block; 42 | struct device_page *page = &block->page; 43 | 44 | size_t nr_segments; 45 | 46 | (void)name; 47 | (void)flags; 48 | 49 | info->nr_bus = (1 << DEVICE_NR_BUS_BITS); 50 | info->nr_chips = (1 << DEVICE_NR_CHIPS_BITS); 51 | 52 | package->nr_blocks = 64; /**< This for make 4GiB disk */ 53 | block->nr_pages = (1 << DEVICE_NR_PAGES_BITS); 54 | page->size = DEVICE_PAGE_SIZE; 55 | 56 | ramdisk = (struct ramdisk *)dev->d_private; 57 | ramdisk->size = device_get_total_size(dev); 58 | ramdisk->o_flags = flags; 59 | 60 | pr_info("ramdisk generated (size: %zu bytes)\n", ramdisk->size); 61 | buffer = (char *)malloc(ramdisk->size); 62 | if (buffer == NULL) { 63 | pr_err("memory allocation failed\n"); 64 | ret = -ENOMEM; 65 | goto exception; 66 | } 67 | memset(buffer, 0, ramdisk->size); 68 | ramdisk->buffer = buffer; 69 | 70 | bitmap_size = (size_t)BITS_TO_UINT64_ALIGN(ramdisk->size / page->size); 71 | is_used = (uint64_t *)malloc((size_t)bitmap_size); 72 | if (is_used == NULL) { 73 | pr_err("memory allocation failed\n"); 74 | ret = -ENOMEM; 75 | goto exception; 76 | } 77 | pr_info("bitmap generated (size: %zu bytes)\n", bitmap_size); 78 | memset(is_used, 0, bitmap_size); 79 | ramdisk->is_used = is_used; 80 | 81 | nr_segments = device_get_nr_segments(dev); 82 | dev->badseg_bitmap = 83 | (uint64_t *)malloc((size_t)BITS_TO_UINT64_ALIGN(nr_segments)); 84 | if (dev->badseg_bitmap == NULL) { 85 | pr_err("memory allocation failed\n"); 86 | ret = -ENOMEM; 87 | goto exception; 88 | } 89 | memset(dev->badseg_bitmap, 0, 90 | (size_t)BITS_TO_UINT64_ALIGN(nr_segments)); 91 | return ret; 92 | exception: 93 | ramdisk_close(dev); 94 | return ret; 95 | } 96 | 97 | /** 98 | * @brief write to the ramdisk 99 | * 100 | * @param dev pointer of the device structure 101 | * @param request pointer of the device request structure 102 | * 103 | * @return written size (bytes) 104 | */ 105 | ssize_t ramdisk_write(struct device *dev, struct device_request *request) 106 | { 107 | struct ramdisk *ramdisk = (struct ramdisk *)dev->d_private; 108 | struct device_address addr = request->paddr; 109 | size_t page_size = device_get_page_size(dev); 110 | ssize_t ret = 0; 111 | int is_used; 112 | 113 | if (request->data == NULL) { 114 | pr_err("you do not pass the data pointer to NULL\n"); 115 | ret = -ENODATA; 116 | goto exit; 117 | } 118 | 119 | if (request->flag != DEVICE_WRITE) { 120 | pr_err("request type is not matched (expected: %u, current: %u)\n", 121 | (unsigned int)DEVICE_WRITE, request->flag); 122 | ret = -EINVAL; 123 | goto exit; 124 | } 125 | 126 | if (request->paddr.lpn == PADDR_EMPTY) { 127 | pr_err("physical address is not specified...\n"); 128 | ret = -EINVAL; 129 | goto exit; 130 | } 131 | 132 | if (request->data_len != page_size) { 133 | pr_err("data write size is must be %zu (current: %zu)\n", 134 | request->data_len, page_size); 135 | ret = -EINVAL; 136 | goto exit; 137 | } 138 | 139 | is_used = get_bit(ramdisk->is_used, addr.lpn); 140 | if (is_used == 1) { 141 | pr_err("you overwrite the already written page\n"); 142 | ret = -EINVAL; 143 | goto exit; 144 | } 145 | set_bit(ramdisk->is_used, addr.lpn); 146 | memcpy(&ramdisk->buffer[addr.lpn * page_size], request->data, 147 | request->data_len); 148 | ret = (ssize_t)request->data_len; 149 | if (request->end_rq) { 150 | request->end_rq(request); 151 | } 152 | exit: 153 | return ret; 154 | } 155 | 156 | /** 157 | * @brief read from the ramdisk 158 | * 159 | * @param dev pointer of the device structure 160 | * @param request pointer of the device request structure 161 | * 162 | * @return read size (bytes) 163 | */ 164 | ssize_t ramdisk_read(struct device *dev, struct device_request *request) 165 | { 166 | struct ramdisk *ramdisk = (struct ramdisk *)dev->d_private; 167 | struct device_address addr = request->paddr; 168 | size_t page_size; 169 | ssize_t ret; 170 | 171 | ret = 0; 172 | 173 | if (request->data == NULL) { 174 | pr_err("you do not pass the data pointer to NULL\n"); 175 | ret = -ENODATA; 176 | goto exit; 177 | } 178 | 179 | if (request->flag != DEVICE_READ) { 180 | pr_err("request type is not matched (expected: %u, current: %u)\n", 181 | (unsigned int)DEVICE_READ, request->flag); 182 | ret = -EINVAL; 183 | goto exit; 184 | } 185 | 186 | page_size = device_get_page_size(dev); 187 | if (request->data_len != page_size) { 188 | pr_err("data read size is must be %zu (current: %zu)\n", 189 | request->data_len, page_size); 190 | ret = -EINVAL; 191 | goto exit; 192 | } 193 | 194 | if (request->paddr.lpn == PADDR_EMPTY) { 195 | pr_err("physical address is not specified...\n"); 196 | ret = -EINVAL; 197 | goto exit; 198 | } 199 | 200 | memcpy(request->data, &ramdisk->buffer[addr.lpn * page_size], 201 | request->data_len); 202 | ret = (ssize_t)request->data_len; 203 | pr_debug("request->end_rq %p %p\n", request->end_rq, 204 | &((struct device_request *)request->rq_private)->mutex); 205 | if (request->end_rq) { 206 | request->end_rq(request); 207 | } 208 | exit: 209 | return ret; 210 | } 211 | 212 | /** 213 | * @brief erase a segment 214 | * 215 | * @param dev pointer of the device structure 216 | * @param request pointer of the device request structure 217 | * 218 | * @return 0 for success, negative value for fail 219 | */ 220 | int ramdisk_erase(struct device *dev, struct device_request *request) 221 | { 222 | struct ramdisk *ramdisk = (struct ramdisk *)dev->d_private; 223 | struct device_address addr; 224 | size_t page_size; 225 | uint32_t nr_pages_per_segment; 226 | uint32_t lpn; 227 | uint16_t segnum; 228 | int ret; 229 | 230 | addr.lpn = 0; 231 | ret = 0; 232 | 233 | if (request->flag != DEVICE_ERASE) { 234 | pr_err("request type is not matched (expected: %u, current: %u)\n", 235 | (unsigned int)DEVICE_ERASE, request->flag); 236 | ret = -EINVAL; 237 | goto exit; 238 | } 239 | segnum = (uint16_t)request->paddr.format.block; 240 | page_size = device_get_page_size(dev); 241 | nr_pages_per_segment = (uint32_t)device_get_pages_per_segment(dev); 242 | addr.format.block = segnum; 243 | for (lpn = addr.lpn; lpn < addr.lpn + nr_pages_per_segment; lpn++) { 244 | memset(&ramdisk->buffer[lpn * page_size], 0, page_size); 245 | reset_bit(ramdisk->is_used, lpn); 246 | } 247 | 248 | if (request->end_rq) { 249 | request->end_rq(request); 250 | } 251 | exit: 252 | return ret; 253 | } 254 | 255 | /** 256 | * @brief close the ramdisk 257 | * 258 | * @param dev pointer of the device structure 259 | * 260 | * @return 0 for success, negative value for fail 261 | */ 262 | int ramdisk_close(struct device *dev) 263 | { 264 | struct ramdisk *ramdisk; 265 | if (dev->badseg_bitmap != NULL) { 266 | free(dev->badseg_bitmap); 267 | dev->badseg_bitmap = NULL; 268 | } 269 | ramdisk = (struct ramdisk *)dev->d_private; 270 | if (ramdisk == NULL) { 271 | return 0; 272 | } 273 | if (ramdisk->buffer != NULL) { 274 | free(ramdisk->buffer); 275 | ramdisk->buffer = NULL; 276 | } 277 | if (ramdisk->is_used != NULL) { 278 | free(ramdisk->is_used); 279 | ramdisk->is_used = NULL; 280 | } 281 | ramdisk->size = 0; 282 | return 0; 283 | } 284 | 285 | /** 286 | * @brief ramdisk operations 287 | */ 288 | const struct device_operations __ramdisk_dops = { 289 | .open = ramdisk_open, 290 | .write = ramdisk_write, 291 | .read = ramdisk_read, 292 | .erase = ramdisk_erase, 293 | .close = ramdisk_close, 294 | }; 295 | 296 | /** 297 | * @brief initialize the device and ramdisk module 298 | * 299 | * @param dev pointer of the device structure 300 | * @param flags flags for ramdisk and device 301 | * 302 | * @return 0 for sucess, negative value for fail 303 | */ 304 | int ramdisk_device_init(struct device *dev, uint64_t flags) 305 | { 306 | int ret = 0; 307 | struct ramdisk *ramdisk; 308 | 309 | (void)flags; 310 | ramdisk = (struct ramdisk *)malloc(sizeof(struct ramdisk)); 311 | if (ramdisk == NULL) { 312 | pr_err("memory allocation failed\n"); 313 | ret = -ENOMEM; 314 | goto exception; 315 | } 316 | ramdisk->buffer = NULL; 317 | ramdisk->size = 0; 318 | dev->d_op = &__ramdisk_dops; 319 | dev->d_private = (void *)ramdisk; 320 | dev->d_submodule_exit = ramdisk_device_exit; 321 | 322 | if (dev->d_private == NULL) { 323 | goto exception; 324 | } 325 | 326 | return ret; 327 | exception: 328 | ramdisk_device_exit(dev); 329 | return ret; 330 | } 331 | 332 | /** 333 | * @brief deallocate the device module 334 | * 335 | * @param dev pointer of the device structure 336 | * 337 | * @return 0 for success, negative value for fail 338 | */ 339 | int ramdisk_device_exit(struct device *dev) 340 | { 341 | struct ramdisk *ramdisk; 342 | ramdisk = (struct ramdisk *)dev->d_private; 343 | if (ramdisk != NULL) { 344 | ramdisk_close(dev); 345 | free(ramdisk); 346 | dev->d_private = NULL; 347 | } 348 | return 0; 349 | } 350 | -------------------------------------------------------------------------------- /device/raspberry/raspberry.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file raspberry.c 3 | * @brief implementation of the raspberry which is inherited by the device 4 | * @author Gijun Oh 5 | * @date 2023-05-29 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "flash.h" 17 | #include "raspberry.h" 18 | #include "device.h" 19 | #include "log.h" 20 | #include "bits.h" 21 | #include "nand.h" 22 | 23 | /** 24 | * @brief open the raspberry (allocate the device resources) 25 | * 26 | * @param dev pointer of the device structure 27 | * @param name this does not use in this module 28 | * @param flags open flags for raspberry 29 | * 30 | * @return 0 for success, negative value to fail 31 | */ 32 | int raspberry_open(struct device *dev, const char *name, int flags) 33 | { 34 | int ret = 0, i; 35 | struct raspberry *raspberry; 36 | 37 | struct device_info *info = &dev->info; 38 | struct device_package *package = &info->package; 39 | struct device_block *block = &package->block; 40 | struct device_page *page = &block->page; 41 | 42 | size_t nr_segments; 43 | 44 | (void)name; 45 | (void)flags; 46 | 47 | info->nr_bus = (1 << DEVICE_NR_BUS_BITS); 48 | info->nr_chips = (1 << DEVICE_NR_CHIPS_BITS); 49 | block->nr_pages = (1 << DEVICE_NR_PAGES_BITS); 50 | 51 | package->nr_blocks = 1024; 52 | page->size = DEVICE_PAGE_SIZE; 53 | 54 | raspberry = (struct raspberry *)dev->d_private; 55 | raspberry->size = device_get_total_size(dev); 56 | raspberry->o_flags = flags; 57 | 58 | pr_info("raspberry generated (size: %zu bytes)\n", raspberry->size); 59 | 60 | nr_segments = device_get_nr_segments(dev); 61 | dev->badseg_bitmap = 62 | (uint64_t *)malloc((size_t)BITS_TO_UINT64_ALIGN(nr_segments)); 63 | if (dev->badseg_bitmap == NULL) { 64 | pr_err("memory allocation failed\n"); 65 | ret = -ENOMEM; 66 | goto exception; 67 | } 68 | memset(dev->badseg_bitmap, 0, 69 | (size_t)BITS_TO_UINT64_ALIGN(nr_segments)); 70 | 71 | // TODO: this part will be removed after implmenting recovery routine 72 | for (i = 0; (size_t)i < package->nr_blocks; i++) { 73 | int status; 74 | pthread_spin_lock(&raspberry->lock); 75 | status = nand_erase(i); 76 | pthread_spin_unlock(&raspberry->lock); 77 | if (status) { 78 | set_bit(dev->badseg_bitmap, (uint64_t)i); 79 | } 80 | } 81 | 82 | return ret; 83 | exception: 84 | raspberry_close(dev); 85 | return ret; 86 | } 87 | 88 | /** 89 | * @brief write to the raspberry 90 | * 91 | * @param dev pointer of the device structure 92 | * @param request pointer of the device request structure 93 | * 94 | * @return written size (bytes) 95 | */ 96 | ssize_t raspberry_write(struct device *dev, struct device_request *request) 97 | { 98 | struct device_address addr; 99 | size_t page_size = device_get_page_size(dev); 100 | ssize_t ret; 101 | struct raspberry *raspberry; 102 | 103 | addr.lpn = 0; 104 | addr.raspberry_converter.bus = request->paddr.format.bus; 105 | addr.raspberry_converter.chip = request->paddr.format.chip; 106 | addr.raspberry_converter.block = request->paddr.format.block; 107 | addr.raspberry_converter.page = request->paddr.format.page; 108 | 109 | ret = 0; 110 | 111 | raspberry = (struct raspberry *)dev->d_private; 112 | 113 | if (request->data == NULL) { 114 | pr_err("you do not pass the data pointer to NULL\n"); 115 | ret = -ENODATA; 116 | goto exit; 117 | } 118 | 119 | if (request->flag != DEVICE_WRITE) { 120 | pr_err("request type is not matched (expected: %u, current: %u)\n", 121 | (unsigned int)DEVICE_WRITE, request->flag); 122 | ret = -EINVAL; 123 | goto exit; 124 | } 125 | 126 | if (request->paddr.lpn == PADDR_EMPTY) { 127 | pr_err("physical address is not specified...\n"); 128 | ret = -EINVAL; 129 | goto exit; 130 | } 131 | 132 | if (request->data_len != page_size) { 133 | pr_err("data write size is must be %zu (current: %zu)\n", 134 | request->data_len, page_size); 135 | ret = -EINVAL; 136 | goto exit; 137 | } 138 | 139 | pthread_spin_lock(&raspberry->lock); 140 | ret = nand_write((char *)request->data, addr.raspberry.block, 141 | addr.raspberry.page); 142 | pthread_spin_unlock(&raspberry->lock); 143 | if (ret) { 144 | pr_err("write error detected %zu\n", ret); 145 | goto exit; 146 | } 147 | ret = (ssize_t)request->data_len; 148 | if (request->end_rq) { 149 | request->end_rq(request); 150 | } 151 | exit: 152 | return ret; 153 | } 154 | 155 | /** 156 | * @brief read from the raspberry 157 | * 158 | * @param dev pointer of the device structure 159 | * @param request pointer of the device request structure 160 | * 161 | * @return read size (bytes) 162 | */ 163 | ssize_t raspberry_read(struct device *dev, struct device_request *request) 164 | { 165 | struct device_address addr; 166 | size_t page_size; 167 | ssize_t ret; 168 | struct raspberry *raspberry; 169 | 170 | addr.lpn = 0; 171 | addr.raspberry_converter.bus = request->paddr.format.bus; 172 | addr.raspberry_converter.chip = request->paddr.format.chip; 173 | addr.raspberry_converter.block = request->paddr.format.block; 174 | addr.raspberry_converter.page = request->paddr.format.page; 175 | 176 | ret = 0; 177 | 178 | raspberry = (struct raspberry *)dev->d_private; 179 | 180 | if (request->data == NULL) { 181 | pr_err("you do not pass the data pointer to NULL\n"); 182 | ret = -ENODATA; 183 | goto exit; 184 | } 185 | 186 | if (request->flag != DEVICE_READ) { 187 | pr_err("request type is not matched (expected: %u, current: %u)\n", 188 | (unsigned int)DEVICE_READ, request->flag); 189 | ret = -EINVAL; 190 | goto exit; 191 | } 192 | 193 | page_size = device_get_page_size(dev); 194 | if (request->data_len != page_size) { 195 | pr_err("data read size is must be %zu (current: %zu)\n", 196 | request->data_len, page_size); 197 | ret = -EINVAL; 198 | goto exit; 199 | } 200 | 201 | if (request->paddr.lpn == PADDR_EMPTY) { 202 | pr_err("physical address is not specified...\n"); 203 | ret = -EINVAL; 204 | goto exit; 205 | } 206 | 207 | pthread_spin_lock(&raspberry->lock); 208 | ret = nand_read((char *)request->data, addr.raspberry.block, 209 | addr.raspberry.page); 210 | pthread_spin_unlock(&raspberry->lock); 211 | if (ret) { 212 | pr_warn("read error detected %s\n", 213 | nand_get_read_error_msg((int)ret)); 214 | goto exit; 215 | } 216 | ret = (ssize_t)request->data_len; 217 | pr_debug("request->end_rq %p %p\n", request->end_rq, 218 | &((struct device_request *)request->rq_private)->mutex); 219 | if (request->end_rq) { 220 | request->end_rq(request); 221 | } 222 | exit: 223 | return ret; 224 | } 225 | 226 | /** 227 | * @brief erase a segment 228 | * 229 | * @param dev pointer of the device structure 230 | * @param request pointer of the device request structure 231 | * 232 | * @return 0 for success, negative value for fail 233 | */ 234 | int raspberry_erase(struct device *dev, struct device_request *request) 235 | { 236 | uint16_t segnum; 237 | int ret; 238 | struct raspberry *raspberry; 239 | 240 | ret = 0; 241 | raspberry = (struct raspberry *)dev->d_private; 242 | 243 | if (request->flag != DEVICE_ERASE) { 244 | pr_err("request type is not matched (expected: %u, current: %u)\n", 245 | (unsigned int)DEVICE_ERASE, request->flag); 246 | ret = -EINVAL; 247 | goto exit; 248 | } 249 | segnum = (uint16_t)request->paddr.format.block; 250 | 251 | pthread_spin_lock(&raspberry->lock); 252 | ret = nand_erase(segnum); 253 | pthread_spin_unlock(&raspberry->lock); 254 | if (ret) { 255 | pr_warn("erase fail detected %d\n", ret); 256 | set_bit(dev->badseg_bitmap, segnum); 257 | goto exit; 258 | } 259 | 260 | if (request->end_rq) { 261 | request->end_rq(request); 262 | } 263 | exit: 264 | return ret; 265 | } 266 | 267 | /** 268 | * @brief close the raspberry 269 | * 270 | * @param dev pointer of the device structure 271 | * 272 | * @return 0 for success, negative value for fail 273 | */ 274 | int raspberry_close(struct device *dev) 275 | { 276 | struct raspberry *raspberry; 277 | if (dev->badseg_bitmap != NULL) { 278 | free(dev->badseg_bitmap); 279 | dev->badseg_bitmap = NULL; 280 | } 281 | raspberry = (struct raspberry *)dev->d_private; 282 | if (raspberry == NULL) { 283 | return 0; 284 | } 285 | raspberry->size = 0; 286 | return 0; 287 | } 288 | 289 | /** 290 | * @brief raspberry operations 291 | */ 292 | const struct device_operations __raspberry_dops = { 293 | .open = raspberry_open, 294 | .write = raspberry_write, 295 | .read = raspberry_read, 296 | .erase = raspberry_erase, 297 | .close = raspberry_close, 298 | }; 299 | 300 | /** 301 | * @brief initialize the device and raspberry module 302 | * 303 | * @param dev pointer of the device structure 304 | * @param flags flags for raspberry and device 305 | * 306 | * @return 0 for sucess, negative value for fail 307 | */ 308 | int raspberry_device_init(struct device *dev, uint64_t flags) 309 | { 310 | int ret = 0; 311 | struct raspberry *raspberry; 312 | 313 | (void)flags; 314 | raspberry = (struct raspberry *)malloc(sizeof(struct raspberry)); 315 | if (raspberry == NULL) { 316 | pr_err("memory allocation failed\n"); 317 | ret = -ENOMEM; 318 | goto exception; 319 | } 320 | raspberry->size = 0; 321 | dev->d_op = &__raspberry_dops; 322 | dev->d_private = (void *)raspberry; 323 | dev->d_submodule_exit = raspberry_device_exit; 324 | 325 | if (dev->d_private == NULL) { 326 | goto exception; 327 | } 328 | 329 | ret = nand_init(); 330 | if (ret) { 331 | goto exception; 332 | } 333 | 334 | return ret; 335 | exception: 336 | raspberry_device_exit(dev); 337 | return ret; 338 | } 339 | 340 | /** 341 | * @brief deallocate the device module 342 | * 343 | * @param dev pointer of the device structure 344 | * 345 | * @return 0 for success, negative value for fail 346 | */ 347 | int raspberry_device_exit(struct device *dev) 348 | { 349 | struct raspberry *raspberry; 350 | raspberry = (struct raspberry *)dev->d_private; 351 | if (raspberry != NULL) { 352 | raspberry_close(dev); 353 | free(raspberry); 354 | dev->d_private = NULL; 355 | } 356 | nand_free(); 357 | return 0; 358 | } 359 | -------------------------------------------------------------------------------- /ftl/page/page-interface.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file page-interface.c 3 | * @brief interface for page ftl 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-09-22 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "log.h" 14 | #include "page.h" 15 | #include "device.h" 16 | 17 | /** 18 | * @brief open the page flash translation layer based device 19 | * 20 | * @param flash pointer of the flash device information 21 | * @param name file's name 22 | * 23 | * @return zero to success, error number to fail 24 | */ 25 | static int page_ftl_open_interface(struct flash_device *flash, const char *name, 26 | int flags) 27 | { 28 | struct page_ftl *pgftl = NULL; 29 | if (flash == NULL) { 30 | pr_err("flash pointer doesn't exist\n"); 31 | return -EINVAL; 32 | } 33 | pgftl = (struct page_ftl *)flash->f_private; 34 | if (pgftl == NULL) { 35 | pr_err("page FTL information doesn't exist\n"); 36 | return -EINVAL; 37 | } 38 | return page_ftl_open(pgftl, name, flags); 39 | } 40 | 41 | /** 42 | * @brief write the page flash translation layer based device 43 | * 44 | * @param flash pointer of the flash device information 45 | * @param buffer pointer of the data buffer 46 | * @param count size of the buffer (bytes) 47 | * @param offset size of the offset (bytes) 48 | * 49 | * @return positive or zero write size to success, negative number to fail 50 | */ 51 | static ssize_t page_ftl_write_interface(struct flash_device *flash, 52 | void *buffer, size_t count, 53 | off_t offset) 54 | { 55 | ssize_t size = -1; 56 | struct page_ftl *pgftl = NULL; 57 | struct device_request *request = NULL; 58 | char *ptr; 59 | size_t page_size; 60 | ssize_t remain; 61 | 62 | /** check the pointer validity */ 63 | if (flash == NULL) { 64 | pr_err("flash pointer doesn't exist\n"); 65 | size = -EINVAL; 66 | goto exception; 67 | } 68 | 69 | pgftl = (struct page_ftl *)flash->f_private; 70 | if (pgftl == NULL) { 71 | pr_err("page FTL information doesn't exist\n"); 72 | size = -EINVAL; 73 | goto exception; 74 | } 75 | 76 | if (!((pgftl->o_flags & O_ACCMODE) == O_WRONLY || 77 | (pgftl->o_flags & O_ACCMODE) == O_RDWR)) { 78 | pr_err("cannot find the valid write flags (flags: 0x%x)\n", 79 | pgftl->o_flags); 80 | size = -EINVAL; 81 | goto exception; 82 | } 83 | 84 | ptr = (char *)buffer; 85 | page_size = device_get_page_size(pgftl->dev); 86 | size = 0; 87 | remain = (ssize_t)count; 88 | while (remain > 0) { 89 | size_t pos; 90 | ssize_t write_size; 91 | ssize_t submit_size; 92 | 93 | pos = page_ftl_get_page_offset(pgftl, (size_t)offset); 94 | if (pos + (size_t)remain < page_size) { 95 | submit_size = (ssize_t)remain; 96 | } else { 97 | submit_size = (ssize_t)(page_size - pos); 98 | } 99 | 100 | /** allocate the request */ 101 | request = device_alloc_request(DEVICE_DEFAULT_REQUEST); 102 | if (request == NULL) { 103 | pr_err("fail to allocate request structure\n"); 104 | size = -ENOMEM; 105 | goto exception; 106 | } 107 | 108 | request->flag = DEVICE_WRITE; 109 | request->data_len = (size_t)submit_size; 110 | request->sector = (size_t)offset; 111 | request->data = ptr; 112 | 113 | pr_debug("%zu (length: %zu, buffer: %lu, remain:%zu)\n", 114 | request->sector, request->data_len, 115 | (uintptr_t)request->data - (uintptr_t)buffer, remain); 116 | 117 | /** submit the request */ 118 | write_size = page_ftl_submit_request(pgftl, request); 119 | if (write_size != submit_size) { 120 | pr_err("page FTL submit request failed (write size: %zd)\n", 121 | write_size); 122 | if (write_size > 0) { 123 | request = NULL; 124 | } 125 | goto exception; 126 | } 127 | 128 | offset += write_size; 129 | remain -= (ssize_t)write_size; 130 | ptr += write_size; 131 | size += write_size; 132 | } 133 | if (remain != 0) { 134 | pr_err("write failed; remain size must be zero, but %zd\n", 135 | remain); 136 | goto exception; 137 | } 138 | return size; 139 | 140 | exception: 141 | if (request) { 142 | device_free_request(request); 143 | } 144 | return size; 145 | } 146 | 147 | /** 148 | * @brief read the page flash translation layer based device 149 | * 150 | * @param flash pointer of the flash device information 151 | * @param buffer pointer of the data buffer 152 | * @param count size of the buffer (bytes) 153 | * @param offset size of the offset (bytes) 154 | * 155 | * @return positive or zero read size to success, negative number to fail 156 | */ 157 | static ssize_t page_ftl_read_interface(struct flash_device *flash, void *buffer, 158 | size_t count, off_t offset) 159 | { 160 | struct page_ftl *pgftl = NULL; 161 | struct device_request *request = NULL; 162 | 163 | char *temp = NULL; 164 | 165 | ssize_t size = -1; 166 | ssize_t remain; 167 | size_t page_size; 168 | 169 | char *ptr; 170 | 171 | ptr = (char *)buffer; 172 | if (ptr == NULL) { 173 | pr_err("buffer pointer doesn't exist\n"); 174 | size = -EINVAL; 175 | goto exception; 176 | } 177 | 178 | /** check the pointer validity */ 179 | if (flash == NULL) { 180 | pr_err("flash pointer doesn't exist\n"); 181 | size = -EINVAL; 182 | goto exception; 183 | } 184 | pgftl = (struct page_ftl *)flash->f_private; 185 | if (pgftl == NULL) { 186 | pr_err("page FTL information doesn't exist\n"); 187 | size = -EINVAL; 188 | goto exception; 189 | } 190 | 191 | if (!((pgftl->o_flags & O_ACCMODE) == O_RDONLY || 192 | (pgftl->o_flags & O_ACCMODE) == O_RDWR)) { 193 | pr_err("cannot find the valid read flags (flags: 0x%x)\n", 194 | pgftl->o_flags); 195 | size = -EINVAL; 196 | goto exception; 197 | } 198 | 199 | page_size = device_get_page_size(pgftl->dev); 200 | temp = (char *)malloc(page_size); 201 | if (temp == NULL) { 202 | pr_err("memory allocation failed\n"); 203 | size = -ENOMEM; 204 | goto exception; 205 | } 206 | 207 | size = 0; 208 | remain = (ssize_t)count; 209 | while (remain != 0) { 210 | size_t pos; 211 | ssize_t read_size; 212 | ssize_t submit_size; 213 | 214 | pos = page_ftl_get_page_offset(pgftl, (size_t)offset); 215 | if (pos + (size_t)remain < page_size) { 216 | submit_size = remain; 217 | } else { 218 | submit_size = (ssize_t)(page_size - pos); 219 | } 220 | 221 | /** allocate the request */ 222 | request = device_alloc_request(DEVICE_DEFAULT_REQUEST); 223 | if (request == NULL) { 224 | pr_err("fail to allocate request structure\n"); 225 | size = -ENOMEM; 226 | goto exception; 227 | } 228 | 229 | request->flag = DEVICE_READ; 230 | request->data_len = (size_t)submit_size; 231 | request->sector = (size_t)offset; 232 | request->data = temp; 233 | 234 | /** submit the request */ 235 | read_size = page_ftl_submit_request(pgftl, request); 236 | if (read_size < (ssize_t)0) { 237 | pr_err("page FTL submit request failed\n"); 238 | size = -EINVAL; 239 | goto exception; 240 | } 241 | memcpy(ptr, temp, (size_t)read_size); 242 | offset += read_size; 243 | remain -= read_size; 244 | ptr += read_size; 245 | size += read_size; 246 | } 247 | free(temp); 248 | return size; 249 | 250 | exception: 251 | if (temp) { 252 | free(temp); 253 | } 254 | if (request) { 255 | device_free_request(request); 256 | } 257 | return size; 258 | } 259 | 260 | /** 261 | * @brief close the page flash translation layer based device 262 | * 263 | * @param flash pointer of the flash device information 264 | * 265 | * @return zero to success, error number to fail 266 | */ 267 | static int page_ftl_close_interface(struct flash_device *flash) 268 | { 269 | struct page_ftl *pgftl = NULL; 270 | if (flash == NULL) { 271 | pr_err("flash pointer doesn't exist\n"); 272 | return -EINVAL; 273 | } 274 | pgftl = (struct page_ftl *)flash->f_private; 275 | if (pgftl == NULL) { 276 | pr_err("page FTL information doesn't exist\n"); 277 | return -EINVAL; 278 | } 279 | return page_ftl_close(pgftl); 280 | } 281 | 282 | /** 283 | * @brief additional commands support interface 284 | * 285 | * @param flash pointer of the flash device information 286 | * @param request ioctl's request identifier 287 | * @param ... additional parameters 288 | * 289 | * @return 0 for success, negative number for failed 290 | */ 291 | static int page_ftl_ioctl_interface(struct flash_device *flash, 292 | unsigned int request, ...) 293 | { 294 | struct device_request *device_rq; 295 | struct page_ftl *pgftl = NULL; 296 | int ret = 0; 297 | 298 | if (flash == NULL) { 299 | pr_err("flash pointer doesn't exist\n"); 300 | return -EINVAL; 301 | } 302 | 303 | pgftl = (struct page_ftl *)flash->f_private; 304 | if (pgftl == NULL) { 305 | pr_err("page FTL information doesn't exist\n"); 306 | return -EINVAL; 307 | } 308 | 309 | device_rq = device_alloc_request(DEVICE_DEFAULT_REQUEST); 310 | if (device_rq == NULL) { 311 | pr_err("request allocation failed\n"); 312 | return -ENOMEM; 313 | } 314 | switch (request) { 315 | case PAGE_FTL_IOCTL_TRIM: 316 | device_rq->flag = DEVICE_ERASE; 317 | ret = (int)page_ftl_gc_from_list(pgftl, device_rq, 318 | PAGE_FTL_GC_ALL); 319 | break; 320 | default: 321 | pr_err("invalid command requested(commands: %u)\n", request); 322 | return -EINVAL; 323 | } 324 | device_free_request(device_rq); 325 | return ret; 326 | } 327 | 328 | /** 329 | * @brief implementation of the flash_operations 330 | */ 331 | const struct flash_operations __page_fops = { 332 | .open = page_ftl_open_interface, 333 | .write = page_ftl_write_interface, 334 | .read = page_ftl_read_interface, 335 | .ioctl = page_ftl_ioctl_interface, 336 | .close = page_ftl_close_interface, 337 | }; 338 | 339 | /** 340 | * @brief initialize the page flash translation layer module 341 | * 342 | * @param flash pointer of the flash device information 343 | * @param flags flags for flash and submodule 344 | * 345 | * @return zero to success, error number to fail 346 | */ 347 | int page_ftl_module_init(struct flash_device *flash, uint64_t flags) 348 | { 349 | int err = 0; 350 | uint64_t modnum = flags; 351 | struct page_ftl *pgftl; 352 | 353 | (void)flags; 354 | 355 | flash->f_op = &__page_fops; 356 | 357 | pgftl = (struct page_ftl *)malloc(sizeof(struct page_ftl)); 358 | if (pgftl == NULL) { 359 | err = errno; 360 | pr_err("fail to allocate the page FTL information pointer\n"); 361 | goto exception; 362 | } 363 | memset(pgftl, 0, sizeof(*pgftl)); 364 | 365 | err = device_module_init(modnum, &pgftl->dev, 0); 366 | if (err) { 367 | pr_err("initialize the device module failed\n"); 368 | goto exception; 369 | } 370 | 371 | flash->f_private = (void *)pgftl; 372 | flash->f_submodule_exit = page_ftl_module_exit; 373 | return 0; 374 | 375 | exception: 376 | page_ftl_module_exit(flash); 377 | return err; 378 | } 379 | 380 | /** 381 | * @brief free resources in the page flash translation layer module 382 | * 383 | * @param flash pointer of the flash device information 384 | * 385 | * @return zero to success, error number to fail 386 | * 387 | * @note 388 | * You must not free resources related on the flash module which is parent module 389 | */ 390 | int page_ftl_module_exit(struct flash_device *flash) 391 | { 392 | int ret = 0; 393 | struct page_ftl *pgftl = NULL; 394 | struct device *dev = NULL; 395 | if (flash == NULL) { 396 | pr_info("flash pointer is null detected\n"); 397 | return 0; 398 | } 399 | 400 | pgftl = (struct page_ftl *)flash->f_private; 401 | if (pgftl == NULL) { 402 | pr_info("page ftl doesn't exist\n"); 403 | return 0; 404 | } 405 | dev = pgftl->dev; 406 | page_ftl_close(pgftl); 407 | free(pgftl); 408 | 409 | if (dev == NULL) { 410 | pr_info("device module doesn't exist\n"); 411 | return 0; 412 | } 413 | 414 | ret = device_module_exit(dev); 415 | if (ret) { 416 | pr_err("device module exit failed\n"); 417 | return ret; 418 | } 419 | return 0; 420 | } 421 | -------------------------------------------------------------------------------- /device/zone/zone.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file zone.c 3 | * @brief implementation of the lizbd which is inherited by the device 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-09 7 | */ 8 | #ifndef _GNU_SOURCE 9 | #define _GNU_SOURCE 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "zone.h" 20 | #include "device.h" 21 | #include "log.h" 22 | 23 | /** 24 | * @brief open the zoned block deivce file 25 | * 26 | * @param dev pointer of the device structure 27 | * @param name zoned block device's device filename 28 | * @param flags open flags for ramdisk 29 | * 30 | * @return 0 for success, negative number for fail 31 | */ 32 | int zone_open(struct device *dev, const char *name, int flags) 33 | { 34 | struct zone_meta *meta; 35 | struct zbd_info zone_info; 36 | 37 | struct device_info *info = &dev->info; 38 | struct device_package *package = &info->package; 39 | struct device_block *block = &package->block; 40 | struct device_page *page = &block->page; 41 | 42 | unsigned int reported_zones; 43 | unsigned int i; 44 | int ret = 0; 45 | 46 | info->nr_bus = (1 << DEVICE_NR_BUS_BITS); 47 | info->nr_chips = (1 << DEVICE_NR_CHIPS_BITS); 48 | 49 | block->nr_pages = (1 << DEVICE_NR_PAGES_BITS); 50 | page->size = DEVICE_PAGE_SIZE; 51 | 52 | meta = (struct zone_meta *)dev->d_private; 53 | 54 | meta->o_flags = flags; 55 | 56 | meta->read.fd = zbd_open(name, O_RDONLY, &zone_info); 57 | if (meta->read.fd < 0) { 58 | ret = -errno; 59 | pr_err("zone read file descriptor open failed\n"); 60 | goto exception; 61 | } 62 | meta->write.fd = zbd_open(name, O_WRONLY | O_DIRECT, &zone_info); 63 | if (meta->write.fd < 0) { 64 | ret = -errno; 65 | pr_err("zone write file descriptor open failed\n"); 66 | goto exception; 67 | } 68 | meta->total_size = zone_info.nr_zones * zone_info.zone_size; 69 | if (meta->o_flags & O_CREAT) { 70 | ret = zbd_reset_zones(meta->write.fd, 0, meta->total_size); 71 | if (ret) { 72 | pr_err("zone reset failed\n"); 73 | goto exception; 74 | } 75 | } 76 | meta->zone_size = zone_info.zone_size; 77 | meta->nr_zones = zone_info.nr_zones; 78 | meta->block_size = zone_info.pblock_size; 79 | 80 | package->nr_blocks = meta->nr_zones; 81 | 82 | if (meta->zone_size % page->size) { 83 | pr_err("zone size is not aligned %lu bytes", page->size); 84 | ret = -EINVAL; 85 | goto exception; 86 | } 87 | 88 | if (zone_info.model != ZBD_DM_HOST_MANAGED) { 89 | pr_err("not host managed\n"); 90 | ret = -EINVAL; 91 | goto exception; 92 | } 93 | 94 | ret = zbd_list_zones(meta->read.fd, 0, meta->total_size, ZBD_RO_ALL, 95 | &meta->zones, &reported_zones); 96 | if (ret || reported_zones != meta->nr_zones) { 97 | pr_err("failed to list zones, err %d", ret); 98 | goto exception; 99 | } 100 | 101 | for (i = 0; i < reported_zones; i++) { 102 | struct zbd_zone *z = &meta->zones[i]; 103 | if (zbd_zone_type(z) != ZBD_ZONE_TYPE_SWR) { 104 | pr_err("zone type is not sequential mode\n"); 105 | goto exception; 106 | } 107 | } 108 | 109 | return ret; 110 | exception: 111 | zone_close(dev); 112 | return ret; 113 | } 114 | 115 | /** 116 | * @brief execute the read and write function 117 | * 118 | * @param fd the number which contains the file descriptor 119 | * @param flag I/O direction 120 | * @param buffer pointer of the buffer 121 | * @param count the number of byte to read or write 122 | * @param offset position which wants to read or write 123 | * 124 | * @return the number of bytes after read or write, negative number means fail 125 | */ 126 | static ssize_t zone_do_rw(int fd, int flag, void *buffer, size_t count, 127 | off_t offset) 128 | { 129 | size_t remaining = count; 130 | off_t ofst = offset; 131 | ssize_t ret; 132 | 133 | while (remaining) { 134 | if (flag == DEVICE_READ) { 135 | ret = pread(fd, buffer, remaining, ofst); 136 | } else if (flag == DEVICE_WRITE) { 137 | ret = pwrite(fd, buffer, remaining, ofst); 138 | } else { 139 | pr_err("invalid flag detected (flag: %d)\n", flag); 140 | return -EINVAL; 141 | } 142 | if (ret < 0) { 143 | pr_err("%s failed %d (%s)\n", 144 | flag == DEVICE_READ ? "read" : "write", errno, 145 | strerror(errno)); 146 | return -errno; 147 | } 148 | if (!ret) 149 | break; 150 | 151 | remaining -= ret; 152 | ofst += ret; 153 | } 154 | 155 | return count - remaining; 156 | } 157 | 158 | /** 159 | * @brief write to the zoned block device 160 | * 161 | * @param dev pointer of the device structre 162 | * @param request pointer of the user request 163 | * 164 | * @return the number of bytes to write, negative number for fail 165 | */ 166 | ssize_t zone_write(struct device *dev, struct device_request *request) 167 | { 168 | struct zone_meta *meta; 169 | struct zbd_zone *zone; 170 | size_t page_size = device_get_page_size(dev); 171 | ssize_t ret = 0; 172 | uint64_t zone_num; 173 | char *buffer; 174 | 175 | meta = (struct zone_meta *)dev->d_private; 176 | buffer = NULL; 177 | 178 | if (request->data == NULL) { 179 | pr_err("you do not pass the data pointer to NULL\n"); 180 | ret = -ENODATA; 181 | goto exit; 182 | } 183 | if (request->flag != DEVICE_WRITE) { 184 | pr_err("request type is not matched (expected: %u, current: %u)\n", 185 | (unsigned int)DEVICE_WRITE, request->flag); 186 | ret = -EINVAL; 187 | goto exit; 188 | } 189 | if (request->paddr.lpn == PADDR_EMPTY) { 190 | pr_err("physical address is not specified...\n"); 191 | ret = -EINVAL; 192 | goto exit; 193 | } 194 | if (request->data_len != page_size) { 195 | pr_err("data write size is must be %zu (current: %zu)\n", 196 | request->data_len, page_size); 197 | ret = -EINVAL; 198 | goto exit; 199 | } 200 | ret = posix_memalign((void **)&buffer, sysconf(_SC_PAGESIZE), 201 | device_get_page_size(dev)); 202 | if (ret) { 203 | pr_err("buffer allocation failed\n"); 204 | buffer = NULL; 205 | goto exit; 206 | } 207 | memcpy(buffer, request->data, request->data_len); 208 | zone_num = zone_get_zone_number(dev, request->paddr); 209 | if (zone_num >= meta->nr_zones) { 210 | pr_err("invalid address value detected (lpn: %u)\n", 211 | request->paddr.lpn); 212 | ret = -EINVAL; 213 | goto exit; 214 | } 215 | zone = &meta->zones[zone_num]; 216 | if (zone->wp != (request->paddr.lpn * page_size)) { 217 | pr_err("write pointer doesn't match (expected: %lu, actual: %lu, zone: %lu)\n", 218 | (uint64_t)(zone->wp), 219 | (uint64_t)(request->paddr.lpn * page_size), zone_num); 220 | ret = -EINVAL; 221 | goto exit; 222 | } 223 | ret = zone_do_rw(meta->write.fd, request->flag, buffer, 224 | request->data_len, 225 | (off_t)request->paddr.lpn * page_size); 226 | if (ret != (ssize_t)page_size) { 227 | pr_err("do io sequence failed(expected: %ld, actual: %ld)\n", 228 | ret, (ssize_t)page_size); 229 | ret = -EFAULT; 230 | goto exit; 231 | } 232 | zone->wp += ret; 233 | if (zone->wp == zone->start + zone->len) { 234 | int status; 235 | status = zbd_finish_zones(meta->write.fd, zone->start, 236 | zone->len); 237 | if (status) { 238 | pr_err("zone close failed (start:%llu, len: %llu)\n", 239 | zone->start, zone->len); 240 | goto exit; 241 | } 242 | } 243 | if (request->end_rq) { 244 | request->end_rq(request); 245 | } 246 | exit: 247 | if (buffer) { 248 | free(buffer); 249 | } 250 | return ret; 251 | } 252 | 253 | /** 254 | * @brief read to the zoned block device 255 | * 256 | * @param dev pointer of the device structre 257 | * @param request pointer of the user request 258 | * 259 | * @return the number of bytes to read, negative number for fail 260 | */ 261 | ssize_t zone_read(struct device *dev, struct device_request *request) 262 | { 263 | struct zone_meta *meta; 264 | size_t page_size; 265 | ssize_t ret = 0; 266 | uint64_t zone_num; 267 | char *buffer; 268 | 269 | meta = (struct zone_meta *)dev->d_private; 270 | buffer = NULL; 271 | 272 | if (request->data == NULL) { 273 | pr_err("NULL data pointer detected\n"); 274 | ret = -ENODATA; 275 | goto exit; 276 | } 277 | 278 | if (request->flag != DEVICE_READ) { 279 | pr_err("request type is not matched (expected: %u, current: %u)\n", 280 | (unsigned int)DEVICE_READ, request->flag); 281 | } 282 | 283 | page_size = device_get_page_size(dev); 284 | if (request->data_len != page_size) { 285 | pr_err("data read size is must be %zu (current: %zu)\n", 286 | request->data_len, page_size); 287 | ret = -EINVAL; 288 | goto exit; 289 | } 290 | 291 | if (request->paddr.lpn == PADDR_EMPTY) { 292 | pr_err("physical address is not specified...\n"); 293 | ret = -EINVAL; 294 | goto exit; 295 | } 296 | zone_num = zone_get_zone_number(dev, request->paddr); 297 | if (zone_num >= meta->nr_zones) { 298 | pr_err("invalid address value detected (lpn: %u)\n", 299 | request->paddr.lpn); 300 | ret = -EINVAL; 301 | goto exit; 302 | } 303 | ret = posix_memalign((void **)&buffer, sysconf(_SC_PAGESIZE), 304 | device_get_page_size(dev)); 305 | if (ret) { 306 | pr_err("buffer allocation failed\n"); 307 | buffer = NULL; 308 | goto exit; 309 | } 310 | memset(buffer, 0, page_size); 311 | ret = zone_do_rw(meta->read.fd, request->flag, buffer, 312 | request->data_len, 313 | (off_t)request->paddr.lpn * page_size); 314 | memcpy(request->data, buffer, page_size); 315 | if (request && request->end_rq) { 316 | request->end_rq(request); 317 | } 318 | exit: 319 | if (buffer) { 320 | free(buffer); 321 | } 322 | return ret; 323 | } 324 | 325 | /** 326 | * @brief erase the segment to the zoned block device 327 | * 328 | * @param dev pointer of the device structre 329 | * @param request pointer of the user request 330 | * 331 | * @return 0 for success, negative number for fail 332 | */ 333 | int zone_erase(struct device *dev, struct device_request *request) 334 | { 335 | struct zone_meta *meta; 336 | uint64_t zone_num; 337 | int ret = 0; 338 | off_t offset, length; 339 | 340 | meta = (struct zone_meta *)dev->d_private; 341 | zone_num = request->paddr.format.block; 342 | if (zone_num >= meta->nr_zones) { 343 | pr_err("invalid address detected\n"); 344 | ret = -EINVAL; 345 | goto exit; 346 | } 347 | offset = meta->zone_size * zone_num; 348 | length = meta->zone_size; 349 | ret = zbd_reset_zones(meta->write.fd, offset, length); 350 | if (ret) { 351 | pr_err("zone reset failed (%lu ~ %lu)\n", offset, 352 | offset + length); 353 | goto exit; 354 | } 355 | 356 | if (request->end_rq) { 357 | request->end_rq(request); 358 | } 359 | meta->zones[zone_num].wp = meta->zones[zone_num].start; 360 | exit: 361 | return ret; 362 | } 363 | 364 | /** 365 | * @brief close the zoned block device 366 | * 367 | * @param dev pointer of the device structure 368 | * 369 | * @return 0 for success, negative number for fail 370 | */ 371 | int zone_close(struct device *dev) 372 | { 373 | struct zone_meta *meta; 374 | meta = (struct zone_meta *)dev->d_private; 375 | if (meta == NULL) { 376 | return 0; 377 | } 378 | if (meta->read.fd >= 0) { 379 | zbd_close(meta->read.fd); 380 | meta->read.fd = -1; 381 | } 382 | if (meta->write.fd >= 0) { 383 | zbd_close(meta->write.fd); 384 | meta->write.fd = -1; 385 | } 386 | if (meta->zones) { 387 | free(meta->zones); 388 | meta->zones = NULL; 389 | } 390 | return 0; 391 | } 392 | 393 | /** 394 | * @brief zoned block device operations 395 | */ 396 | struct device_operations __zone_dops = { 397 | .open = zone_open, 398 | .write = zone_write, 399 | .read = zone_read, 400 | .erase = zone_erase, 401 | .close = zone_close, 402 | }; 403 | 404 | /** 405 | * @brief initialize the device module 406 | * 407 | * @param dev pointer of the device structure 408 | * @param flags flags for ramdisk and device 409 | * 410 | * @return 0 for sucess, negative value for fail 411 | */ 412 | int zone_device_init(struct device *dev, uint64_t flags) 413 | { 414 | int ret = 0; 415 | struct zone_meta *meta; 416 | 417 | (void)flags; 418 | 419 | meta = (struct zone_meta *)malloc(sizeof(struct zone_meta)); 420 | if (meta == NULL) { 421 | pr_err("memory allocation failed\n"); 422 | ret = -ENOMEM; 423 | goto exception; 424 | } 425 | memset(meta, 0, sizeof(struct zone_meta)); 426 | meta->read.fd = -1; 427 | meta->write.fd = -1; 428 | 429 | dev->d_private = (void *)meta; 430 | dev->d_submodule_exit = zone_device_exit; 431 | dev->d_op = &__zone_dops; 432 | 433 | return ret; 434 | exception: 435 | zone_device_exit(dev); 436 | return ret; 437 | } 438 | 439 | /** 440 | * @brief deallocate the device module 441 | * 442 | * @param dev pointer of the device structure 443 | * 444 | * @return 0 for success, negative value for fail 445 | */ 446 | int zone_device_exit(struct device *dev) 447 | { 448 | struct zone_meta *meta; 449 | meta = (struct zone_meta *)dev->d_private; 450 | if (meta != NULL) { 451 | zone_close(dev); 452 | free(meta); 453 | dev->d_private = NULL; 454 | } 455 | return 0; 456 | } 457 | -------------------------------------------------------------------------------- /ftl/page/page-core.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file page-core.c 3 | * @brief core logic for page ftl 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-09-22 7 | */ 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "page.h" 19 | #include "log.h" 20 | #include "bits.h" 21 | #include "device.h" 22 | #include "lru.h" 23 | #include 24 | 25 | static int is_gc_thread_exit; 26 | 27 | /** 28 | * @brief do garbage collection thread 29 | * 30 | * @param data containing the pointer of the page ftl structure 31 | * 32 | * @return NULL 33 | */ 34 | static void *page_ftl_gc_thread(void *data) 35 | { 36 | struct page_ftl *pgftl; 37 | size_t total_pages; 38 | ssize_t ret; 39 | struct device_request request; 40 | struct timespec req; 41 | 42 | req.tv_sec = 1; 43 | req.tv_nsec = 0; 44 | 45 | pgftl = (struct page_ftl *)data; 46 | assert(NULL != pgftl); 47 | assert(NULL != pgftl->dev); 48 | 49 | memset(&request, 0, sizeof(struct device_request)); 50 | request.flag = DEVICE_ERASE; 51 | 52 | total_pages = device_get_total_pages(pgftl->dev); 53 | ret = 0; 54 | while (1) { 55 | size_t free_pages; 56 | g_assert(nanosleep(&req, NULL) == 0); 57 | if (g_atomic_int_get(&is_gc_thread_exit) == 1) { 58 | break; 59 | } 60 | free_pages = page_ftl_get_free_pages(pgftl); 61 | if ((double)free_pages > 62 | (double)total_pages * PAGE_FTL_GC_THRESHOLD) { 63 | continue; 64 | } 65 | ret = page_ftl_gc_from_list(pgftl, &request, PAGE_FTL_GC_RATIO); 66 | if (ret < 0) { 67 | pr_err("critical garbage collection error detected (errno: %zd)\n", 68 | ret); 69 | break; 70 | } 71 | #ifdef USE_GC_MESSAGE 72 | pr_info("gc triggered (nr_erase: %zd)\n", ret); 73 | #endif 74 | } 75 | return NULL; 76 | } 77 | 78 | /** 79 | * @brief allocate the segment's bitmap 80 | * 81 | * @param pgftl pointer of the page-ftl structure 82 | * @param bitmap double pointer of the bitmap 83 | * 84 | * @return 0 for successfully allocated 85 | */ 86 | static int page_ftl_alloc_bitmap(struct page_ftl *pgftl, uint64_t **bitmap) 87 | { 88 | size_t nr_pages_per_segment; 89 | uint64_t *bits; 90 | 91 | nr_pages_per_segment = device_get_pages_per_segment(pgftl->dev); 92 | bits = (uint64_t *)malloc( 93 | (size_t)BITS_TO_UINT64_ALIGN(nr_pages_per_segment)); 94 | if (bits == NULL) { 95 | pr_err("bitmap allocation failed\n"); 96 | return -ENOMEM; 97 | } 98 | memset(bits, 0, (size_t)BITS_TO_UINT64_ALIGN(nr_pages_per_segment)); 99 | *bitmap = bits; 100 | return 0; 101 | } 102 | 103 | /** 104 | * @brief initialize the page ftl's segment data only 105 | * 106 | * @param pgftl pointer of the page-ftl structure 107 | * @param segment pointer of the target segment 108 | * 109 | * @return 0 for successfully initialized 110 | */ 111 | int page_ftl_segment_data_init(struct page_ftl *pgftl, 112 | struct page_ftl_segment *segment) 113 | { 114 | gint nr_pages_per_segment; 115 | nr_pages_per_segment = (gint)device_get_pages_per_segment(pgftl->dev); 116 | g_atomic_int_set(&segment->nr_free_pages, nr_pages_per_segment); 117 | g_atomic_int_set(&segment->nr_valid_pages, 0); 118 | 119 | memset(segment->use_bits, 0, 120 | (size_t)BITS_TO_UINT64_ALIGN(nr_pages_per_segment)); 121 | if (segment->lpn_list) { 122 | g_list_free(segment->lpn_list); 123 | } 124 | segment->lpn_list = NULL; 125 | return 0; 126 | } 127 | 128 | /** 129 | * @brief initialize each segment's metadata 130 | * 131 | * @param pgftl pointer of the page ftl structure 132 | * 133 | * @return 0 to success, negative value to fail 134 | */ 135 | static int page_ftl_init_segment(struct page_ftl *pgftl) 136 | { 137 | size_t nr_segments; 138 | 139 | struct page_ftl_segment *segments; 140 | 141 | nr_segments = device_get_nr_segments(pgftl->dev); 142 | 143 | segments = (struct page_ftl_segment *)malloc( 144 | sizeof(struct page_ftl_segment) * nr_segments); 145 | if (segments == NULL) { 146 | pr_err("memory allocation failed\n"); 147 | return -ENOMEM; 148 | } 149 | for (size_t i = 0; i < nr_segments; i++) { 150 | segments[i].use_bits = NULL; 151 | } 152 | for (size_t i = 0; i < nr_segments; i++) { 153 | int ret; 154 | ret = page_ftl_alloc_bitmap(pgftl, &segments[i].use_bits); 155 | if (ret) { 156 | pr_err("initialize the use bitmap failed (segnum: %zu)\n", 157 | i); 158 | return ret; 159 | } 160 | segments[i].lpn_list = NULL; 161 | ret = page_ftl_segment_data_init(pgftl, &segments[i]); 162 | if (ret) { 163 | pr_err("initialize the segment data failed (segnum: %zu)\n", 164 | i); 165 | return ret; 166 | } 167 | pr_debug("initialize the segment %zu (bits: %zu, size: %lu)\n", 168 | i, device_get_pages_per_segment(pgftl->dev), 169 | (uint64_t)(device_get_pages_per_segment(pgftl->dev)) / 170 | 8); 171 | } 172 | 173 | pgftl->segments = segments; 174 | return 0; 175 | } 176 | 177 | /** 178 | * @brief initialize the page-ftl's each bus rwlock 179 | * 180 | * @param pgftl pointer of the page-ftl structure 181 | * 182 | * @return 0 to success, negative value to fail 183 | */ 184 | static int page_ftl_init_bus_lock(struct page_ftl *pgftl) 185 | { 186 | struct device *dev; 187 | size_t i; 188 | dev = pgftl->dev; 189 | pgftl->bus_rwlock = (pthread_rwlock_t *)malloc( 190 | sizeof(pthread_rwlock_t) * dev->info.nr_bus); 191 | if (pgftl->bus_rwlock == NULL) { 192 | pr_err("rwlock allocation failed\n"); 193 | return -ENOMEM; 194 | } 195 | 196 | for (i = 0; i < dev->info.nr_bus; i++) { 197 | int err; 198 | err = pthread_rwlock_init(&pgftl->bus_rwlock[i], NULL); 199 | if (err) { 200 | pr_err("rwlock initialize failed\n"); 201 | return -errno; 202 | } 203 | } 204 | return 0; 205 | } 206 | 207 | /** 208 | * @brief initialize the page-ftl's mapping table 209 | * 210 | * @param pgftl pointer of the page-ftl structure 211 | * 212 | * @return 0 to success, negative value to fail 213 | */ 214 | static int page_ftl_init_map(struct page_ftl *pgftl) 215 | { 216 | size_t map_size; 217 | map_size = page_ftl_get_map_size(pgftl); 218 | pgftl->trans_map = (uint32_t *)malloc(map_size); 219 | if (pgftl->trans_map == NULL) { 220 | pr_err("cannot allocate the memory for mapping table\n"); 221 | return -ENOMEM; 222 | } 223 | /** initialize the mapping table */ 224 | for (uint32_t lpn = 0; lpn < map_size / sizeof(uint32_t); lpn++) { 225 | pgftl->trans_map[lpn] = PADDR_EMPTY; 226 | } 227 | return 0; 228 | } 229 | 230 | /** 231 | * @brief allocate the page ftl structure's members 232 | * 233 | * @param pgftl pointer of the page ftl structure 234 | * @param name file's name for open 235 | * @param flags flags for open 236 | * 237 | * @return zero to success, negative number to fail 238 | * 239 | * @todo you must make a recovery process 240 | */ 241 | int page_ftl_open(struct page_ftl *pgftl, const char *name, int flags) 242 | { 243 | int err; 244 | int gc_thread_status; 245 | size_t nr_segments; 246 | 247 | struct device *dev; 248 | 249 | if (!(flags & O_CREAT)) { 250 | pr_warn("current version needs to O_CREAT flag\n"); 251 | } 252 | 253 | assert(NULL != pgftl->dev); 254 | 255 | err = pthread_mutex_init(&pgftl->mutex, NULL); 256 | if (err) { 257 | pr_err("mutex initialize failed\n"); 258 | goto exception; 259 | } 260 | 261 | err = pthread_mutex_init(&pgftl->gc_mutex, NULL); 262 | if (err) { 263 | pr_err("gc_mutex initialize failed\n"); 264 | goto exception; 265 | } 266 | 267 | dev = pgftl->dev; 268 | err = dev->d_op->open(dev, name, flags); 269 | if (err) { 270 | pr_err("device open failed\n"); 271 | err = -EINVAL; 272 | goto exception; 273 | } 274 | 275 | err = page_ftl_init_bus_lock(pgftl); 276 | if (err) { 277 | goto exception; 278 | } 279 | 280 | err = page_ftl_init_map(pgftl); 281 | if (err) { 282 | goto exception; 283 | } 284 | 285 | err = page_ftl_init_segment(pgftl); 286 | if (err) { 287 | goto exception; 288 | } 289 | pgftl->gc_list = NULL; 290 | 291 | nr_segments = device_get_nr_segments(dev); 292 | pgftl->gc_seg_bits = 293 | (uint64_t *)malloc((size_t)BITS_TO_UINT64_ALIGN(nr_segments)); 294 | if (pgftl->gc_seg_bits == NULL) { 295 | pr_err("memory allocation failed\n"); 296 | goto exception; 297 | } 298 | memset(pgftl->gc_seg_bits, 0, 299 | (size_t)BITS_TO_UINT64_ALIGN(nr_segments)); 300 | 301 | pgftl->o_flags = flags; 302 | 303 | g_atomic_int_set(&is_gc_thread_exit, 0); 304 | gc_thread_status = pthread_create(&pgftl->gc_thread, NULL, 305 | page_ftl_gc_thread, (void *)pgftl); 306 | if (gc_thread_status < 0) { 307 | pr_err("garbage collection thread creation failed\n"); 308 | goto exception; 309 | } 310 | 311 | return 0; 312 | 313 | exception: 314 | page_ftl_close(pgftl); 315 | return err; 316 | } 317 | 318 | /** 319 | * @brief submit the request to the valid function 320 | * 321 | * @param pgftl pointer of the page ftl structure 322 | * @param request pointer of the request 323 | * 324 | * @return read and write return the size of the submit, 325 | * fail to return the nugative value 326 | * 327 | * @note 328 | * garbage collection doesn't free the request. 329 | */ 330 | ssize_t page_ftl_submit_request(struct page_ftl *pgftl, 331 | struct device_request *request) 332 | { 333 | ssize_t ret = 0; 334 | if (pgftl == NULL || request == NULL) { 335 | pr_err("null detected (pgftl:%p, request:%p)\n", pgftl, 336 | request); 337 | return -EINVAL; 338 | } 339 | pthread_mutex_lock(&pgftl->gc_mutex); 340 | switch (request->flag) { 341 | case DEVICE_WRITE: 342 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 343 | pthread_rwlock_wrlock(&pgftl->rwlock); 344 | #endif 345 | pthread_mutex_unlock(&pgftl->gc_mutex); 346 | ret = page_ftl_write(pgftl, request); 347 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 348 | pthread_rwlock_unlock(&pgftl->rwlock); 349 | #endif 350 | break; 351 | case DEVICE_READ: 352 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 353 | pthread_rwlock_rdlock(&pgftl->rwlock); 354 | #endif 355 | pthread_mutex_unlock(&pgftl->gc_mutex); 356 | ret = page_ftl_read(pgftl, request); 357 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 358 | pthread_rwlock_unlock(&pgftl->rwlock); 359 | #endif 360 | break; 361 | case DEVICE_ERASE: 362 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 363 | pthread_rwlock_wrlock(&pgftl->rwlock); 364 | #endif 365 | ret = (ssize_t)page_ftl_do_gc(pgftl); 366 | pthread_mutex_unlock(&pgftl->gc_mutex); 367 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 368 | pthread_rwlock_unlock(&pgftl->rwlock); 369 | #endif 370 | break; 371 | default: 372 | pr_err("invalid flag detected: %u\n", request->flag); 373 | return -EINVAL; 374 | } 375 | return ret; 376 | } 377 | 378 | /** 379 | * @brief deallocate the ftl's segments 380 | * 381 | * @param segments pointer of the segment array 382 | */ 383 | static void page_ftl_free_segments(struct page_ftl *pgftl) 384 | { 385 | struct page_ftl_segment *segments = pgftl->segments; 386 | size_t nr_segments; 387 | size_t i; 388 | assert(NULL != segments); 389 | nr_segments = device_get_nr_segments(pgftl->dev); 390 | for (i = 0; i < nr_segments; i++) { 391 | uint64_t *use_bits; 392 | 393 | use_bits = segments[i].use_bits; 394 | 395 | if (use_bits != NULL) { 396 | free(use_bits); 397 | } 398 | 399 | segments[i].use_bits = NULL; 400 | 401 | if (segments[i].lpn_list) { 402 | g_list_free(segments[i].lpn_list); 403 | segments[i].lpn_list = NULL; 404 | } 405 | } 406 | } 407 | 408 | /** 409 | * @brief deallocate the page ftl structure's members 410 | * 411 | * @param pgftl pointer of the page ftl structure 412 | * 413 | * @return zero to success, negative number to fail 414 | */ 415 | int page_ftl_close(struct page_ftl *pgftl) 416 | { 417 | int ret = 0; 418 | long status = 0; 419 | if (pgftl == NULL) { 420 | pr_err("null page ftl structure submitted\n"); 421 | return ret; 422 | } 423 | g_atomic_int_set(&is_gc_thread_exit, 1); 424 | pthread_join(pgftl->gc_thread, (void **)&status); 425 | 426 | pthread_mutex_destroy(&pgftl->mutex); 427 | pthread_mutex_destroy(&pgftl->gc_mutex); 428 | #ifdef PAGE_FTL_USE_GLOBAL_RWLOCK 429 | pthread_rwlock_destroy(&pgftl->rwlock); 430 | #endif 431 | 432 | if (pgftl->segments) { 433 | page_ftl_free_segments(pgftl); 434 | free(pgftl->segments); 435 | pgftl->segments = NULL; 436 | } 437 | 438 | if (pgftl->trans_map) { 439 | free(pgftl->trans_map); 440 | pgftl->trans_map = NULL; 441 | } 442 | 443 | if (pgftl->gc_list) { 444 | g_list_free(pgftl->gc_list); 445 | pgftl->gc_list = NULL; 446 | } 447 | 448 | if (pgftl->gc_seg_bits) { 449 | free(pgftl->gc_seg_bits); 450 | pgftl->gc_seg_bits = NULL; 451 | } 452 | 453 | if (pgftl->dev && pgftl->bus_rwlock) { 454 | size_t i = 0; 455 | size_t nr_bus = pgftl->dev->info.nr_bus; 456 | for (i = 0; i < nr_bus; i++) { 457 | pthread_rwlock_destroy(&pgftl->bus_rwlock[i]); 458 | } 459 | free(pgftl->bus_rwlock); 460 | pgftl->bus_rwlock = NULL; 461 | } 462 | 463 | if (pgftl->dev && pgftl->dev->d_op) { 464 | struct device *dev = pgftl->dev; 465 | ret = dev->d_op->close(dev); 466 | } 467 | return ret; 468 | } 469 | -------------------------------------------------------------------------------- /device/bluedbm/bluedbm.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bluedbm.c 3 | * @brief implementation of the bluedbm abstraction layer which is inherited by the device 4 | * @author Gijun Oh 5 | * @version 0.2 6 | * @date 2021-10-20 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "bluedbm.h" 18 | #include "device.h" 19 | #include "log.h" 20 | #include "bits.h" 21 | 22 | gint *g_badseg_counter = NULL; /**< counter for bad segemnt detection */ 23 | gint *g_erase_counter = NULL; /**< counter for # of erase in the segment*/ 24 | 25 | /** 26 | * @brief end request for the erase 27 | * 28 | * @param segnum erased segment number 29 | * @param is_bad erased segment is bad segment or not 30 | * 31 | * @note 32 | * Do not use the non atomic operation or complex operation in this routine. 33 | * It may occur serious problem. 34 | */ 35 | static void bluedbm_erase_end_request(uint64_t segnum, uint8_t is_bad) 36 | { 37 | if (g_badseg_counter == NULL || g_erase_counter == NULL) { 38 | pr_err("NULL pointer detected\n"); 39 | pr_err("\tg_badseg_counter: %p\n", g_badseg_counter); 40 | pr_err("\tg_erase_counter: %p\n", g_erase_counter); 41 | return; 42 | } 43 | g_atomic_int_inc(&g_erase_counter[segnum]); 44 | if (is_bad) { 45 | g_atomic_int_inc(&g_badseg_counter[segnum]); 46 | } 47 | } 48 | 49 | /** 50 | * @brief clear all segments in the flash board 51 | * 52 | * @param dev pointer of the device structure 53 | * 54 | * @return 0 for success, negative value for fail 55 | */ 56 | static int bluedbm_clear(struct device *dev) 57 | { 58 | struct bluedbm *bdbm; 59 | 60 | struct device_info *info = &dev->info; 61 | struct device_package *package = &info->package; 62 | struct device_block *block = &package->block; 63 | struct device_page *page = &block->page; 64 | struct device_address addr; 65 | 66 | size_t pages_per_segment; 67 | size_t erase_size; 68 | uint32_t segnum; 69 | 70 | bdbm = (struct bluedbm *)dev->d_private; 71 | if (bdbm->mio == NULL) { 72 | pr_err("mio must be specified.\n"); 73 | return -EINVAL; 74 | } 75 | pages_per_segment = device_get_pages_per_segment(dev); 76 | erase_size = pages_per_segment * page->size; 77 | for (segnum = 0; segnum < package->nr_blocks; segnum++) { 78 | addr.lpn = 0; 79 | addr.format.block = segnum; 80 | memio_trim(bdbm->mio, addr.lpn, erase_size, 81 | bluedbm_erase_end_request); 82 | } 83 | return 0; 84 | } 85 | 86 | /** 87 | * @brief wait the erase is finished 88 | * 89 | * @param dev pointer of the device structure 90 | * @param segnum initial position to erase target segment 91 | * @param nr_segments number of segments to erase 92 | * 93 | * @note 94 | * If you fail to execute the erase the target segment, 95 | * this function may block the thread forever. 96 | */ 97 | static void bluedbm_wait_erase_finish(struct device *dev, size_t segnum, 98 | size_t nr_segments) 99 | { 100 | size_t blocks_per_segment; 101 | size_t end_segment = segnum + nr_segments; 102 | blocks_per_segment = device_get_blocks_per_segment(dev); 103 | while (segnum < end_segment) { 104 | gint nr_erased_block; 105 | gint status; 106 | 107 | status = g_atomic_int_get(&g_badseg_counter[segnum]); 108 | if (status) { 109 | set_bit(dev->badseg_bitmap, segnum); 110 | segnum++; 111 | continue; 112 | } 113 | 114 | nr_erased_block = g_atomic_int_get(&g_erase_counter[segnum]); 115 | 116 | if (nr_erased_block == (gint)blocks_per_segment) { 117 | g_atomic_int_set(&g_erase_counter[segnum], 0); 118 | segnum++; 119 | continue; 120 | } 121 | sleep(1); 122 | } 123 | } 124 | 125 | /** 126 | * @brief open the bluedbm based device 127 | * 128 | * @param dev pointer of the device structure 129 | * @param name this does not use in this module 130 | * @param flags open flags for this module 131 | * 132 | * @return 0 for success, negative value to fail 133 | */ 134 | int bluedbm_open(struct device *dev, const char *name, int flags) 135 | { 136 | struct bluedbm *bdbm; 137 | 138 | struct device_info *info = &dev->info; 139 | struct device_package *package = &info->package; 140 | struct device_block *block = &package->block; 141 | struct device_page *page = &block->page; 142 | 143 | int ret; 144 | size_t nr_segments; 145 | 146 | memio_t *mio; 147 | 148 | (void)name; 149 | 150 | info->nr_bus = (1 << DEVICE_NR_BUS_BITS); 151 | info->nr_chips = (1 << DEVICE_NR_CHIPS_BITS); 152 | block->nr_pages = (1 << DEVICE_NR_PAGES_BITS); 153 | page->size = DEVICE_PAGE_SIZE; 154 | 155 | package->nr_blocks = BLUEDBM_NR_BLOCKS; 156 | 157 | nr_segments = device_get_nr_segments(dev); 158 | 159 | bdbm = (struct bluedbm *)dev->d_private; 160 | mio = memio_open(); 161 | if (mio == NULL) { 162 | pr_err("memio open failed\n"); 163 | ret = -EFAULT; 164 | goto exception; 165 | } 166 | bdbm->size = device_get_total_size(dev); 167 | bdbm->o_flags = flags; 168 | bdbm->mio = mio; 169 | 170 | dev->badseg_bitmap = 171 | (uint64_t *)malloc(BITS_TO_UINT64_ALIGN(nr_segments)); 172 | if (dev->badseg_bitmap == NULL) { 173 | pr_err("memory allocation failed\n"); 174 | ret = -ENOMEM; 175 | goto exception; 176 | } 177 | memset(dev->badseg_bitmap, 0, BITS_TO_UINT64_ALIGN(nr_segments)); 178 | 179 | g_erase_counter = (gint *)malloc(nr_segments * sizeof(gint)); 180 | if (g_erase_counter == NULL) { 181 | pr_err("memory allocation failed\n"); 182 | ret = -ENOMEM; 183 | goto exception; 184 | } 185 | memset(g_erase_counter, 0, nr_segments * sizeof(gint)); 186 | 187 | g_badseg_counter = (gint *)malloc(nr_segments * sizeof(gint)); 188 | if (g_badseg_counter == NULL) { 189 | pr_err("memory allocation failed\n"); 190 | ret = -ENOMEM; 191 | goto exception; 192 | } 193 | memset(g_badseg_counter, 0, nr_segments * sizeof(gint)); 194 | 195 | if (bdbm->o_flags & O_CREAT) { 196 | bluedbm_clear(dev); 197 | sleep(1); 198 | bluedbm_wait_erase_finish(dev, 0, nr_segments); 199 | } 200 | 201 | return 0; 202 | exception: 203 | bluedbm_close(dev); 204 | return ret; 205 | } 206 | 207 | /** 208 | * @brief end request for the read/write 209 | * 210 | * @param rw_req read/write request pointer 211 | */ 212 | static void bluedbm_end_rw_request(async_bdbm_req *rw_req) 213 | { 214 | bluedbm_dma_t *dma; 215 | struct device_request *user_rq; 216 | 217 | if (rw_req == NULL) { 218 | pr_warn("NULL rw_req detected\n"); 219 | return; 220 | } 221 | 222 | dma = (bluedbm_dma_t *)rw_req->private_data; 223 | if (dma == NULL) { 224 | pr_warn("NULL request detected (rw_req: %p)\n", rw_req); 225 | free(rw_req); 226 | return; 227 | } 228 | user_rq = (struct device_request *)dma->d_private; 229 | assert(NULL != user_rq); 230 | 231 | switch (rw_req->type) { 232 | case REQTYPE_IO_WRITE: 233 | memio_free_dma(DMA_WRITE_BUF, dma->tag); 234 | break; 235 | case REQTYPE_IO_READ: 236 | memcpy(user_rq->data, dma->data, user_rq->data_len); 237 | memio_free_dma(DMA_READ_BUF, dma->tag); 238 | break; 239 | default: 240 | pr_warn("unknown request type detected (flag: %u)", 241 | rw_req->type); 242 | break; 243 | } 244 | free(dma); 245 | free(rw_req); 246 | 247 | if (user_rq && user_rq->end_rq) { 248 | user_rq->end_rq(user_rq); 249 | } 250 | } 251 | 252 | /** 253 | * @brief write to the flash board 254 | * 255 | * @param dev pointer of the device structure 256 | * @param request pointer of the device request structure 257 | * 258 | * @return written size (bytes) 259 | */ 260 | ssize_t bluedbm_write(struct device *dev, struct device_request *request) 261 | { 262 | bluedbm_dma_t *dma = NULL; 263 | async_bdbm_req *write_rq = NULL; 264 | memio_t *mio; 265 | 266 | struct bluedbm *bdbm; 267 | 268 | size_t page_size; 269 | ssize_t ret = 0; 270 | 271 | uint32_t lpn; 272 | 273 | page_size = device_get_page_size(dev); 274 | bdbm = (struct bluedbm *)dev->d_private; 275 | mio = bdbm->mio; 276 | 277 | if (mio == NULL) { 278 | pr_err("memio global structure doesn't exist\n"); 279 | ret = -EFAULT; 280 | goto exception; 281 | } 282 | 283 | if (request->data == NULL) { 284 | pr_err("you do not pass the data pointer to NULL\n"); 285 | ret = -ENODATA; 286 | goto exception; 287 | } 288 | 289 | if (request->paddr.lpn == PADDR_EMPTY) { 290 | pr_err("physical address is not specified...\n"); 291 | ret = -EINVAL; 292 | goto exception; 293 | } 294 | 295 | if (request->flag != DEVICE_WRITE) { 296 | pr_err("request type is not matched (expected: %u, current: %u)\n", 297 | (unsigned int)DEVICE_WRITE, request->flag); 298 | ret = -EINVAL; 299 | goto exception; 300 | } 301 | 302 | if (request->data_len != page_size) { 303 | pr_err("data write size is must be %zu (current: %zu)\n", 304 | request->data_len, page_size); 305 | ret = -EINVAL; 306 | goto exception; 307 | } 308 | 309 | lpn = request->paddr.lpn; 310 | 311 | dma = (bluedbm_dma_t *)malloc(sizeof(bluedbm_dma_t)); 312 | if (dma == NULL) { 313 | pr_err("dma cannot be allocated\n"); 314 | ret = -errno; 315 | goto exception; 316 | } 317 | dma->tag = memio_alloc_dma(DMA_WRITE_BUF, &dma->data); 318 | dma->d_private = (void *)request; 319 | memcpy(dma->data, request->data, page_size); 320 | 321 | write_rq = (async_bdbm_req *)malloc(sizeof(async_bdbm_req)); 322 | if (write_rq == NULL) { 323 | pr_err("memory allocation failed\n"); 324 | ret = -ENOMEM; 325 | goto exception; 326 | } 327 | write_rq->type = REQTYPE_IO_WRITE; 328 | write_rq->private_data = (void *)dma; 329 | write_rq->end_req = bluedbm_end_rw_request; 330 | 331 | ret = memio_write(mio, lpn, page_size, (uint8_t *)dma->data, false, 332 | (void *)write_rq, dma->tag); 333 | return ret; 334 | exception: 335 | if (write_rq) { 336 | free(write_rq); 337 | } 338 | if (dma) { 339 | free(dma); 340 | } 341 | return ret; 342 | } 343 | 344 | /** 345 | * @brief read from the flash board 346 | * 347 | * @param dev pointer of the device structure 348 | * @param request pointer of the device request structure 349 | * 350 | * @return read size (bytes) 351 | */ 352 | ssize_t bluedbm_read(struct device *dev, struct device_request *request) 353 | { 354 | bluedbm_dma_t *dma = NULL; 355 | async_bdbm_req *read_rq = NULL; 356 | memio_t *mio; 357 | 358 | struct bluedbm *bdbm; 359 | 360 | size_t page_size; 361 | ssize_t ret = 0; 362 | 363 | uint32_t lpn; 364 | 365 | page_size = device_get_page_size(dev); 366 | bdbm = (struct bluedbm *)dev->d_private; 367 | mio = bdbm->mio; 368 | 369 | if (mio == NULL) { 370 | pr_err("memio global structure doesn't exist\n"); 371 | ret = -EFAULT; 372 | goto exception; 373 | } 374 | 375 | if (request->data == NULL) { 376 | pr_err("you do not pass the data pointer to NULL\n"); 377 | ret = -ENODATA; 378 | goto exception; 379 | } 380 | 381 | if (request->flag != DEVICE_READ) { 382 | pr_err("request type is not matched (expected: %u, current: %u)\n", 383 | (unsigned int)DEVICE_READ, request->flag); 384 | ret = -EINVAL; 385 | goto exception; 386 | } 387 | if (request->paddr.lpn == PADDR_EMPTY) { 388 | pr_err("physical address is not specified...\n"); 389 | ret = -EINVAL; 390 | goto exception; 391 | } 392 | if (request->data_len != page_size) { 393 | pr_err("data read size is must be %zu (current: %zu)\n", 394 | request->data_len, page_size); 395 | ret = -EINVAL; 396 | goto exception; 397 | } 398 | 399 | lpn = request->paddr.lpn; 400 | 401 | dma = (bluedbm_dma_t *)malloc(sizeof(bluedbm_dma_t)); 402 | if (dma == NULL) { 403 | pr_err("memory allocation failed\n"); 404 | ret = -ENOMEM; 405 | goto exception; 406 | } 407 | dma->tag = memio_alloc_dma(DMA_READ_BUF, &dma->data); 408 | dma->d_private = (void *)request; 409 | 410 | read_rq = (async_bdbm_req *)malloc(sizeof(async_bdbm_req)); 411 | if (read_rq == NULL) { 412 | pr_err("memory allocation failed\n"); 413 | ret = -ENOMEM; 414 | goto exception; 415 | } 416 | read_rq->type = REQTYPE_IO_READ; 417 | read_rq->private_data = (void *)dma; 418 | read_rq->end_req = bluedbm_end_rw_request; 419 | 420 | ret = memio_read(mio, lpn, page_size, (uint8_t *)dma->data, false, 421 | (void *)read_rq, dma->tag); 422 | return ret; 423 | exception: 424 | if (read_rq) { 425 | free(read_rq); 426 | } 427 | if (dma) { 428 | free(dma); 429 | } 430 | return ret; 431 | } 432 | 433 | /** 434 | * @brief erase a segment 435 | * 436 | * @param dev pointer of the device structure 437 | * @param request pointer of the device request structure 438 | * 439 | * @return 0 for success, negative value for fail 440 | */ 441 | int bluedbm_erase(struct device *dev, struct device_request *request) 442 | { 443 | struct bluedbm *bdbm; 444 | memio_t *mio; 445 | struct device_address addr = request->paddr; 446 | size_t page_size; 447 | size_t segnum; 448 | uint32_t pages_per_segment; 449 | uint32_t lpn; 450 | size_t erase_size; 451 | int ret = 0; 452 | 453 | bdbm = (struct bluedbm *)dev->d_private; 454 | mio = bdbm->mio; 455 | 456 | if (mio == NULL) { 457 | pr_err("memio global structure doesn't exist\n"); 458 | ret = -EFAULT; 459 | goto exception; 460 | } 461 | 462 | if (request->flag != DEVICE_ERASE) { 463 | pr_err("request type is not matched (expected: %u, current: %u)\n", 464 | (unsigned int)DEVICE_ERASE, request->flag); 465 | ret = -EINVAL; 466 | goto exception; 467 | } 468 | 469 | if (request->paddr.lpn == PADDR_EMPTY) { 470 | pr_err("physical address is not specified...\n"); 471 | ret = -EINVAL; 472 | goto exception; 473 | } 474 | 475 | page_size = device_get_page_size(dev); 476 | pages_per_segment = (uint32_t)device_get_pages_per_segment(dev); 477 | segnum = addr.format.block; 478 | addr.lpn = 0; 479 | addr.format.block = segnum; 480 | 481 | if (request->end_rq) { 482 | request->end_rq(request); 483 | } 484 | erase_size = pages_per_segment * page_size; 485 | memio_trim(mio, addr.lpn, erase_size, bluedbm_erase_end_request); 486 | bluedbm_wait_erase_finish(dev, segnum, 1); 487 | return ret; 488 | exception: 489 | return ret; 490 | } 491 | 492 | /** 493 | * @brief close the bluedbm 494 | * 495 | * @param dev pointer of the device structure 496 | * 497 | * @return 0 for success, negative value for fail 498 | */ 499 | int bluedbm_close(struct device *dev) 500 | { 501 | struct bluedbm *bdbm; 502 | bdbm = (struct bluedbm *)dev->d_private; 503 | if (bdbm == NULL) { 504 | return 0; 505 | } 506 | 507 | if (bdbm->mio) { 508 | memio_close(bdbm->mio); 509 | bdbm->mio = NULL; 510 | } 511 | 512 | if (dev->badseg_bitmap) { 513 | free(dev->badseg_bitmap); 514 | dev->badseg_bitmap = NULL; 515 | } 516 | 517 | if (g_erase_counter) { 518 | free(g_erase_counter); 519 | g_erase_counter = NULL; 520 | } 521 | 522 | if (g_badseg_counter) { 523 | free(g_badseg_counter); 524 | g_badseg_counter = NULL; 525 | } 526 | 527 | return 0; 528 | } 529 | 530 | /** 531 | * @brief bluedbm module operations 532 | */ 533 | const struct device_operations __bluedbm_dops = { 534 | .open = bluedbm_open, 535 | .write = bluedbm_write, 536 | .read = bluedbm_read, 537 | .erase = bluedbm_erase, 538 | .close = bluedbm_close, 539 | }; 540 | 541 | /** 542 | * @brief initialize the device and bluedbm module 543 | * 544 | * @param dev pointer of the device structure 545 | * @param flags flags for bluedbm and device 546 | * 547 | * @return 0 for success, negative value for fail 548 | */ 549 | int bluedbm_device_init(struct device *dev, uint64_t flags) 550 | { 551 | int ret = 0; 552 | struct bluedbm *bdbm; 553 | 554 | (void)flags; 555 | bdbm = (struct bluedbm *)malloc(sizeof(struct bluedbm)); 556 | if (bdbm == NULL) { 557 | pr_err("memory allocation failed\n"); 558 | ret = -ENOMEM; 559 | goto exception; 560 | } 561 | memset(bdbm, 0, sizeof(struct bluedbm)); 562 | dev->d_op = &__bluedbm_dops; 563 | dev->d_private = (void *)bdbm; 564 | dev->d_submodule_exit = bluedbm_device_exit; 565 | return ret; 566 | exception: 567 | bluedbm_device_exit(dev); 568 | return ret; 569 | } 570 | 571 | /** 572 | * @brief deallocate the device module 573 | * 574 | * @param dev pointer of the device structure 575 | * 576 | * @return 0 for success, negative value for fail 577 | */ 578 | int bluedbm_device_exit(struct device *dev) 579 | { 580 | struct bluedbm *bdbm; 581 | bdbm = (struct bluedbm *)dev->d_private; 582 | if (bdbm) { 583 | bluedbm_close(dev); 584 | free(bdbm); 585 | dev->d_private = NULL; 586 | } 587 | return 0; 588 | } 589 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 4. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | #AfterExternBlock: false # Unknown to clang-format-5.0 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | #SplitEmptyFunction: true # Unknown to clang-format-4.0 45 | #SplitEmptyRecord: true # Unknown to clang-format-4.0 46 | #SplitEmptyNamespace: true # Unknown to clang-format-4.0 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | #BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | #CompactNamespaces: false # Unknown to clang-format-4.0 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | #FixNamespaceComments: false # Unknown to clang-format-4.0 66 | 67 | # Taken from: 68 | # git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ 69 | # | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ 70 | # | sort | uniq 71 | ForEachMacros: 72 | - 'apei_estatus_for_each_section' 73 | - 'ata_for_each_dev' 74 | - 'ata_for_each_link' 75 | - '__ata_qc_for_each' 76 | - 'ata_qc_for_each' 77 | - 'ata_qc_for_each_raw' 78 | - 'ata_qc_for_each_with_internal' 79 | - 'ax25_for_each' 80 | - 'ax25_uid_for_each' 81 | - '__bio_for_each_bvec' 82 | - 'bio_for_each_bvec' 83 | - 'bio_for_each_bvec_all' 84 | - 'bio_for_each_integrity_vec' 85 | - '__bio_for_each_segment' 86 | - 'bio_for_each_segment' 87 | - 'bio_for_each_segment_all' 88 | - 'bio_list_for_each' 89 | - 'bip_for_each_vec' 90 | - 'bitmap_for_each_clear_region' 91 | - 'bitmap_for_each_set_region' 92 | - 'blkg_for_each_descendant_post' 93 | - 'blkg_for_each_descendant_pre' 94 | - 'blk_queue_for_each_rl' 95 | - 'bond_for_each_slave' 96 | - 'bond_for_each_slave_rcu' 97 | - 'bpf_for_each_spilled_reg' 98 | - 'btree_for_each_safe128' 99 | - 'btree_for_each_safe32' 100 | - 'btree_for_each_safe64' 101 | - 'btree_for_each_safel' 102 | - 'card_for_each_dev' 103 | - 'cgroup_taskset_for_each' 104 | - 'cgroup_taskset_for_each_leader' 105 | - 'cpufreq_for_each_entry' 106 | - 'cpufreq_for_each_entry_idx' 107 | - 'cpufreq_for_each_valid_entry' 108 | - 'cpufreq_for_each_valid_entry_idx' 109 | - 'css_for_each_child' 110 | - 'css_for_each_descendant_post' 111 | - 'css_for_each_descendant_pre' 112 | - 'device_for_each_child_node' 113 | - 'displayid_iter_for_each' 114 | - 'dma_fence_chain_for_each' 115 | - 'do_for_each_ftrace_op' 116 | - 'drm_atomic_crtc_for_each_plane' 117 | - 'drm_atomic_crtc_state_for_each_plane' 118 | - 'drm_atomic_crtc_state_for_each_plane_state' 119 | - 'drm_atomic_for_each_plane_damage' 120 | - 'drm_client_for_each_connector_iter' 121 | - 'drm_client_for_each_modeset' 122 | - 'drm_connector_for_each_possible_encoder' 123 | - 'drm_for_each_bridge_in_chain' 124 | - 'drm_for_each_connector_iter' 125 | - 'drm_for_each_crtc' 126 | - 'drm_for_each_crtc_reverse' 127 | - 'drm_for_each_encoder' 128 | - 'drm_for_each_encoder_mask' 129 | - 'drm_for_each_fb' 130 | - 'drm_for_each_legacy_plane' 131 | - 'drm_for_each_plane' 132 | - 'drm_for_each_plane_mask' 133 | - 'drm_for_each_privobj' 134 | - 'drm_mm_for_each_hole' 135 | - 'drm_mm_for_each_node' 136 | - 'drm_mm_for_each_node_in_range' 137 | - 'drm_mm_for_each_node_safe' 138 | - 'flow_action_for_each' 139 | - 'for_each_acpi_dev_match' 140 | - 'for_each_active_dev_scope' 141 | - 'for_each_active_drhd_unit' 142 | - 'for_each_active_iommu' 143 | - 'for_each_aggr_pgid' 144 | - 'for_each_available_child_of_node' 145 | - 'for_each_bio' 146 | - 'for_each_board_func_rsrc' 147 | - 'for_each_bvec' 148 | - 'for_each_card_auxs' 149 | - 'for_each_card_auxs_safe' 150 | - 'for_each_card_components' 151 | - 'for_each_card_dapms' 152 | - 'for_each_card_pre_auxs' 153 | - 'for_each_card_prelinks' 154 | - 'for_each_card_rtds' 155 | - 'for_each_card_rtds_safe' 156 | - 'for_each_card_widgets' 157 | - 'for_each_card_widgets_safe' 158 | - 'for_each_cgroup_storage_type' 159 | - 'for_each_child_of_node' 160 | - 'for_each_clear_bit' 161 | - 'for_each_clear_bit_from' 162 | - 'for_each_cmsghdr' 163 | - 'for_each_compatible_node' 164 | - 'for_each_component_dais' 165 | - 'for_each_component_dais_safe' 166 | - 'for_each_comp_order' 167 | - 'for_each_console' 168 | - 'for_each_cpu' 169 | - 'for_each_cpu_and' 170 | - 'for_each_cpu_not' 171 | - 'for_each_cpu_wrap' 172 | - 'for_each_dapm_widgets' 173 | - 'for_each_dev_addr' 174 | - 'for_each_dev_scope' 175 | - 'for_each_dma_cap_mask' 176 | - 'for_each_dpcm_be' 177 | - 'for_each_dpcm_be_rollback' 178 | - 'for_each_dpcm_be_safe' 179 | - 'for_each_dpcm_fe' 180 | - 'for_each_drhd_unit' 181 | - 'for_each_dss_dev' 182 | - 'for_each_dtpm_table' 183 | - 'for_each_efi_memory_desc' 184 | - 'for_each_efi_memory_desc_in_map' 185 | - 'for_each_element' 186 | - 'for_each_element_extid' 187 | - 'for_each_element_id' 188 | - 'for_each_endpoint_of_node' 189 | - 'for_each_evictable_lru' 190 | - 'for_each_fib6_node_rt_rcu' 191 | - 'for_each_fib6_walker_rt' 192 | - 'for_each_free_mem_pfn_range_in_zone' 193 | - 'for_each_free_mem_pfn_range_in_zone_from' 194 | - 'for_each_free_mem_range' 195 | - 'for_each_free_mem_range_reverse' 196 | - 'for_each_func_rsrc' 197 | - 'for_each_hstate' 198 | - 'for_each_if' 199 | - 'for_each_iommu' 200 | - 'for_each_ip_tunnel_rcu' 201 | - 'for_each_irq_nr' 202 | - 'for_each_link_codecs' 203 | - 'for_each_link_cpus' 204 | - 'for_each_link_platforms' 205 | - 'for_each_lru' 206 | - 'for_each_matching_node' 207 | - 'for_each_matching_node_and_match' 208 | - 'for_each_member' 209 | - 'for_each_memcg_cache_index' 210 | - 'for_each_mem_pfn_range' 211 | - '__for_each_mem_range' 212 | - 'for_each_mem_range' 213 | - '__for_each_mem_range_rev' 214 | - 'for_each_mem_range_rev' 215 | - 'for_each_mem_region' 216 | - 'for_each_migratetype_order' 217 | - 'for_each_msi_entry' 218 | - 'for_each_msi_entry_safe' 219 | - 'for_each_net' 220 | - 'for_each_net_continue_reverse' 221 | - 'for_each_netdev' 222 | - 'for_each_netdev_continue' 223 | - 'for_each_netdev_continue_rcu' 224 | - 'for_each_netdev_continue_reverse' 225 | - 'for_each_netdev_feature' 226 | - 'for_each_netdev_in_bond_rcu' 227 | - 'for_each_netdev_rcu' 228 | - 'for_each_netdev_reverse' 229 | - 'for_each_netdev_safe' 230 | - 'for_each_net_rcu' 231 | - 'for_each_new_connector_in_state' 232 | - 'for_each_new_crtc_in_state' 233 | - 'for_each_new_mst_mgr_in_state' 234 | - 'for_each_new_plane_in_state' 235 | - 'for_each_new_private_obj_in_state' 236 | - 'for_each_node' 237 | - 'for_each_node_by_name' 238 | - 'for_each_node_by_type' 239 | - 'for_each_node_mask' 240 | - 'for_each_node_state' 241 | - 'for_each_node_with_cpus' 242 | - 'for_each_node_with_property' 243 | - 'for_each_nonreserved_multicast_dest_pgid' 244 | - 'for_each_of_allnodes' 245 | - 'for_each_of_allnodes_from' 246 | - 'for_each_of_cpu_node' 247 | - 'for_each_of_pci_range' 248 | - 'for_each_old_connector_in_state' 249 | - 'for_each_old_crtc_in_state' 250 | - 'for_each_old_mst_mgr_in_state' 251 | - 'for_each_oldnew_connector_in_state' 252 | - 'for_each_oldnew_crtc_in_state' 253 | - 'for_each_oldnew_mst_mgr_in_state' 254 | - 'for_each_oldnew_plane_in_state' 255 | - 'for_each_oldnew_plane_in_state_reverse' 256 | - 'for_each_oldnew_private_obj_in_state' 257 | - 'for_each_old_plane_in_state' 258 | - 'for_each_old_private_obj_in_state' 259 | - 'for_each_online_cpu' 260 | - 'for_each_online_node' 261 | - 'for_each_online_pgdat' 262 | - 'for_each_pci_bridge' 263 | - 'for_each_pci_dev' 264 | - 'for_each_pci_msi_entry' 265 | - 'for_each_pcm_streams' 266 | - 'for_each_physmem_range' 267 | - 'for_each_populated_zone' 268 | - 'for_each_possible_cpu' 269 | - 'for_each_present_cpu' 270 | - 'for_each_prime_number' 271 | - 'for_each_prime_number_from' 272 | - 'for_each_process' 273 | - 'for_each_process_thread' 274 | - 'for_each_prop_codec_conf' 275 | - 'for_each_prop_dai_codec' 276 | - 'for_each_prop_dai_cpu' 277 | - 'for_each_prop_dlc_codecs' 278 | - 'for_each_prop_dlc_cpus' 279 | - 'for_each_prop_dlc_platforms' 280 | - 'for_each_property_of_node' 281 | - 'for_each_registered_fb' 282 | - 'for_each_requested_gpio' 283 | - 'for_each_requested_gpio_in_range' 284 | - 'for_each_reserved_mem_range' 285 | - 'for_each_reserved_mem_region' 286 | - 'for_each_rtd_codec_dais' 287 | - 'for_each_rtd_components' 288 | - 'for_each_rtd_cpu_dais' 289 | - 'for_each_rtd_dais' 290 | - 'for_each_set_bit' 291 | - 'for_each_set_bit_from' 292 | - 'for_each_set_clump8' 293 | - 'for_each_sg' 294 | - 'for_each_sg_dma_page' 295 | - 'for_each_sg_page' 296 | - 'for_each_sgtable_dma_page' 297 | - 'for_each_sgtable_dma_sg' 298 | - 'for_each_sgtable_page' 299 | - 'for_each_sgtable_sg' 300 | - 'for_each_sibling_event' 301 | - 'for_each_subelement' 302 | - 'for_each_subelement_extid' 303 | - 'for_each_subelement_id' 304 | - '__for_each_thread' 305 | - 'for_each_thread' 306 | - 'for_each_unicast_dest_pgid' 307 | - 'for_each_vsi' 308 | - 'for_each_wakeup_source' 309 | - 'for_each_zone' 310 | - 'for_each_zone_zonelist' 311 | - 'for_each_zone_zonelist_nodemask' 312 | - 'fwnode_for_each_available_child_node' 313 | - 'fwnode_for_each_child_node' 314 | - 'fwnode_graph_for_each_endpoint' 315 | - 'gadget_for_each_ep' 316 | - 'genradix_for_each' 317 | - 'genradix_for_each_from' 318 | - 'hash_for_each' 319 | - 'hash_for_each_possible' 320 | - 'hash_for_each_possible_rcu' 321 | - 'hash_for_each_possible_rcu_notrace' 322 | - 'hash_for_each_possible_safe' 323 | - 'hash_for_each_rcu' 324 | - 'hash_for_each_safe' 325 | - 'hctx_for_each_ctx' 326 | - 'hlist_bl_for_each_entry' 327 | - 'hlist_bl_for_each_entry_rcu' 328 | - 'hlist_bl_for_each_entry_safe' 329 | - 'hlist_for_each' 330 | - 'hlist_for_each_entry' 331 | - 'hlist_for_each_entry_continue' 332 | - 'hlist_for_each_entry_continue_rcu' 333 | - 'hlist_for_each_entry_continue_rcu_bh' 334 | - 'hlist_for_each_entry_from' 335 | - 'hlist_for_each_entry_from_rcu' 336 | - 'hlist_for_each_entry_rcu' 337 | - 'hlist_for_each_entry_rcu_bh' 338 | - 'hlist_for_each_entry_rcu_notrace' 339 | - 'hlist_for_each_entry_safe' 340 | - 'hlist_for_each_entry_srcu' 341 | - '__hlist_for_each_rcu' 342 | - 'hlist_for_each_safe' 343 | - 'hlist_nulls_for_each_entry' 344 | - 'hlist_nulls_for_each_entry_from' 345 | - 'hlist_nulls_for_each_entry_rcu' 346 | - 'hlist_nulls_for_each_entry_safe' 347 | - 'i3c_bus_for_each_i2cdev' 348 | - 'i3c_bus_for_each_i3cdev' 349 | - 'ide_host_for_each_port' 350 | - 'ide_port_for_each_dev' 351 | - 'ide_port_for_each_present_dev' 352 | - 'idr_for_each_entry' 353 | - 'idr_for_each_entry_continue' 354 | - 'idr_for_each_entry_continue_ul' 355 | - 'idr_for_each_entry_ul' 356 | - 'in_dev_for_each_ifa_rcu' 357 | - 'in_dev_for_each_ifa_rtnl' 358 | - 'inet_bind_bucket_for_each' 359 | - 'inet_lhash2_for_each_icsk_rcu' 360 | - 'key_for_each' 361 | - 'key_for_each_safe' 362 | - 'klp_for_each_func' 363 | - 'klp_for_each_func_safe' 364 | - 'klp_for_each_func_static' 365 | - 'klp_for_each_object' 366 | - 'klp_for_each_object_safe' 367 | - 'klp_for_each_object_static' 368 | - 'kunit_suite_for_each_test_case' 369 | - 'kvm_for_each_memslot' 370 | - 'kvm_for_each_vcpu' 371 | - 'list_for_each' 372 | - 'list_for_each_codec' 373 | - 'list_for_each_codec_safe' 374 | - 'list_for_each_continue' 375 | - 'list_for_each_entry' 376 | - 'list_for_each_entry_continue' 377 | - 'list_for_each_entry_continue_rcu' 378 | - 'list_for_each_entry_continue_reverse' 379 | - 'list_for_each_entry_from' 380 | - 'list_for_each_entry_from_rcu' 381 | - 'list_for_each_entry_from_reverse' 382 | - 'list_for_each_entry_lockless' 383 | - 'list_for_each_entry_rcu' 384 | - 'list_for_each_entry_reverse' 385 | - 'list_for_each_entry_safe' 386 | - 'list_for_each_entry_safe_continue' 387 | - 'list_for_each_entry_safe_from' 388 | - 'list_for_each_entry_safe_reverse' 389 | - 'list_for_each_entry_srcu' 390 | - 'list_for_each_prev' 391 | - 'list_for_each_prev_safe' 392 | - 'list_for_each_safe' 393 | - 'llist_for_each' 394 | - 'llist_for_each_entry' 395 | - 'llist_for_each_entry_safe' 396 | - 'llist_for_each_safe' 397 | - 'mci_for_each_dimm' 398 | - 'media_device_for_each_entity' 399 | - 'media_device_for_each_intf' 400 | - 'media_device_for_each_link' 401 | - 'media_device_for_each_pad' 402 | - 'nanddev_io_for_each_page' 403 | - 'netdev_for_each_lower_dev' 404 | - 'netdev_for_each_lower_private' 405 | - 'netdev_for_each_lower_private_rcu' 406 | - 'netdev_for_each_mc_addr' 407 | - 'netdev_for_each_uc_addr' 408 | - 'netdev_for_each_upper_dev_rcu' 409 | - 'netdev_hw_addr_list_for_each' 410 | - 'nft_rule_for_each_expr' 411 | - 'nla_for_each_attr' 412 | - 'nla_for_each_nested' 413 | - 'nlmsg_for_each_attr' 414 | - 'nlmsg_for_each_msg' 415 | - 'nr_neigh_for_each' 416 | - 'nr_neigh_for_each_safe' 417 | - 'nr_node_for_each' 418 | - 'nr_node_for_each_safe' 419 | - 'of_for_each_phandle' 420 | - 'of_property_for_each_string' 421 | - 'of_property_for_each_u32' 422 | - 'pci_bus_for_each_resource' 423 | - 'pcl_for_each_chunk' 424 | - 'pcl_for_each_segment' 425 | - 'pcm_for_each_format' 426 | - 'ping_portaddr_for_each_entry' 427 | - 'plist_for_each' 428 | - 'plist_for_each_continue' 429 | - 'plist_for_each_entry' 430 | - 'plist_for_each_entry_continue' 431 | - 'plist_for_each_entry_safe' 432 | - 'plist_for_each_safe' 433 | - 'pnp_for_each_card' 434 | - 'pnp_for_each_dev' 435 | - 'protocol_for_each_card' 436 | - 'protocol_for_each_dev' 437 | - 'queue_for_each_hw_ctx' 438 | - 'radix_tree_for_each_slot' 439 | - 'radix_tree_for_each_tagged' 440 | - 'rb_for_each' 441 | - 'rbtree_postorder_for_each_entry_safe' 442 | - 'rdma_for_each_block' 443 | - 'rdma_for_each_port' 444 | - 'rdma_umem_for_each_dma_block' 445 | - 'resource_list_for_each_entry' 446 | - 'resource_list_for_each_entry_safe' 447 | - 'rhl_for_each_entry_rcu' 448 | - 'rhl_for_each_rcu' 449 | - 'rht_for_each' 450 | - 'rht_for_each_entry' 451 | - 'rht_for_each_entry_from' 452 | - 'rht_for_each_entry_rcu' 453 | - 'rht_for_each_entry_rcu_from' 454 | - 'rht_for_each_entry_safe' 455 | - 'rht_for_each_from' 456 | - 'rht_for_each_rcu' 457 | - 'rht_for_each_rcu_from' 458 | - '__rq_for_each_bio' 459 | - 'rq_for_each_bvec' 460 | - 'rq_for_each_segment' 461 | - 'scsi_for_each_prot_sg' 462 | - 'scsi_for_each_sg' 463 | - 'sctp_for_each_hentry' 464 | - 'sctp_skb_for_each' 465 | - 'shdma_for_each_chan' 466 | - '__shost_for_each_device' 467 | - 'shost_for_each_device' 468 | - 'sk_for_each' 469 | - 'sk_for_each_bound' 470 | - 'sk_for_each_entry_offset_rcu' 471 | - 'sk_for_each_from' 472 | - 'sk_for_each_rcu' 473 | - 'sk_for_each_safe' 474 | - 'sk_nulls_for_each' 475 | - 'sk_nulls_for_each_from' 476 | - 'sk_nulls_for_each_rcu' 477 | - 'snd_array_for_each' 478 | - 'snd_pcm_group_for_each_entry' 479 | - 'snd_soc_dapm_widget_for_each_path' 480 | - 'snd_soc_dapm_widget_for_each_path_safe' 481 | - 'snd_soc_dapm_widget_for_each_sink_path' 482 | - 'snd_soc_dapm_widget_for_each_source_path' 483 | - 'tb_property_for_each' 484 | - 'tcf_exts_for_each_action' 485 | - 'udp_portaddr_for_each_entry' 486 | - 'udp_portaddr_for_each_entry_rcu' 487 | - 'usb_hub_for_each_child' 488 | - 'v4l2_device_for_each_subdev' 489 | - 'v4l2_m2m_for_each_dst_buf' 490 | - 'v4l2_m2m_for_each_dst_buf_safe' 491 | - 'v4l2_m2m_for_each_src_buf' 492 | - 'v4l2_m2m_for_each_src_buf_safe' 493 | - 'virtio_device_for_each_vq' 494 | - 'while_for_each_ftrace_op' 495 | - 'xa_for_each' 496 | - 'xa_for_each_marked' 497 | - 'xa_for_each_range' 498 | - 'xa_for_each_start' 499 | - 'xas_for_each' 500 | - 'xas_for_each_conflict' 501 | - 'xas_for_each_marked' 502 | - 'xbc_array_for_each_value' 503 | - 'xbc_for_each_key_value' 504 | - 'xbc_node_for_each_array_value' 505 | - 'xbc_node_for_each_child' 506 | - 'xbc_node_for_each_key_value' 507 | - 'zorro_for_each_dev' 508 | 509 | #IncludeBlocks: Preserve # Unknown to clang-format-5.0 510 | IncludeCategories: 511 | - Regex: '.*' 512 | Priority: 1 513 | IncludeIsMainRegex: '(Test)?$' 514 | IndentCaseLabels: false 515 | #IndentPPDirectives: None # Unknown to clang-format-5.0 516 | IndentWidth: 8 517 | IndentWrappedFunctionNames: false 518 | JavaScriptQuotes: Leave 519 | JavaScriptWrapImports: true 520 | KeepEmptyLinesAtTheStartOfBlocks: false 521 | MacroBlockBegin: '' 522 | MacroBlockEnd: '' 523 | MaxEmptyLinesToKeep: 1 524 | NamespaceIndentation: None 525 | #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 526 | ObjCBlockIndentWidth: 8 527 | ObjCSpaceAfterProperty: true 528 | ObjCSpaceBeforeProtocolList: true 529 | 530 | # Taken from git's rules 531 | #PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 532 | PenaltyBreakBeforeFirstCallParameter: 30 533 | PenaltyBreakComment: 10 534 | PenaltyBreakFirstLessLess: 0 535 | PenaltyBreakString: 10 536 | PenaltyExcessCharacter: 100 537 | PenaltyReturnTypeOnItsOwnLine: 60 538 | 539 | PointerAlignment: Right 540 | ReflowComments: false 541 | SortIncludes: false 542 | #SortUsingDeclarations: false # Unknown to clang-format-4.0 543 | SpaceAfterCStyleCast: false 544 | SpaceAfterTemplateKeyword: true 545 | SpaceBeforeAssignmentOperators: true 546 | #SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 547 | #SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 548 | SpaceBeforeParens: ControlStatements 549 | #SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 550 | SpaceInEmptyParentheses: false 551 | SpacesBeforeTrailingComments: 1 552 | SpacesInAngles: false 553 | SpacesInContainerLiterals: false 554 | SpacesInCStyleCastParentheses: false 555 | SpacesInParentheses: false 556 | SpacesInSquareBrackets: false 557 | Standard: Cpp03 558 | TabWidth: 8 559 | UseTab: Always 560 | ... 561 | --------------------------------------------------------------------------------