├── logs └── README.txt ├── traces └── readme.md ├── UserCustomizeGuide.md ├── .gitignore ├── src ├── .statusDef.h.swp ├── strategy │ ├── .pore.c.swp │ ├── .losertree4pore.c.swp │ ├── strategies.h │ ├── most.h │ ├── most_cdc.h │ ├── lru.h │ ├── most.h.bak │ ├── for-programmer.md │ ├── lru_private.h │ ├── sac.h │ ├── lru_private.c │ ├── lru.c │ ├── most.c.bak │ ├── most.c │ ├── most_cdc.c │ ├── paul.c.most_+costmodel │ └── sac.c ├── .vscode │ ├── settings.json │ └── c_cpp_properties.json ├── report.h ├── trace2call.h ├── statusDef.h ├── shmlib.h ├── strategy.h ├── timerUtils.h ├── timerUtils.c ├── smr-emulator │ ├── hashtb_pb.h │ ├── emulator_v2.h │ ├── hashtb_pb.c │ └── emulator_v2.c ├── hashtable_utils.h ├── report.c ├── rdtsc.h ├── global.c ├── cache.h ├── global.h ├── hashtable_utils.c ├── shmlib.c ├── trace2call.c ├── main.c └── cache.c ├── .gitattributes ├── scripts ├── quicktest_compare_algorithms.sh └── smr-pb-forceclean.sh ├── lib ├── xstrtoumax.c ├── xstrtol.h └── xstrtol.c ├── Makefile └── README.md /logs/README.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /traces/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /UserCustomizeGuide.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.req 3 | *.o 4 | sac 5 | -------------------------------------------------------------------------------- /src/.statusDef.h.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cds-ruc/sac/HEAD/src/.statusDef.h.swp -------------------------------------------------------------------------------- /src/strategy/.pore.c.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cds-ruc/sac/HEAD/src/strategy/.pore.c.swp -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.req filter=lfs diff=lfs merge=lfs -text 2 | “*.req” filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /src/strategy/.losertree4pore.c.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cds-ruc/sac/HEAD/src/strategy/.losertree4pore.c.swp -------------------------------------------------------------------------------- /src/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "strategies.h": "c", 4 | "pore.h": "c" 5 | }, 6 | "C_Cpp.errorSquiggles": "Disabled" 7 | } -------------------------------------------------------------------------------- /src/strategy/strategies.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRATEGIES_H_ 2 | #define _STRATEGIES_H_ 3 | 4 | #include "lru_private.h" 5 | #include "most.h" 6 | #include "most_cdc.h" 7 | #include "sac.h" 8 | 9 | //#include "oldpore.h" 10 | //#include "pv3.h" 11 | 12 | #endif // _STRATEGIES_H_ 13 | -------------------------------------------------------------------------------- /src/report.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | extern void sac_info(char* format, ...); 5 | extern int sac_warning(char* format, ...); 6 | extern void sac_error_exit(char* format, ...); 7 | extern int sac_log(char* log, FILE* file); 8 | extern void sac_exit(int flag); 9 | -------------------------------------------------------------------------------- /src/strategy/most.h: -------------------------------------------------------------------------------- 1 | #ifndef _MOST_H_ 2 | #define _MOST_H_ 3 | #include "../cache.h" 4 | #include "../global.h" 5 | 6 | extern int Init_most(); 7 | extern int LogIn_most(long despId, SSDBufTag tag, unsigned flag); 8 | extern int Hit_most(long despId, unsigned flag); 9 | extern int LogOut_most(long * out_despid_array, int max_n_batch); 10 | #endif // _PORE_H 11 | -------------------------------------------------------------------------------- /src/trace2call.h: -------------------------------------------------------------------------------- 1 | #define DEBUG 0 2 | /* ---------------------------trace 2 call---------------------------- */ 3 | #ifndef _TRACETOCALL_H 4 | #define _TRACETOCALL_H 1 5 | 6 | #define ACT_READ '0' 7 | #define ACT_WRITE '1' 8 | 9 | //#define _CG_LIMIT 1 10 | 11 | #endif // TRACETOCALL 12 | extern FILE *log_lat; 13 | 14 | extern void trace_to_iocall(FILE * trace, off_t startLBA); 15 | -------------------------------------------------------------------------------- /src/strategy/most_cdc.h: -------------------------------------------------------------------------------- 1 | #ifndef _MOST_CDC_H_ 2 | #define _MOST_CDC_H_ 3 | #include "../cache.h" 4 | #include "../global.h" 5 | 6 | extern int Init_most_cdc(); 7 | extern int LogIn_most_cdc(long despId, SSDBufTag tag, unsigned flag); 8 | extern int Hit_most_cdc(long despId, unsigned flag); 9 | extern int LogOut_most_cdc(long * out_despid_array, int max_n_batch, enum_t_vict suggest_type); 10 | 11 | #endif // _MOST_CDC_H_ 12 | -------------------------------------------------------------------------------- /src/statusDef.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Configure file of SSD-SMR cache system. 3 | * All the switch is turn-off by default. 4 | */ 5 | 6 | 7 | /** Log reporting level **/ 8 | 9 | #define LOG_ALLOW // Log allowed EXCLUSIVELY for 1. Print in Emulator. 10 | #undef LOG_SINGLE_REQ // LEGACY: Print detail time information of each single request. 11 | #undef LOG_IO_LAT // report each io latency. 12 | 13 | 14 | /** Emulator Related **/ 15 | #undef EMULATION_AIO 16 | #define EMU_NO_DISK_IO 17 | 18 | /* Future Features */ 19 | -------------------------------------------------------------------------------- /src/shmlib.h: -------------------------------------------------------------------------------- 1 | #ifndef _SHMLIB_H 2 | #define _SHMLIB_H 1 3 | 4 | extern void* SHM_alloc(char* shm_name,size_t len); 5 | extern int SHM_free(char* shm_name,void* shm_vir_addr,long len); 6 | extern void* SHM_get(char* shm_name,size_t len); 7 | 8 | extern int SHM_lock_n_check(char* lockname); 9 | extern int SHM_unlock(char* shm_name); 10 | 11 | extern int SHM_mutex_init(pthread_mutex_t* lock); 12 | extern void SHM_mutex_lock(pthread_mutex_t* lock); 13 | extern void SHM_mutex_unlock(pthread_mutex_t* lock); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/strategy.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRAtEGY_H_ 2 | #define _STRAtEGY_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | struct Ref_lru_global 8 | { 9 | blksize_t whole_cache_size; // the blocks number of the whole lru cache which shared by all users. 10 | }; 11 | 12 | struct Ref_lru_private 13 | { 14 | //blksize_t maxssd; // this user private lru cache blocks count. 15 | }; 16 | 17 | union StratetyUnion 18 | { 19 | struct Ref_lru_global ref_lru_global; 20 | struct Ref_lru_private ref_lru_private; 21 | }; 22 | 23 | 24 | #endif // _STRAtEGY_H_ 25 | -------------------------------------------------------------------------------- /src/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "macFrameworkPath": [ 10 | "/System/Library/Frameworks", 11 | "/Library/Frameworks" 12 | ], 13 | "compilerPath": "/usr/bin/clang", 14 | "cStandard": "c11", 15 | "cppStandard": "c++17", 16 | "intelliSenseMode": "clang-x64" 17 | } 18 | ], 19 | "version": 4 20 | } -------------------------------------------------------------------------------- /src/timerUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | This Timer Utilities is applied to count the **micro-second** of a process block 3 | */ 4 | #ifndef _TIMER_UTILS_H_ 5 | #define _TIMER_UTILS_H_ 6 | 7 | #include 8 | #include 9 | 10 | typedef struct timeval timeval; 11 | 12 | typedef __useconds_t microsecond_t; 13 | 14 | extern void _TimerLap(timeval* tv); 15 | 16 | extern microsecond_t TimerInterval_MICRO(timeval* tv_start, timeval* tv_stop); 17 | extern double TimerInterval_SECOND(timeval* tv_start, timeval* tv_stop); 18 | 19 | extern double Mirco2Sec(microsecond_t msecond); 20 | extern double Mirco2Milli(microsecond_t msecond); 21 | #endif // _TIMER_UTILS_H_ 22 | -------------------------------------------------------------------------------- /src/timerUtils.c: -------------------------------------------------------------------------------- 1 | #include "timerUtils.h" 2 | 3 | void _TimerLap(timeval* tv) 4 | { 5 | gettimeofday(tv, NULL); 6 | } 7 | 8 | microsecond_t TimerInterval_MICRO(timeval* tv_start, timeval* tv_stop) 9 | { 10 | 11 | return (tv_stop->tv_sec-tv_start->tv_sec)*1000000 + (tv_stop->tv_usec-tv_start->tv_usec); 12 | } 13 | double TimerInterval_SECOND(timeval* tv_start, timeval* tv_stop) 14 | { 15 | return (tv_stop->tv_sec-tv_start->tv_sec) + (tv_stop->tv_usec-tv_start->tv_usec)/1000000.0; 16 | } 17 | double Mirco2Sec(microsecond_t msecond) 18 | { 19 | return msecond/1000000.0; 20 | } 21 | 22 | double Mirco2Milli(microsecond_t msecond) 23 | { 24 | return msecond/1000.0; 25 | } 26 | -------------------------------------------------------------------------------- /src/smr-emulator/hashtb_pb.h: -------------------------------------------------------------------------------- 1 | #ifndef SSDBUFTABLE_H 2 | #define SSDBUFTABLE_H 3 | 4 | #include "emulator_v2.h" 5 | typedef struct SSDHashBucket 6 | { 7 | DespTag hash_key; 8 | long despId; 9 | struct SSDHashBucket *next_item; 10 | } SSDHashBucket; 11 | 12 | extern void initSSDTable(size_t size); 13 | extern unsigned long ssdtableHashcode(DespTag tag); 14 | extern long ssdtableLookup(DespTag tag, unsigned long hash_code); 15 | extern long ssdtableInsert(DespTag tag, unsigned long hash_code, long despId); 16 | extern long ssdtableDelete(DespTag tag, unsigned long hash_code); 17 | extern long ssdtableUpdate(DespTag tag, unsigned long hash_code, long despId); 18 | #endif /* SSDBUFTABLE_H */ 19 | -------------------------------------------------------------------------------- /src/hashtable_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _SSDBUFTABLE_H 2 | #define _SSDBUFTABLE_H 1 3 | 4 | #include "cache.h" 5 | 6 | typedef struct SSDBufHashBucket 7 | { 8 | SSDBufTag hash_key; 9 | long desp_serial_id; 10 | struct SSDBufHashBucket *next_item; 11 | } SSDBufHashBucket; 12 | 13 | extern SSDBufHashBucket* ssd_buf_hashtable; 14 | extern int HashTab_Init(); 15 | extern unsigned long HashTab_GetHashCode(SSDBufTag ssd_buf_tag); 16 | extern long HashTab_Lookup(SSDBufTag ssd_buf_tag, unsigned long hash_code); 17 | extern long HashTab_Insert(SSDBufTag ssd_buf_tag, unsigned long hash_code, long desp_serial_id); 18 | extern long HashTab_Delete(SSDBufTag ssd_buf_tag, unsigned long hash_code); 19 | #endif /* SSDBUFTABLE_H */ 20 | -------------------------------------------------------------------------------- /src/report.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "report.h" 3 | #include "statusDef.h" 4 | #include "unistd.h" 5 | #include 6 | FILE* LogFile; 7 | void sac_info(char* format, ...) 8 | { 9 | printf("process [%d]: ",getpid()); 10 | printf(format); 11 | } 12 | 13 | int sac_warning(char* format, ...) 14 | { 15 | printf("process [%d], errno: %d:",getpid(), errno); 16 | printf(format); 17 | return errno; 18 | } 19 | 20 | void sac_error_exit(char* format, ...) 21 | { 22 | printf("process [%d], errno: %d:",getpid(), errno); 23 | printf(format); 24 | exit(EXIT_FAILURE); 25 | } 26 | 27 | 28 | void sac_exit(int flag) 29 | { 30 | exit(flag); 31 | } 32 | 33 | 34 | int sac_log(char* log, FILE* file) 35 | { 36 | #ifdef LOG_ALLOW 37 | return fwrite(log,strlen(log),1,file); 38 | #endif // LOG_ALLOW 39 | } 40 | -------------------------------------------------------------------------------- /src/strategy/lru.h: -------------------------------------------------------------------------------- 1 | #ifndef _LRU_GLOBAL_H_ 2 | #define _LRU_GLOBAL_H_ 3 | #define DEBUG 0 4 | 5 | /* ---------------------------lru---------------------------- */ 6 | 7 | typedef struct 8 | { 9 | long serial_id; // the corresponding descriptor serial number. 10 | long next_lru; // to link used SSD as LRU 11 | long last_lru; // to link used SSD as LRU 12 | pthread_mutex_t lock; 13 | } StrategyDesp_LRU_global; 14 | 15 | typedef struct 16 | { 17 | long first_lru; // Head of list of LRU 18 | long last_lru; // Tail of list of LRU 19 | pthread_mutex_t lock; 20 | } StrategyCtrl_LRU_global; 21 | 22 | extern int initSSDBufferForLRU(); 23 | extern long Unload_LRUBuf(); 24 | extern int hitInLRUBuffer(long serial_id); 25 | extern void *insertLRUBuffer(long serial_id); 26 | #endif // _LRU_GLOBAL_H_ 27 | -------------------------------------------------------------------------------- /src/strategy/most.h.bak: -------------------------------------------------------------------------------- 1 | #define DEBUG 0 2 | /*----------------------------------Most---------------------------------*/ 3 | #include "band_table.h" 4 | #include "cache.h" 5 | 6 | typedef struct 7 | { 8 | long ssd_buf_id;//ssd buffer location in shared buffer 9 | long next_ssd_buf; 10 | } SSDBufDespForMost; 11 | 12 | typedef struct 13 | { 14 | unsigned long band_num; 15 | unsigned long current_pages; 16 | unsigned long first_page; 17 | } BandDescForMost; 18 | 19 | typedef struct 20 | { 21 | long nbands; // # of cached bands 22 | } SSDBufferStrategyControlForMost; 23 | 24 | 25 | SSDBufDespForMost *ssd_buf_desps_for_most; 26 | BandDescForMost *band_descriptors_for_most; 27 | SSDBufferStrategyControlForMost *ssd_buf_strategy_ctrl_for_most; 28 | BandHashBucket *band_hashtable_for_most; 29 | 30 | extern int initSSDBufferForMost(); 31 | extern int HitMostBuffer(); 32 | extern long LogOutDesp_most(); 33 | extern int LogInMostBuffer(); 34 | -------------------------------------------------------------------------------- /src/smr-emulator/emulator_v2.h: -------------------------------------------------------------------------------- 1 | #ifndef SMR_SSD_CACHE_SMR_EMULATION_H 2 | #define SMR_SSD_CACHE_SMR_EMULATION_H 3 | 4 | #include "../global.h" 5 | #include "../statusDef.h" 6 | 7 | #define DEBUG 0 8 | /* ---------------------------smr simulator---------------------------- */ 9 | #include 10 | 11 | typedef struct 12 | { 13 | off_t offset; 14 | } DespTag; 15 | 16 | typedef struct 17 | { 18 | DespTag tag; 19 | long despId; 20 | int isValid; 21 | } FIFODesc; 22 | 23 | typedef struct 24 | { 25 | unsigned long n_used; 26 | long head, tail; 27 | } FIFOCtrl; 28 | 29 | extern int fd_fifo_part; 30 | extern int fd_smr_part; 31 | extern void InitEmulator(); 32 | extern int simu_smr_read(char *buffer, size_t size, off_t offset); 33 | extern int simu_smr_write(char *buffer, size_t size, off_t offset); 34 | extern void Emu_PrintStatistic(); 35 | extern void Emu_ResetStatisic(); 36 | extern void CloseSMREmu(); 37 | #endif 38 | -------------------------------------------------------------------------------- /src/strategy/for-programmer.md: -------------------------------------------------------------------------------- 1 | ## There are some things i write down. Search those keyword for a quick locating. 2 | *-- AUTHOR: DIANSEN SUN. (diansensun@gmail.com)* 3 | 4 | ### ***ugly way*** 5 | There are few of functions which is not given in properate way, even kind of ugly way. 6 | 7 | So i annotate them by: `// >< #ugly way.` *Have to modify them into a elegant way* 8 | 9 | ### ***version*** 10 | I use the `[alpha/beta]` to mark those functional modules which are in development. 11 | 12 | ### ***About the naming rules of variables..*** 13 | Till now, sorry, i have no idea of how to name a variables according to some specific consistent rule. 14 | 15 | ### *** about Branch *** 16 | I usually `git checkout -b` a new branch for temp test, each of them has the following features required attention: 17 | - Base Branch: Every issue branch has a base branch from which to build extra modification. 18 | - Extra Modifications: we use the `<*FLAG*>` as comment in each modified line. -------------------------------------------------------------------------------- /src/strategy/lru_private.h: -------------------------------------------------------------------------------- 1 | #ifndef _LRU_PRIVATE_H_ 2 | #define _LRU_PRIVATE_H_ 3 | #define DEBUG 0 4 | /* ---------------------------lru---------------------------- */ 5 | 6 | typedef struct 7 | { 8 | long serial_id; // the corresponding descriptor serial number. 9 | long next_self_lru; 10 | long last_self_lru; 11 | int user_id; 12 | pthread_mutex_t lock; 13 | long stamp; 14 | } StrategyDesp_LRU_private; 15 | 16 | typedef struct 17 | { 18 | long first_self_lru; // Head of list of LRU 19 | long last_self_lru; // Tail of list of LRU 20 | pthread_mutex_t lock; 21 | blkcnt_t count; 22 | } StrategyCtrl_LRU_private; 23 | 24 | extern int initSSDBufferFor_LRU_private(); 25 | extern int Unload_Buf_LRU_private(long * out_despid_array, int max_n_batch); 26 | extern int hitInBuffer_LRU_private(long serial_id); 27 | extern int insertBuffer_LRU_private(long serial_id); 28 | #endif // _LRU_PRIVATE_H_ 29 | -------------------------------------------------------------------------------- /scripts/quicktest_compare_algorithms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DATETIME="`date +%Y-%m-%d,%H:%m:%s`" 4 | 5 | cd .. 6 | 7 | type ./sac > /dev/null 2>&1 8 | 9 | CMD_SAC=$? 10 | 11 | if [ $CMD_SAC -ne 0 ]; then 12 | echo "error: No executable program file , please check if the program has been built." 13 | exit 1 14 | fi 15 | 16 | echo "Testing.. SAC" 17 | ./sac --no-real-io --use-emulator --workload 11 --workload-mode W --requests 125000000 --algorithm SAC > ./logs/SAC_${DATETIME}.log 18 | echo "SAC Finished. See: logs/SAC_"${DATETIME}".log" 19 | echo "Testing.. MOST" 20 | ./sac --no-real-io --use-emulator --workload 11 --workload-mode W --requests 125000000 --algorithm MOST > ./logs/MOST_${DATETIME}.log 21 | echo "MOST Finished. See: logs/MOST_"${DATETIME}".log" 22 | echo "Testing.. LRU" 23 | ./sac --no-real-io --use-emulator --workload 11 --workload-mode W --requests 125000000 --algorithm LRU > ./logs/LRU_${DATETIME}.log 24 | echo "LRU Finished. See: logs/LRU_"${DATETIME}".log" 25 | 26 | echo "Done. " 27 | 28 | -------------------------------------------------------------------------------- /src/strategy/sac.h: -------------------------------------------------------------------------------- 1 | #ifndef _SAC_H_ 2 | #define _SAC_H_ 3 | 4 | #include "../cache.h" 5 | #include "../global.h" 6 | 7 | typedef struct Dscptr_sac 8 | { 9 | long serial_id; 10 | SSDBufTag ssd_buf_tag; 11 | unsigned flag; 12 | long pre,next; 13 | long stamp; 14 | unsigned long zoneId; 15 | }Dscptr_sac; 16 | 17 | //typedef struct StrategyCtrl_pore 18 | //{ 19 | // long freeId; // Head of list of free ssds 20 | // long n_used; 21 | //}StrategyCtrl_pore; 22 | 23 | typedef struct ZoneCtrl_pual 24 | { 25 | unsigned int zoneId; 26 | int pagecnt_dirty; 27 | //int pagecnt_clean; 28 | int head,tail; 29 | int OOD_num; 30 | int activate_after_n_cycles; 31 | }ZoneCtrl_pual; 32 | 33 | extern int Init_SAC(); 34 | extern int LogIn_SAC(long despId, SSDBufTag tag, unsigned flag); 35 | extern int Hit_SAC(long despId, unsigned flag); 36 | extern int LogOut_SAC(long * out_despid_array, int max_n_batch, enum_t_vict suggest_type); 37 | 38 | #endif // _SAC_H_ 39 | -------------------------------------------------------------------------------- /lib/xstrtoumax.c: -------------------------------------------------------------------------------- 1 | /* xstrtoumax.c -- A more useful interface to strtoumax. 2 | Copyright 1999 Free Software Foundation, Inc. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2, or (at your option) 7 | any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software Foundation, 16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 17 | 18 | /* Written by Paul Eggert. */ 19 | 20 | #if HAVE_CONFIG_H 21 | # include 22 | #endif 23 | 24 | #if HAVE_INTTYPES_H 25 | # include 26 | #endif 27 | 28 | #define __strtol strtoumax 29 | #define __strtol_t uintmax_t 30 | #define __xstrtol xstrtoumax 31 | #include "xstrtol.c" 32 | -------------------------------------------------------------------------------- /scripts/smr-pb-forceclean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_DEV=$1 4 | 5 | type fio > /dev/null 2>&1 6 | CMD_FIO=$? 7 | 8 | 9 | if [ $CMD_FIO -ne 0 ]; then 10 | echo -e "error: No 'fio' command detected, please ensure you have installed the fio tool and add it into PATH.\n" 11 | exit 12 | fi 13 | 14 | if [ -a "${TEST_DEV}" ]; then 15 | 16 | echo "PB cleanning..." 17 | echo "force cleaning smr PB, fdev=${TEST_DEV}" 18 | #STEP 1. random write 4KB * 250K = 1GB data with target LAB range of 0~8GB. 19 | # to make sure smr PB has RMW all the old data, and PB now is full with the (0~8)GB data. 20 | fio --name=pb_forvceclean --filename=${TEST_DEV} --bs=4K --filesize=8G --size=1G --ioengine=sync --direct=1 --rw=randwrite --numjobs=1 21 | 22 | #STEP 2. seq write to the 0~8GB range. note that, smr knows you are random writing, so it will let your seq write directly flush into target position, do not need to pass by the PB, and FTL will invaild those data in PB. Okay, the PB is cleaned up. 23 | 24 | dd if=/dev/zero of=${TEST_DEV} bs=4K count=2M 25 | sync 26 | echo "PB cleaned up!" 27 | else 28 | echo -e "error: File <${TEST_DEV}> doesn't exist. Please specify the file path of SMR dev by: ./smr-pb-forceclean.sh [FILE].\n" 29 | fi 30 | -------------------------------------------------------------------------------- /src/rdtsc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2011, Spyros Blanas. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * CHANGELOG 20 | * - changed `unsigned long long' declerations to uint64_t and added include 21 | * for . May 2012, Cagri. 22 | * 23 | */ 24 | 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #if !defined(__i386__) && !defined(__x86_64__) && !defined(__sparc__) 32 | #warning No supported architecture found -- timers will return junk. 33 | #endif 34 | 35 | static __inline__ uint64_t curtick() { 36 | uint64_t tick; 37 | #if defined(__i386__) 38 | unsigned long lo, hi; 39 | __asm__ __volatile__ (".byte 0x0f, 0x31" : "=a" (lo), "=d" (hi)); 40 | tick = (uint64_t) hi << 32 | lo; 41 | #elif defined(__x86_64__) 42 | unsigned long lo, hi; 43 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 44 | tick = (uint64_t) hi << 32 | lo; 45 | #elif defined(__sparc__) 46 | __asm__ __volatile__ ("rd %%tick, %0" : "=r" (tick)); 47 | #endif 48 | return tick; 49 | } 50 | 51 | static __inline__ void startTimer(uint64_t* t) { 52 | *t = curtick(); 53 | } 54 | 55 | static __inline__ void stopTimer(uint64_t* t) { 56 | *t = curtick() - *t; 57 | } 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /src/global.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // FILE* 3 | #include 4 | #include 5 | 6 | #include "statusDef.h" 7 | #include "global.h" 8 | 9 | 10 | /* ENV */ 11 | 12 | 13 | /** This user basic info */ 14 | int TraceID = -1; 15 | FILE* TraceFile = NULL; 16 | long int Request_limit = -1; 17 | off_t StartLBA = 0; 18 | int Workload_Mode = IOMODE_RW; // *Default 19 | SSDEvictionStrategy EvictStrategy = SAC; // *Default 20 | long Cycle_Length; 21 | 22 | int NO_REAL_DISK_IO = 0; 23 | int NO_CACHE = 0; 24 | int EMULATION = 0; 25 | 26 | /** ENV**/ 27 | struct RuntimeSTAT* STT; 28 | blksize_t BLKSZ = 4096; 29 | 30 | // Cache Layer 31 | blksize_t NBLOCK_SSD_CACHE = 8000000; // 32GB 32 | blksize_t NTABLE_SSD_CACHE = 8000000; // equal with NBLOCK_SSD_CACHE 33 | 34 | // SMR layer 35 | blksize_t NBLOCK_SMR_PB = 30 * 5000; 36 | blkcnt_t NZONES = 400000;/* size = 8TB */ //194180; // NZONES * ZONESZ = 37 | blksize_t ZONESZ = 5000 * 4096;//20MB // Unit: Byte. 38 | 39 | // Device Files 40 | char* simu_smr_fifo_device = NULL;// "/mnt/smr/pb"; 41 | char* simu_smr_smr_dev_path = NULL;//"/mnt/smr/smr"; 42 | char* smr_dev_path = NULL;//"/mnt/smr/smr-rawdisk"; // /dev/sdc"; 43 | char* cache_dev_path = NULL;//"/mnt/ssd/ssd";//"/mnt/ramdisk/ramdisk";//"/dev/memdiska";// "/mnt/ssd/ssd"; 44 | 45 | int cache_fd = -1; 46 | int smr_fd = -1; 47 | 48 | /* Logs */ 49 | char Log_emu_path[] = "./logs/emulator.log"; 50 | FILE* Log_emu = NULL; 51 | 52 | /** Shared memory variable names **/ 53 | // Note: They are legacy from multi-user version, and are not used in this code. 54 | char* SHM_SSDBUF_STRATEGY_CTRL = "SHM_SSDBUF_STRATEGY_CTRL"; 55 | char* SHM_SSDBUF_STRATEGY_DESP = "SHM_SSDBUF_STRATEGY_DESP"; 56 | 57 | char* SHM_SSDBUF_DESP_CTRL = "SHM_SSDBUF_DESP_CTRL"; 58 | char* SHM_SSDBUF_DESPS = "SHM_SSDBUF_DESPS"; 59 | 60 | char* SHM_SSDBUF_HASHTABLE_CTRL = "SHM_SSDBUF_HASHTABLE_CTRL"; 61 | char* SHM_SSDBUF_HASHTABLE = "SHM_SSDBUF_HASHTABLE"; 62 | char* SHM_SSDBUF_HASHDESPS = "SHM_SSDBUF_HASHDESPS"; 63 | char* SHM_PROCESS_REQ_LOCK = "SHM_PROCESS_REQ_LOCK"; 64 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc -g -g 2 | 3 | ifndef SAC_ROOT 4 | SAC_ROOT=. 5 | SAC_LIB=${SAC_ROOT}/lib 6 | SAC_SRC=${SAC_ROOT}/src 7 | SAC_ALG=${SAC_SRC}/strategy 8 | SAC_EMU=${SAC_SRC}/smr-emulator 9 | 10 | endif 11 | 12 | CFLAGS += -W -pthread 13 | CPPFLAGS += -I$(SAC_ROOT) -I$(SAC_SRC) -I$(SAC_SRC)/smr-emulator -I$(SAC_SRC)/strategy -I$(SAC_LIB) 14 | 15 | RM = rm -rf 16 | #RMSHM = rm -f /dev/shm/* 17 | OBJS = global.o main.o report.o timerUtils.o shmlib.o hashtable_utils.o cache.o trace2call.o sac.o most.o most_cdc.o lru_private.o hashtb_pb.o emulator_v2.o xstrtoumax.o 18 | 19 | 20 | all: $(OBJS) sac 21 | @echo 'Successfully built sac...' 22 | 23 | sac: 24 | $(CC) $(CPPFLAGS) $(CFLAGS) $(OBJS) -lrt -lm -o $@ 25 | global.o: ${SAC_SRC}/global.c 26 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 27 | 28 | xstrtoumax.o: ${SAC_LIB}/xstrtoumax.c 29 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 30 | 31 | report.o: ${SAC_SRC}/report.c 32 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 33 | 34 | shmlib.o: ${SAC_SRC}/shmlib.c 35 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 36 | 37 | timerUtils.o: ${SAC_SRC}/timerUtils.c 38 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 39 | 40 | hashtable_utils.o: ${SAC_SRC}/hashtable_utils.c 41 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 42 | 43 | cache.o: ${SAC_SRC}/cache.c 44 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 45 | 46 | trace2call.o: ${SAC_SRC}/trace2call.c 47 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 48 | 49 | main.o: ${SAC_SRC}/main.c 50 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 51 | 52 | sac.o: ${SAC_ALG}/sac.c 53 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 54 | 55 | most.o: ${SAC_ALG}/most.c 56 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 57 | 58 | most_cdc.o: ${SAC_ALG}/most_cdc.c 59 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 60 | 61 | lru.o: ${SAC_ALG}/lru.c 62 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 63 | 64 | lru_private.o: ${SAC_ALG}/lru_private.c 65 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 66 | 67 | lru_cdc.o: ${SAC_ALG}/lru_cdc.c 68 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 69 | 70 | hashtb_pb.o: ${SAC_EMU}/hashtb_pb.c 71 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 72 | 73 | emulator_v2.o: ${SAC_EMU}/emulator_v2.c 74 | $(CC) $(CPPFLAGS) $(CFLAGS) -c $? 75 | 76 | clean: 77 | $(RM) *.o 78 | $(RM) $(SAC_ROOT)/sac 79 | # $(RMSHM) 80 | -------------------------------------------------------------------------------- /lib/xstrtol.h: -------------------------------------------------------------------------------- 1 | /* A more useful interface to strtol. 2 | Copyright 1995, 1996, 1998, 1999, 2001 Free Software Foundation, Inc. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2, or (at your option) 7 | any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software Foundation, 16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 17 | 18 | #ifndef XSTRTOL_H_ 19 | # define XSTRTOL_H_ 1 20 | 21 | #include 22 | 23 | # if HAVE_INTTYPES_H 24 | # include /* for uintmax_t */ 25 | # endif 26 | 27 | # ifndef PARAMS 28 | # if defined PROTOTYPES || (defined __STDC__ && __STDC__) 29 | # define PARAMS(Args) Args 30 | # else 31 | # define PARAMS(Args) () 32 | # endif 33 | # endif 34 | 35 | # ifndef _STRTOL_ERROR 36 | enum strtol_error 37 | { 38 | LONGINT_OK, LONGINT_INVALID, LONGINT_INVALID_SUFFIX_CHAR, LONGINT_OVERFLOW 39 | }; 40 | typedef enum strtol_error strtol_error; 41 | # endif 42 | 43 | # define _DECLARE_XSTRTOL(name, type) \ 44 | strtol_error \ 45 | name PARAMS ((const char *s, char **ptr, int base, \ 46 | type *val, const char *valid_suffixes)); 47 | _DECLARE_XSTRTOL (xstrtol, long int) 48 | _DECLARE_XSTRTOL (xstrtoul, unsigned long int) 49 | _DECLARE_XSTRTOL (xstrtoimax, intmax_t) 50 | _DECLARE_XSTRTOL (xstrtoumax, uintmax_t) 51 | 52 | # define _STRTOL_ERROR(Exit_code, Str, Argument_type_string, Err) \ 53 | do \ 54 | { \ 55 | switch ((Err)) \ 56 | { \ 57 | case LONGINT_OK: \ 58 | abort (); \ 59 | \ 60 | case LONGINT_INVALID: \ 61 | error ((Exit_code), 0, "invalid %s `%s'", \ 62 | (Argument_type_string), (Str)); \ 63 | break; \ 64 | \ 65 | case LONGINT_INVALID_SUFFIX_CHAR: \ 66 | error ((Exit_code), 0, "invalid character following %s in `%s'", \ 67 | (Argument_type_string), (Str)); \ 68 | break; \ 69 | \ 70 | case LONGINT_OVERFLOW: \ 71 | error ((Exit_code), 0, "%s `%s' too large", \ 72 | (Argument_type_string), (Str)); \ 73 | break; \ 74 | } \ 75 | } \ 76 | while (0) 77 | 78 | # define STRTOL_FATAL_ERROR(Str, Argument_type_string, Err) \ 79 | _STRTOL_ERROR (2, Str, Argument_type_string, Err) 80 | 81 | # define STRTOL_FAIL_WARN(Str, Argument_type_string, Err) \ 82 | _STRTOL_ERROR (0, Str, Argument_type_string, Err) 83 | 84 | #endif /* not XSTRTOL_H_ */ 85 | -------------------------------------------------------------------------------- /src/cache.h: -------------------------------------------------------------------------------- 1 | #define DEBUG 0 2 | /* ---------------------------ssd cache---------------------------- */ 3 | #ifndef _SSD_CACHE_H 4 | #define _SSD_CACHE_H 5 | 6 | #include 7 | #include "global.h" 8 | #include "timerUtils.h" 9 | 10 | /* about using 'shmlib' for MULTIUSER*/ 11 | #ifdef MULTIUSER 12 | #define multi_SHM_alloc(shm_name,len) SHM_alloc(shm_name,len) 13 | #define multi_SHM_free(shm_name,shm_vir_addr,len) SHM_free(shm_name,shm_vir_addr,len) 14 | #define multi_SHM_get(shm_name,len) SHM_get(shm_name,len) 15 | #define multi_SHM_lock_n_check(lockname) SHM_lock_n_check(lockname) 16 | #define multi_SHM_lock(lockname) SHM_lock(lockname) 17 | #define multi_SHM_unlock(lockname) SHM_unlock(lockname) 18 | #define multi_SHM_mutex_init(lock) SHM_mutex_init(lock) 19 | #define multi_SHM_mutex_lock(lock) SHM_mutex_lock(lock) 20 | #define multi_SHM_mutex_unlock(lock) SHM_mutex_unlock(lock) 21 | #else 22 | #define multi_SHM_alloc(shm_name,len) malloc(len) 23 | #define multi_SHM_free(shm_name,shm_vir_addr,len) free(shm_vir_addr) 24 | #define multi_SHM_get(shm_name,len) NULL// unregister 25 | #define multi_SHM_lock_n_check(lockname) 0 //0:success, -1:failure. 26 | #define multi_SHM_lock(lockname) // 27 | #define multi_SHM_unlock(lockname) // 28 | #define multi_SHM_mutex_init(lock) //SHM_mutex_init(lock) 29 | #define multi_SHM_mutex_lock(lock) //SHM_mutex_lock(lock) 30 | #define multi_SHM_mutex_unlock(lock) //SHM_mutex_unlock(lock) 31 | #endif // MULTIUSER 32 | 33 | 34 | typedef struct 35 | { 36 | off_t offset; 37 | } SSDBufTag; 38 | 39 | typedef struct 40 | { 41 | long serial_id; // the serial number of the descriptor corresponding to SSD buffer. 42 | long ssd_buf_id; // SSD buffer location in shared buffer 43 | unsigned ssd_buf_flag; 44 | long next_freessd; // to link the desp serial number of free SSD buffer 45 | SSDBufTag ssd_buf_tag; 46 | pthread_mutex_t lock; // For the fine grain size 47 | } SSDBufDesp; 48 | 49 | typedef struct 50 | { 51 | long n_usedssd; // For eviction 52 | long first_freessd; // Head of list of free ssds 53 | pthread_mutex_t lock; 54 | } SSDBufDespCtrl; 55 | 56 | typedef enum enum_t_vict 57 | { 58 | ENUM_B_Clean, 59 | ENUM_B_Dirty, 60 | ENUM_B_Any 61 | } enum_t_vict; 62 | 63 | extern int IsHit; 64 | extern microsecond_t msec_r_hdd,msec_w_hdd,msec_r_ssd,msec_w_ssd,msec_bw_hdd; 65 | 66 | extern void CacheLayer_Init(); 67 | extern void read_block(off_t offset, char* ssd_buffer); 68 | extern void write_block(off_t offset, char* ssd_buffer); 69 | extern void read_band(off_t offset, char* ssd_buffer); 70 | extern void write_band(off_t offset, char* ssd_buffer); 71 | extern void CopySSDBufTag(SSDBufTag* objectTag, SSDBufTag* sourceTag); 72 | 73 | extern void _LOCK(pthread_mutex_t* lock); 74 | extern void _UNLOCK(pthread_mutex_t* lock); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/global.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include // FILE* 4 | #include "statusDef.h" 5 | 6 | #ifndef _GLOBAL_H 7 | #define _GLOBAL_H 1 8 | 9 | #define SSD_BUF_VALID 0x01 10 | #define SSD_BUF_DIRTY 0x02 11 | 12 | /* ENV */ 13 | 14 | //extern char* PROJ_ROOT; 15 | 16 | 17 | struct RuntimeSTAT 18 | { 19 | /** This user basic info */ 20 | unsigned int batchId; 21 | unsigned int userId; 22 | unsigned int traceId; 23 | int workload_mode; 24 | unsigned long startLBA; 25 | unsigned long trace_req_amount; 26 | /** Runtime strategy refered parameter **/ 27 | //union StratetyUnion strategyRef; 28 | 29 | /** Runtime Statistic **/ 30 | blkcnt_t cacheLimit; 31 | blkcnt_t cacheUsage; 32 | 33 | blkcnt_t reqcnt_s; 34 | blkcnt_t reqcnt_r; 35 | blkcnt_t reqcnt_w; 36 | 37 | blkcnt_t hitnum_s; 38 | blkcnt_t hitnum_r; 39 | blkcnt_t hitnum_w; 40 | 41 | blkcnt_t load_ssd_blocks; 42 | blkcnt_t load_hdd_blocks; 43 | blkcnt_t flush_hdd_blocks; 44 | blkcnt_t flush_ssd_blocks; 45 | blkcnt_t flush_clean_blocks; 46 | 47 | double time_read_ssd; 48 | double time_read_hdd; 49 | double time_write_ssd; 50 | double time_write_hdd; 51 | 52 | blksize_t hashmiss_sum; 53 | blksize_t hashmiss_read; 54 | blksize_t hashmiss_write; 55 | 56 | blkcnt_t wt_hit_rd, rd_hit_wt; 57 | blkcnt_t incache_n_clean, incache_n_dirty; 58 | 59 | /* Emulator infos*/ 60 | double wtrAmp_cur; 61 | double WA_sum; 62 | unsigned long n_RMW; 63 | }; 64 | 65 | #define IOMODE_R 0x01 66 | #define IOMODE_W 0x10 67 | #define IOMODE_RW 0x11 68 | 69 | typedef enum 70 | { 71 | // LRU, 72 | SAC, 73 | MOST, 74 | MOST_CDC, 75 | LRU_private 76 | }SSDEvictionStrategy; 77 | 78 | 79 | /** This user basic info */ 80 | extern int TraceID; 81 | extern FILE* TraceFile; 82 | extern long int Request_limit; 83 | extern off_t StartLBA; 84 | extern int Workload_Mode; 85 | extern SSDEvictionStrategy EvictStrategy; 86 | extern long Cycle_Length; 87 | 88 | extern int NO_REAL_DISK_IO; 89 | extern int NO_CACHE; 90 | extern int EMULATION; 91 | 92 | /** ENV**/ 93 | extern struct RuntimeSTAT* STT; 94 | extern blksize_t BLKSZ; 95 | 96 | // Cache Layer 97 | extern blksize_t NBLOCK_SSD_CACHE; 98 | extern blksize_t NTABLE_SSD_CACHE; // equal with NBLOCK_SSD_CACHE 99 | 100 | // SMR layer 101 | extern blksize_t NBLOCK_SMR_PB; 102 | extern blkcnt_t NZONES; 103 | extern blksize_t ZONESZ; 104 | 105 | // Device Files 106 | extern char* simu_smr_fifo_device; 107 | extern char* simu_smr_smr_dev_path; 108 | extern char* smr_dev_path; 109 | extern char* cache_dev_path; 110 | 111 | extern int cache_fd; 112 | extern int smr_fd; 113 | 114 | /* Logs */ 115 | extern char Log_emu_path[]; 116 | extern FILE* Log_emu; 117 | 118 | /** Shared memory variable names **/ 119 | // Note: They are legacy from multi-user version, and are not used in this code. 120 | extern char* SHM_SSDBUF_STRATEGY_CTRL; 121 | extern char* SHM_SSDBUF_STRATEGY_DESP; 122 | 123 | extern char* SHM_SSDBUF_DESP_CTRL; 124 | extern char* SHM_SSDBUF_DESPS; 125 | 126 | extern char* SHM_SSDBUF_HASHTABLE_CTRL; 127 | extern char* SHM_SSDBUF_HASHTABLE; 128 | extern char* SHM_SSDBUF_HASHDESPS; 129 | extern char* SHM_PROCESS_REQ_LOCK; 130 | 131 | #endif //_GLOBAL_H 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/smr-emulator/hashtb_pb.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "hashtb_pb.h" 5 | #include "emulator_v2.h" 6 | #include "../cache.h" 7 | 8 | static SSDHashBucket* HashTable; 9 | static SSDHashBucket* HashItemPool,* FreeItem_hdr; 10 | static SSDHashBucket* memPop(); 11 | static void memPush(SSDHashBucket* item); 12 | 13 | #define GetSSDHashBucket(hash_code) ((SSDHashBucket *) (HashTable + (unsigned long) (hash_code))) 14 | 15 | #define IsSame(T1, T2) ((T1.offset == T2.offset) ? 1 : 0) 16 | 17 | 18 | 19 | 20 | void initSSDTable(size_t size) 21 | { 22 | HashTable = (SSDHashBucket*)malloc(sizeof(SSDHashBucket)*size); 23 | HashItemPool = (SSDHashBucket*)malloc(sizeof(SSDHashBucket)*size); 24 | FreeItem_hdr = HashItemPool; 25 | 26 | size_t i; 27 | SSDHashBucket* bucket = HashTable; 28 | SSDHashBucket* freeitem = FreeItem_hdr; 29 | for (i = 0; i < size; bucket++, freeitem++, i++){ 30 | freeitem->despId = bucket->despId = -1; 31 | freeitem->hash_key.offset = bucket->hash_key.offset = -1; 32 | bucket->next_item = NULL; 33 | freeitem->next_item = freeitem + 1; 34 | } 35 | FreeItem_hdr[size - 1].next_item = NULL; 36 | } 37 | 38 | unsigned long ssdtableHashcode(DespTag tag) 39 | { 40 | unsigned long ssd_hash = (tag.offset / BLKSZ) % NBLOCK_SMR_PB; 41 | return ssd_hash; 42 | } 43 | 44 | long ssdtableLookup(DespTag tag, unsigned long hash_code) 45 | { 46 | if (DEBUG) 47 | printf("[INFO] Lookup tag: %lu\n",tag.offset); 48 | SSDHashBucket *nowbucket = GetSSDHashBucket(hash_code); 49 | while (nowbucket != NULL) { 50 | // printf("nowbucket->buf_id = %u %u %u\n", nowbucket->hash_key.rel.database, nowbucket->hash_key.rel.relation, nowbucket->hash_key.block_num); 51 | if (IsSame(nowbucket->hash_key, tag)) { 52 | // printf("find\n"); 53 | return nowbucket->despId; 54 | } 55 | nowbucket = nowbucket->next_item; 56 | } 57 | // printf("no find\n"); 58 | 59 | return -1; 60 | } 61 | 62 | long ssdtableInsert(DespTag tag, unsigned long hash_code, long despId) 63 | { 64 | if (DEBUG) 65 | printf("[INFO] Insert tag: %lu, hash_code=%lu\n",tag.offset, hash_code); 66 | SSDHashBucket *nowbucket = GetSSDHashBucket(hash_code); 67 | while (nowbucket->next_item != NULL) { 68 | nowbucket = nowbucket->next_item; 69 | } 70 | 71 | SSDHashBucket* newitem = memPop(); 72 | newitem->hash_key = tag; 73 | newitem->despId = despId; 74 | newitem->next_item = NULL; 75 | nowbucket->next_item = newitem; 76 | 77 | return 0; 78 | } 79 | 80 | long ssdtableDelete(DespTag tag, unsigned long hash_code) 81 | { 82 | if (DEBUG) 83 | printf("[INFO] Delete tag: %lu, hash_code=%lu\n",tag.offset, hash_code); 84 | SSDHashBucket *nowbucket = GetSSDHashBucket(hash_code); 85 | long del_id; 86 | SSDHashBucket *delitem; 87 | 88 | while (nowbucket->next_item != NULL) { 89 | if (IsSame(nowbucket->next_item->hash_key, tag)) { 90 | delitem = nowbucket->next_item; 91 | del_id = delitem->despId; 92 | nowbucket->next_item = delitem->next_item; 93 | memPush(delitem); 94 | return del_id; 95 | } 96 | nowbucket = nowbucket->next_item; 97 | } 98 | 99 | return -1; 100 | } 101 | 102 | long ssdtableUpdate(DespTag tag, unsigned long hash_code, long despId) 103 | { 104 | if (DEBUG) 105 | printf("[INFO] Insert tag: %lu, hash_code=%lu\n",tag.offset, hash_code); 106 | SSDHashBucket* nowbucket = GetSSDHashBucket(hash_code); 107 | SSDHashBucket* lastbucket = nowbucket; 108 | while (nowbucket != NULL) { 109 | lastbucket = nowbucket; 110 | if (IsSame(nowbucket->hash_key,tag)) { 111 | long oldId = nowbucket->despId; 112 | nowbucket->despId = despId; 113 | return oldId; 114 | } 115 | nowbucket = nowbucket->next_item; 116 | } 117 | 118 | // if not exist in table, insert one. 119 | 120 | SSDHashBucket* newitem = memPop(); 121 | newitem->hash_key = tag; 122 | newitem->despId = despId; 123 | newitem->next_item = NULL; 124 | lastbucket->next_item = newitem; 125 | return -1; 126 | } 127 | 128 | static SSDHashBucket* memPop(){ 129 | if(FreeItem_hdr == NULL) 130 | { 131 | printf("SIMU: fifo hashtale poll overflow!\n"); 132 | exit(-1); 133 | } 134 | SSDHashBucket* item = FreeItem_hdr; 135 | FreeItem_hdr = FreeItem_hdr->next_item; 136 | return item; 137 | } 138 | static void memPush(SSDHashBucket* item){ 139 | item->next_item = FreeItem_hdr; 140 | FreeItem_hdr = item; 141 | } 142 | -------------------------------------------------------------------------------- /src/hashtable_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "shmlib.h" 4 | #include "cache.h" 5 | #include "hashtable_utils.h" 6 | #include "global.h" 7 | #include "mcheck.h" 8 | 9 | #define GetSSDBufHashBucket(hash_code) ((SSDBufHashBucket *) (ssd_buf_hashtable + (unsigned) (hash_code))) 10 | #define isSameTag(tag1,tag2) (tag1.offset == tag2.offset) 11 | extern void _LOCK(pthread_mutex_t* lock); 12 | extern void _UNLOCK(pthread_mutex_t* lock); 13 | 14 | SSDBufHashBucket* ssd_buf_hashtable; 15 | 16 | static SSDBufHashBucket* hashitem_freelist; 17 | static SSDBufHashBucket* topfree_ptr; 18 | static SSDBufHashBucket* buckect_alloc(); 19 | 20 | static long insertCnt,deleteCnt; 21 | 22 | static void freebucket(SSDBufHashBucket* bucket); 23 | int HashTab_Init() 24 | { 25 | insertCnt = deleteCnt = 0; 26 | ssd_buf_hashtable = (SSDBufHashBucket*)malloc(sizeof(SSDBufHashBucket)*NTABLE_SSD_CACHE); 27 | hashitem_freelist = (SSDBufHashBucket*)malloc(sizeof(SSDBufHashBucket)*NTABLE_SSD_CACHE); 28 | topfree_ptr = hashitem_freelist; 29 | 30 | if(ssd_buf_hashtable == NULL || hashitem_freelist == NULL) 31 | return -1; 32 | 33 | SSDBufHashBucket* bucket = ssd_buf_hashtable; 34 | SSDBufHashBucket* freebucket = hashitem_freelist; 35 | blksize_t i = 0; 36 | for(i = 0; i < NBLOCK_SSD_CACHE; bucket++, freebucket++, i++) 37 | { 38 | bucket->desp_serial_id = freebucket->desp_serial_id = -1; 39 | bucket->hash_key.offset = freebucket->hash_key.offset = -1; 40 | bucket->next_item = NULL; 41 | freebucket->next_item = freebucket + 1; 42 | } 43 | hashitem_freelist[NBLOCK_SSD_CACHE - 1].next_item = NULL; 44 | return 0; 45 | } 46 | 47 | unsigned long HashTab_GetHashCode(SSDBufTag ssd_buf_tag) 48 | { 49 | unsigned long hashcode = (ssd_buf_tag.offset / BLKSZ) % NTABLE_SSD_CACHE; 50 | return hashcode; 51 | } 52 | 53 | long HashTab_Lookup(SSDBufTag ssd_buf_tag, unsigned long hash_code) 54 | { 55 | if (DEBUG) 56 | printf("[INFO] Lookup ssd_buf_tag: %lu\n",ssd_buf_tag.offset); 57 | SSDBufHashBucket *nowbucket = GetSSDBufHashBucket(hash_code); 58 | while (nowbucket != NULL) 59 | { 60 | if (isSameTag(nowbucket->hash_key, ssd_buf_tag)) 61 | { 62 | return nowbucket->desp_serial_id; 63 | } 64 | nowbucket = nowbucket->next_item; 65 | } 66 | 67 | return -1; 68 | } 69 | 70 | long HashTab_Insert(SSDBufTag ssd_buf_tag, unsigned long hash_code, long desp_serial_id) 71 | { 72 | if (DEBUG) 73 | printf("[INFO] Insert buf_tag: %lu\n",ssd_buf_tag.offset); 74 | 75 | insertCnt++; 76 | //printf("hashitem alloc times:%d\n",insertCnt); 77 | 78 | SSDBufHashBucket *nowbucket = GetSSDBufHashBucket(hash_code); 79 | if(nowbucket == NULL) 80 | { 81 | printf("[ERROR] Insert HashBucket: Cannot get HashBucket.\n"); 82 | exit(1); 83 | } 84 | while (nowbucket->next_item != NULL) 85 | { 86 | nowbucket = nowbucket->next_item; 87 | } 88 | 89 | SSDBufHashBucket* newitem; 90 | if((newitem = buckect_alloc()) == NULL) 91 | { 92 | printf("hash bucket alloc failure\n"); 93 | exit(-1); 94 | } 95 | newitem->hash_key = ssd_buf_tag; 96 | newitem->desp_serial_id = desp_serial_id; 97 | newitem->next_item = NULL; 98 | 99 | nowbucket->next_item = newitem; 100 | return 0; 101 | } 102 | 103 | long HashTab_Delete(SSDBufTag ssd_buf_tag, unsigned long hash_code) 104 | { 105 | if (DEBUG) 106 | printf("[INFO] Delete buf_tag: %lu\n",ssd_buf_tag.offset); 107 | 108 | deleteCnt++; 109 | //printf("hashitem free times:%d\n",deleteCnt++); 110 | 111 | long del_id; 112 | SSDBufHashBucket *delitem; 113 | SSDBufHashBucket *nowbucket = GetSSDBufHashBucket(hash_code); 114 | 115 | while (nowbucket->next_item != NULL) 116 | { 117 | if (isSameTag(nowbucket->next_item->hash_key, ssd_buf_tag)) 118 | { 119 | delitem = nowbucket->next_item; 120 | del_id = delitem->desp_serial_id; 121 | nowbucket->next_item = delitem->next_item; 122 | freebucket(delitem); 123 | return del_id; 124 | } 125 | nowbucket = nowbucket->next_item; 126 | } 127 | return -1; 128 | } 129 | 130 | static SSDBufHashBucket* buckect_alloc() 131 | { 132 | if(topfree_ptr == NULL) 133 | return NULL; 134 | SSDBufHashBucket* freebucket = topfree_ptr; 135 | topfree_ptr = topfree_ptr->next_item; 136 | return freebucket; 137 | } 138 | 139 | static void freebucket(SSDBufHashBucket* bucket) 140 | { 141 | bucket->next_item = topfree_ptr; 142 | topfree_ptr = bucket; 143 | } 144 | -------------------------------------------------------------------------------- /src/strategy/lru_private.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../global.h" 5 | #include "../cache.h" 6 | #include "lru.h" 7 | #include "lru_private.h" 8 | #include "../shmlib.h" 9 | 10 | #define EVICT_DITRY_GRAIN 64 11 | 12 | /******** 13 | ** SHM** 14 | ********/ 15 | 16 | static StrategyCtrl_LRU_private *self_strategy_ctrl; 17 | static StrategyDesp_LRU_private *strategy_desp; 18 | 19 | static volatile void *addToLRUHead(StrategyDesp_LRU_private * ssd_buf_hdr_for_lru); 20 | static volatile void *deleteFromLRU(StrategyDesp_LRU_private * ssd_buf_hdr_for_lru); 21 | static volatile void *moveToLRUHead(StrategyDesp_LRU_private * ssd_buf_hdr_for_lru); 22 | 23 | /* 24 | * init buffer hash table, Strategy_control, buffer, work_mem 25 | */ 26 | int 27 | initSSDBufferFor_LRU_private() 28 | { 29 | int stat = multi_SHM_lock_n_check("LOCK_SSDBUF_STRATEGY_LRU"); 30 | if(stat == 0) 31 | { 32 | strategy_desp = (StrategyDesp_LRU_private *)multi_SHM_alloc(SHM_SSDBUF_STRATEGY_DESP, sizeof(StrategyDesp_LRU_private) * NBLOCK_SSD_CACHE); 33 | 34 | StrategyDesp_LRU_private *ssd_buf_hdr_for_lru = strategy_desp; 35 | long i; 36 | for (i = 0; i < NBLOCK_SSD_CACHE; ssd_buf_hdr_for_lru++, i++) 37 | { 38 | ssd_buf_hdr_for_lru->serial_id = i; 39 | ssd_buf_hdr_for_lru->next_self_lru = -1; 40 | ssd_buf_hdr_for_lru->last_self_lru = -1; 41 | multi_SHM_mutex_init(&ssd_buf_hdr_for_lru->lock); 42 | } 43 | } 44 | else 45 | { 46 | strategy_desp = (StrategyDesp_LRU_private *)multi_SHM_get(SHM_SSDBUF_STRATEGY_DESP, sizeof(StrategyDesp_LRU_private) * NBLOCK_SSD_CACHE); 47 | } 48 | multi_SHM_unlock("LOCK_SSDBUF_STRATEGY_LRU"); 49 | 50 | self_strategy_ctrl = (StrategyCtrl_LRU_private *)malloc(sizeof(StrategyCtrl_LRU_private)); 51 | self_strategy_ctrl->first_self_lru = -1; 52 | self_strategy_ctrl->last_self_lru = -1; 53 | 54 | return stat; 55 | } 56 | 57 | int 58 | Unload_Buf_LRU_private(long * out_despid_array, int max_n_batch) 59 | { 60 | int cnt = 0; 61 | while(cnt < EVICT_DITRY_GRAIN && cnt < max_n_batch) 62 | { 63 | long frozen_id = self_strategy_ctrl->last_self_lru; 64 | deleteFromLRU(&strategy_desp[frozen_id]); 65 | out_despid_array[cnt] = frozen_id; 66 | cnt ++ ; 67 | } 68 | 69 | return cnt; 70 | } 71 | 72 | int 73 | hitInBuffer_LRU_private(long serial_id) 74 | { 75 | StrategyDesp_LRU_private* ssd_buf_hdr_for_lru = &strategy_desp[serial_id]; 76 | moveToLRUHead(ssd_buf_hdr_for_lru); 77 | return 0; 78 | } 79 | 80 | int 81 | insertBuffer_LRU_private(long serial_id) 82 | { 83 | // strategy_desp[serial_id].user_id = UserId; 84 | addToLRUHead(&strategy_desp[serial_id]); 85 | 86 | return 0; 87 | } 88 | 89 | static volatile void * 90 | addToLRUHead(StrategyDesp_LRU_private* ssd_buf_hdr_for_lru) 91 | { 92 | //deal with self LRU queue 93 | if(self_strategy_ctrl->last_self_lru < 0) 94 | { 95 | self_strategy_ctrl->first_self_lru = ssd_buf_hdr_for_lru->serial_id; 96 | self_strategy_ctrl->last_self_lru = ssd_buf_hdr_for_lru->serial_id; 97 | } 98 | else 99 | { 100 | ssd_buf_hdr_for_lru->next_self_lru = strategy_desp[self_strategy_ctrl->first_self_lru].serial_id; 101 | ssd_buf_hdr_for_lru->last_self_lru = -1; 102 | strategy_desp[self_strategy_ctrl->first_self_lru].last_self_lru = ssd_buf_hdr_for_lru->serial_id; 103 | self_strategy_ctrl->first_self_lru = ssd_buf_hdr_for_lru->serial_id; 104 | } 105 | return NULL; 106 | } 107 | 108 | static volatile void * 109 | deleteFromLRU(StrategyDesp_LRU_private * ssd_buf_hdr_for_lru) 110 | { 111 | //deal with self queue 112 | if(ssd_buf_hdr_for_lru->last_self_lru>=0) 113 | { 114 | strategy_desp[ssd_buf_hdr_for_lru->last_self_lru].next_self_lru = ssd_buf_hdr_for_lru->next_self_lru; 115 | } 116 | else 117 | { 118 | self_strategy_ctrl->first_self_lru = ssd_buf_hdr_for_lru->next_self_lru; 119 | } 120 | 121 | if(ssd_buf_hdr_for_lru->next_self_lru>=0) 122 | { 123 | strategy_desp[ssd_buf_hdr_for_lru->next_self_lru].last_self_lru = ssd_buf_hdr_for_lru->last_self_lru; 124 | } 125 | else 126 | { 127 | self_strategy_ctrl->last_self_lru = ssd_buf_hdr_for_lru->last_self_lru; 128 | } 129 | 130 | ssd_buf_hdr_for_lru->last_self_lru = ssd_buf_hdr_for_lru->next_self_lru = -1; 131 | 132 | return NULL; 133 | } 134 | 135 | static volatile void * 136 | moveToLRUHead(StrategyDesp_LRU_private * ssd_buf_hdr_for_lru) 137 | { 138 | deleteFromLRU(ssd_buf_hdr_for_lru); 139 | addToLRUHead(ssd_buf_hdr_for_lru); 140 | return NULL; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/strategy/lru.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../global.h" 5 | #include "lru.h" 6 | #include "../shmlib.h" 7 | #include "../cache.h" 8 | /******** 9 | ** SHM** 10 | ********/ 11 | static StrategyCtrl_LRU_global *strategy_ctrl; 12 | static StrategyDesp_LRU_global *strategy_desp; 13 | 14 | static volatile void *addToLRUHead(StrategyDesp_LRU_global * ssd_buf_hdr_for_lru); 15 | static volatile void *deleteFromLRU(StrategyDesp_LRU_global * ssd_buf_hdr_for_lru); 16 | static volatile void *moveToLRUHead(StrategyDesp_LRU_global * ssd_buf_hdr_for_lru); 17 | static int hasBeenDeleted(StrategyDesp_LRU_global* ssd_buf_hdr_for_lru); 18 | /* 19 | * init buffer hash table, Strategy_control, buffer, work_mem 20 | */ 21 | int 22 | initSSDBufferForLRU() 23 | { 24 | int stat = SHM_lock_n_check("LOCK_SSDBUF_STRATEGY_LRU"); 25 | if(stat == 0) 26 | { 27 | strategy_ctrl =(StrategyCtrl_LRU_global *)SHM_alloc(SHM_SSDBUF_STRATEGY_CTRL,sizeof(StrategyCtrl_LRU_global)); 28 | strategy_desp = (StrategyDesp_LRU_global *)SHM_alloc(SHM_SSDBUF_STRATEGY_DESP, sizeof(StrategyDesp_LRU_global) * NBLOCK_SSD_CACHE); 29 | 30 | strategy_ctrl->first_lru = -1; 31 | strategy_ctrl->last_lru = -1; 32 | SHM_mutex_init(&strategy_ctrl->lock); 33 | 34 | StrategyDesp_LRU_global *ssd_buf_hdr_for_lru = strategy_desp; 35 | long i; 36 | for (i = 0; i < NBLOCK_SSD_CACHE; ssd_buf_hdr_for_lru++, i++) 37 | { 38 | ssd_buf_hdr_for_lru->serial_id = i; 39 | ssd_buf_hdr_for_lru->next_lru = -1; 40 | ssd_buf_hdr_for_lru->last_lru = -1; 41 | SHM_mutex_init(& 42 | ssd_buf_hdr_for_lru->lock); 43 | } 44 | } 45 | else 46 | { 47 | strategy_ctrl =(StrategyCtrl_LRU_global *)SHM_get(SHM_SSDBUF_STRATEGY_CTRL,sizeof(StrategyCtrl_LRU_global)); 48 | strategy_desp = (StrategyDesp_LRU_global *)SHM_get(SHM_SSDBUF_STRATEGY_DESP, sizeof(StrategyDesp_LRU_global) * NBLOCK_SSD_CACHE); 49 | 50 | } 51 | SHM_unlock("LOCK_SSDBUF_STRATEGY_LRU"); 52 | return stat; 53 | } 54 | 55 | long 56 | Unload_LRUBuf() 57 | { 58 | _LOCK(&strategy_ctrl->lock); 59 | 60 | long frozen_id = strategy_ctrl->last_lru; 61 | deleteFromLRU(&strategy_desp[frozen_id]); 62 | 63 | _UNLOCK(&strategy_ctrl->lock); 64 | return frozen_id; 65 | } 66 | 67 | int 68 | hitInLRUBuffer(long serial_id) 69 | { 70 | _LOCK(&strategy_ctrl->lock); 71 | 72 | StrategyDesp_LRU_global* ssd_buf_hdr_for_lru = &strategy_desp[serial_id]; 73 | if(hasBeenDeleted(ssd_buf_hdr_for_lru)) 74 | { 75 | _UNLOCK(&strategy_ctrl->lock); 76 | return -1; 77 | } 78 | moveToLRUHead(ssd_buf_hdr_for_lru); 79 | _UNLOCK(&strategy_ctrl->lock); 80 | 81 | return 0; 82 | } 83 | 84 | void* 85 | insertLRUBuffer(long serial_id) 86 | { 87 | _LOCK(&strategy_ctrl->lock); 88 | 89 | addToLRUHead(&strategy_desp[serial_id]); 90 | _UNLOCK(&strategy_ctrl->lock); 91 | return 0; 92 | } 93 | 94 | 95 | static volatile void * 96 | addToLRUHead(StrategyDesp_LRU_global* ssd_buf_hdr_for_lru) 97 | { 98 | if (strategy_ctrl->last_lru < 0) 99 | { 100 | strategy_ctrl->first_lru = ssd_buf_hdr_for_lru->serial_id; 101 | strategy_ctrl->last_lru = ssd_buf_hdr_for_lru->serial_id; 102 | } 103 | else 104 | { 105 | ssd_buf_hdr_for_lru->next_lru = strategy_desp[strategy_ctrl->first_lru].serial_id; 106 | ssd_buf_hdr_for_lru->last_lru = -1; 107 | strategy_desp[strategy_ctrl->first_lru].last_lru = ssd_buf_hdr_for_lru->serial_id; 108 | strategy_ctrl->first_lru = ssd_buf_hdr_for_lru->serial_id; 109 | } 110 | return NULL; 111 | } 112 | 113 | static volatile void * 114 | deleteFromLRU(StrategyDesp_LRU_global * ssd_buf_hdr_for_lru) 115 | { 116 | if (ssd_buf_hdr_for_lru->last_lru >= 0) 117 | { 118 | strategy_desp[ssd_buf_hdr_for_lru->last_lru].next_lru = ssd_buf_hdr_for_lru->next_lru; 119 | } 120 | else 121 | { 122 | strategy_ctrl->first_lru = ssd_buf_hdr_for_lru->next_lru; 123 | } 124 | if (ssd_buf_hdr_for_lru->next_lru >= 0) 125 | { 126 | strategy_desp[ssd_buf_hdr_for_lru->next_lru].last_lru = ssd_buf_hdr_for_lru->last_lru; 127 | } 128 | else 129 | { 130 | strategy_ctrl->last_lru = ssd_buf_hdr_for_lru->last_lru; 131 | } 132 | 133 | ssd_buf_hdr_for_lru->last_lru = ssd_buf_hdr_for_lru->next_lru = -1; 134 | return NULL; 135 | } 136 | 137 | static volatile void * 138 | moveToLRUHead(StrategyDesp_LRU_global * ssd_buf_hdr_for_lru) 139 | { 140 | deleteFromLRU(ssd_buf_hdr_for_lru); 141 | addToLRUHead(ssd_buf_hdr_for_lru); 142 | return NULL; 143 | } 144 | 145 | static int 146 | hasBeenDeleted(StrategyDesp_LRU_global* ssd_buf_hdr_for_lru) 147 | { 148 | if(ssd_buf_hdr_for_lru->last_lru < 0 && ssd_buf_hdr_for_lru->next_lru < 0) 149 | return 1; 150 | else 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /src/shmlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | Writed by Sun, Diansen. <2019.12>, email: diansensun@gmail.com 3 | 4 | This file provides utilities for share memory operation based on POSIX Share Memory. 5 | To untilize without error, you might add "-lrt" option onto the linker. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "report.h" 15 | #include "shmlib.h" 16 | 17 | /* 18 | Only call this func one time, otherwise it may cause content confused. 19 | */ 20 | void* SHM_alloc(char* shm_name, size_t len) 21 | { 22 | int fd = shm_open(shm_name, O_RDWR|O_CREAT,0644); 23 | if(fd < 0) 24 | { 25 | printf("create share memory error."); 26 | return NULL; 27 | } 28 | 29 | if(ftruncate(fd,len)!=0) 30 | { 31 | sac_warning("truncate share memory error."); 32 | return NULL; 33 | } 34 | 35 | void* shm_vir_addr = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); 36 | if(shm_vir_addr) 37 | mlock(shm_vir_addr,len); 38 | return shm_vir_addr; 39 | } 40 | 41 | int SHM_free(char* shm_name,void* shm_vir_addr,long len) 42 | { 43 | if(munmap(shm_vir_addr,len)<0 || munlock(shm_vir_addr,len)<0) 44 | return -1; 45 | 46 | int fd = shm_open(shm_name, O_RDWR,0644); 47 | if(fd < 0) 48 | return -1; 49 | /* something uncompleted */ 50 | char filelock[50]; 51 | sprintf(filelock,"/dev/%s_lock",shm_name); 52 | shm_unlink(shm_name); 53 | unlink(filelock); 54 | return 0; 55 | } 56 | 57 | void* SHM_get(char* shm_name,size_t len) 58 | { 59 | int fd = shm_open(shm_name,O_RDWR,0644); 60 | if(fd < 0) 61 | return NULL; 62 | void* shm_vir_addr = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); 63 | if(shm_vir_addr) 64 | mlock(shm_vir_addr,len); 65 | return shm_vir_addr; 66 | } 67 | 68 | 69 | 70 | /************************* 71 | *** Multi-process MUTEX ** 72 | **************************/ 73 | /* 74 | The param 'lock' must be created in SHARE MEMORY, otherwise it will not work for locking. 75 | */ 76 | int SHM_mutex_init(pthread_mutex_t* lock) 77 | { 78 | pthread_mutexattr_t ma; 79 | pthread_mutexattr_init(&ma); 80 | pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); 81 | pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST); 82 | 83 | return pthread_mutex_init(lock, &ma); 84 | } 85 | 86 | void SHM_mutex_lock(pthread_mutex_t* lock) 87 | { 88 | if(pthread_mutex_lock(lock) == EOWNERDEAD) 89 | { 90 | sac_warning("will consistent mutex, please check if any process has terminated while holding this mutex."); 91 | pthread_mutex_consistent(lock); 92 | } 93 | } 94 | 95 | void SHM_mutex_unlock(pthread_mutex_t* lock) 96 | { 97 | pthread_mutex_unlock(lock); 98 | } 99 | 100 | ///* 101 | // GLOBAL LOCK provide you a reliable way for multi-process to coordinate their FIRST resourse competition. 102 | // notice that: 103 | // (1)This function use a SHARE variable 'GLOBAL_UNI_LOCK', you need to aviod reusing the same name lock in your code. 104 | // (2)You only use this function once in a short period. I suggest only use it to lock when initializing the SHARED variable. 105 | //*/ 106 | //static void SHM_GLOBAL_UNI_LOCK() 107 | //{ 108 | // int n=0; 109 | // while(shm_open(GLOBAL_UNI_LOCK,O_CREAT|O_EXCL,0644)<0) 110 | // { 111 | // printf("try lock %d time.\n",n+1); 112 | // sleep(1); 113 | // n++; 114 | // if(n>=10) 115 | // { 116 | // printf("global lock time out!\n"); 117 | // exit(1); 118 | // } 119 | // } 120 | //} 121 | // 122 | //static void SHM_GLOBAL_UNI_UNLOCK() 123 | //{ 124 | // if(shm_unlink(GLOBAL_UNI_LOCK)<0) 125 | // { 126 | // sac_warning("global unlock error"); 127 | // exit(1); 128 | // } 129 | //} 130 | 131 | int SHM_trylock(char* lockname) 132 | { 133 | if(shm_open(lockname,O_CREAT|O_EXCL,0644)<0) 134 | return -1; 135 | return 1; 136 | } 137 | 138 | int SHM_lock(char* lockname) 139 | { 140 | int n = 0; 141 | while(shm_open(lockname,O_CREAT|O_EXCL,0644)<0) 142 | { 143 | char msg[50]; 144 | sprintf(msg,"trying lock '%s': %d times",lockname,++n); 145 | sac_info(msg); 146 | sleep(1); 147 | } 148 | return n; 149 | } 150 | 151 | int SHM_lock_n_check(char* lockname) 152 | { 153 | int n = 0; 154 | while(shm_open(lockname,O_CREAT|O_EXCL,0644)<0) 155 | { 156 | char msg[50]; 157 | sprintf(msg,"trying lock '%s': %d times",lockname,++n); 158 | sac_info(msg); 159 | sleep(1); 160 | } 161 | 162 | char lock[50]; 163 | char chk[50]; 164 | sprintf(lock,"/dev/shm/%s",lockname); 165 | sprintf(chk,"/dev/shm/%s_chk",lockname); 166 | int l = link(lock,chk); 167 | return l; 168 | } 169 | 170 | int SHM_unlock(char* lockname) 171 | { 172 | return shm_unlink(lockname); 173 | } 174 | -------------------------------------------------------------------------------- /src/strategy/most.c.bak: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "most.h" 5 | 6 | long GetSMRBandNumFromSSD(unsigned long offset); 7 | 8 | BandDescForMost EvictedBand; 9 | 10 | int 11 | initSSDBufferForMost() 12 | { 13 | initBandTable(NBANDTables, &band_hashtable_for_most); 14 | 15 | SSDBufDespForMost *ssd_buf_hdr_for_most; 16 | BandDescForMost *band_hdr_for_most; 17 | ssd_buf_desps_for_most = (SSDBufDespForMost *) malloc(sizeof(SSDBufDespForMost) * NBLOCK_SSD_CACHE); 18 | long i; 19 | ssd_buf_hdr_for_most = ssd_buf_desps_for_most; 20 | for (i = 0; i < NBLOCK_SSD_CACHE; ssd_buf_hdr_for_most++, i++) { 21 | ssd_buf_hdr_for_most->ssd_buf_id = i; 22 | ssd_buf_hdr_for_most->next_ssd_buf = -1; 23 | } 24 | 25 | band_descriptors_for_most = (BandDescForMost *) malloc(sizeof(BandDescForMost) * NZONES); 26 | band_hdr_for_most = band_descriptors_for_most; 27 | for (i = 0; i < NZONES; band_hdr_for_most++, i++) { 28 | band_hdr_for_most->band_num = 0; 29 | band_hdr_for_most->current_pages = 0; 30 | band_hdr_for_most->first_page = -1; 31 | } 32 | 33 | ssd_buf_strategy_ctrl_for_most = (SSDBufferStrategyControlForMost *) malloc(sizeof(SSDBufferStrategyControlForMost)); 34 | ssd_buf_strategy_ctrl_for_most->nbands = 0; 35 | 36 | EvictedBand.band_num = 0; 37 | EvictedBand.first_page = -1; 38 | EvictedBand.current_pages = 0; 39 | } 40 | 41 | int 42 | HitMostBuffer() 43 | { 44 | return 1; 45 | } 46 | 47 | long LogOutDesp_most() 48 | { 49 | long band_hash = 0; 50 | if(EvictedBand.first_page < 0){ 51 | bandtableDelete(EvictedBand.band_num, bandtableHashcode(EvictedBand.band_num), &band_hashtable_for_most); 52 | 53 | BandDescForMost temp; 54 | 55 | EvictedBand = band_descriptors_for_most[0]; 56 | temp = band_descriptors_for_most[ssd_buf_strategy_ctrl_for_most->nbands - 1]; 57 | long parent = 0; 58 | long child = parent * 2 + 1; 59 | while (child < ssd_buf_strategy_ctrl_for_most->nbands) { 60 | if (child < ssd_buf_strategy_ctrl_for_most->nbands && band_descriptors_for_most[child].current_pages < band_descriptors_for_most[child + 1].current_pages) 61 | child++; 62 | if (temp.current_pages >= band_descriptors_for_most[child].current_pages) 63 | break; 64 | else { 65 | band_descriptors_for_most[parent] = band_descriptors_for_most[child]; 66 | long band_hash = bandtableHashcode(band_descriptors_for_most[child].band_num); 67 | bandtableDelete(band_descriptors_for_most[child].band_num, band_hash, &band_hashtable_for_most); 68 | bandtableInsert(band_descriptors_for_most[child].band_num, band_hash, parent, &band_hashtable_for_most); 69 | parent = child; 70 | child = child * 2 + 1; 71 | } 72 | } 73 | band_descriptors_for_most[parent] = temp; 74 | band_descriptors_for_most[ssd_buf_strategy_ctrl_for_most->nbands - 1].band_num = -1; 75 | band_descriptors_for_most[ssd_buf_strategy_ctrl_for_most->nbands - 1].current_pages = 0; 76 | band_descriptors_for_most[ssd_buf_strategy_ctrl_for_most->nbands - 1].first_page = -1; 77 | ssd_buf_strategy_ctrl_for_most->nbands--; 78 | band_hash = bandtableHashcode(temp.band_num); 79 | bandtableDelete(temp.band_num, band_hash, &band_hashtable_for_most); 80 | bandtableInsert(temp.band_num, band_hash, parent, &band_hashtable_for_most); 81 | 82 | } 83 | 84 | long band_num = EvictedBand.band_num; 85 | band_hash = bandtableHashcode(band_num); 86 | long band_id = bandtableLookup(band_num, band_hash, band_hashtable_for_most); 87 | long first_page = EvictedBand.first_page; 88 | 89 | ssd_buf_desps_for_most[first_page].next_ssd_buf = -1; 90 | 91 | return ssd_buf_desps_for_most[first_page].ssd_buf_id; 92 | } 93 | 94 | int LogInMostBuffer(long despId, SSDBufTag tag) 95 | { 96 | long band_num = GetSMRBandNumFromSSD(tag.offset); 97 | unsigned long band_hash = bandtableHashcode(band_num); 98 | long band_id = bandtableLookup(band_num, band_hash, band_hashtable_for_most); 99 | 100 | SSDBufDespForMost *ssd_buf_for_most; 101 | BandDescForMost *band_hdr_for_most; 102 | 103 | if (band_id >= 0) { 104 | //printf("hit band %ld\n", band_num); 105 | SSDBufDespForMost *new_ssd_buf_for_most; 106 | new_ssd_buf_for_most = &ssd_buf_desps_for_most[despId]; 107 | new_ssd_buf_for_most->next_ssd_buf = band_descriptors_for_most[band_id].first_page; 108 | band_descriptors_for_most[band_id].first_page = despId; 109 | 110 | band_descriptors_for_most[band_id].current_pages++; 111 | BandDescForMost temp; 112 | long parent = (band_id - 1) / 2; 113 | long child = band_id; 114 | while (parent >= 0 && band_descriptors_for_most[child].current_pages > band_descriptors_for_most[parent].current_pages) { 115 | temp = band_descriptors_for_most[child]; 116 | band_descriptors_for_most[child] = band_descriptors_for_most[parent]; 117 | band_hash = bandtableHashcode(band_descriptors_for_most[parent].band_num); 118 | bandtableDelete(band_descriptors_for_most[parent].band_num, band_hash, &band_hashtable_for_most); 119 | bandtableInsert(band_descriptors_for_most[parent].band_num, band_hash, child, &band_hashtable_for_most); 120 | band_descriptors_for_most[parent] = temp; 121 | band_hash = bandtableHashcode(temp.band_num); 122 | bandtableDelete(temp.band_num, band_hash, &band_hashtable_for_most); 123 | bandtableInsert(temp.band_num, band_hash, parent, &band_hashtable_for_most); 124 | 125 | child = parent; 126 | parent = (child - 1) / 2; 127 | } 128 | } else { 129 | ssd_buf_strategy_ctrl_for_most->nbands++; 130 | band_descriptors_for_most[ssd_buf_strategy_ctrl_for_most->nbands - 1].band_num = band_num; 131 | band_descriptors_for_most[ssd_buf_strategy_ctrl_for_most->nbands - 1].current_pages = 1; 132 | band_descriptors_for_most[ssd_buf_strategy_ctrl_for_most->nbands - 1].first_page = despId; 133 | bandtableInsert(band_num, band_hash, ssd_buf_strategy_ctrl_for_most->nbands - 1, &band_hashtable_for_most); 134 | SSDBufDespForMost *new_ssd_buf_for_most; 135 | new_ssd_buf_for_most = &ssd_buf_desps_for_most[despId]; 136 | new_ssd_buf_for_most->next_ssd_buf = -1; 137 | } 138 | } 139 | 140 | long GetSMRBandNumFromSSD(unsigned long offset) 141 | { 142 | long BNDSZ = 36*1024*1024; // bandsize = 36MB (18MB~36MB) 143 | long band_size_num = BNDSZ / 1024 / 1024 / 2 + 1; 144 | long num_each_size = NZONES / band_size_num; 145 | long i, size, total_size = 0; 146 | for (i = 0; i < band_size_num; i++) 147 | { 148 | size = BNDSZ / 2 + i * 1024 * 1024; 149 | if (total_size + size * num_each_size > offset) 150 | return num_each_size * i + (offset - total_size) / size; 151 | total_size += size * num_each_size; 152 | } 153 | 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /lib/xstrtol.c: -------------------------------------------------------------------------------- 1 | /* A more useful interface to strtol. 2 | Copyright (C) 1995, 1996, 1998-2001 Free Software Foundation, Inc. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2, or (at your option) 7 | any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software Foundation, 16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 17 | 18 | /* Written by Jim Meyering. */ 19 | 20 | #if HAVE_CONFIG_H 21 | # include 22 | #endif 23 | 24 | #ifndef __strtol 25 | # define __strtol strtol 26 | # define __strtol_t long int 27 | # define __xstrtol xstrtol 28 | #endif 29 | 30 | /* Some pre-ANSI implementations (e.g. SunOS 4) 31 | need stderr defined if assertion checking is enabled. */ 32 | #include 33 | 34 | #if STDC_HEADERS 35 | # include 36 | #endif 37 | 38 | #if HAVE_STRING_H 39 | # include 40 | #else 41 | # include 42 | # ifndef strchr 43 | # define strchr index 44 | # endif 45 | #endif 46 | 47 | #include 48 | #include 49 | 50 | #include 51 | #ifndef errno 52 | extern int errno; 53 | #endif 54 | 55 | #if HAVE_LIMITS_H 56 | # include 57 | #endif 58 | 59 | #ifndef CHAR_BIT 60 | # define CHAR_BIT 8 61 | #endif 62 | 63 | /* The extra casts work around common compiler bugs. */ 64 | #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) 65 | /* The outer cast is needed to work around a bug in Cray C 5.0.3.0. 66 | It is necessary at least when t == time_t. */ 67 | #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \ 68 | ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0)) 69 | #define TYPE_MAXIMUM(t) (~ (t) 0 - TYPE_MINIMUM (t)) 70 | 71 | #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) 72 | # define IN_CTYPE_DOMAIN(c) 1 73 | #else 74 | # define IN_CTYPE_DOMAIN(c) isascii(c) 75 | #endif 76 | 77 | #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) 78 | 79 | #include "xstrtol.h" 80 | 81 | #if !HAVE_DECL_STRTOL && !defined strtol 82 | long int strtol (); 83 | #endif 84 | 85 | #if !HAVE_DECL_STRTOUL && !defined strtoul 86 | unsigned long int strtoul (); 87 | #endif 88 | 89 | #if !HAVE_DECL_STRTOIMAX && !defined strtoimax 90 | intmax_t strtoimax (); 91 | #endif 92 | 93 | #if !HAVE_DECL_STRTOUMAX && !defined strtoumax 94 | uintmax_t strtoumax (); 95 | #endif 96 | 97 | static int 98 | bkm_scale (__strtol_t *x, int scale_factor) 99 | { 100 | __strtol_t product = *x * scale_factor; 101 | if (*x != product / scale_factor) 102 | return 1; 103 | *x = product; 104 | return 0; 105 | } 106 | 107 | static int 108 | bkm_scale_by_power (__strtol_t *x, int base, int power) 109 | { 110 | while (power--) 111 | if (bkm_scale (x, base)) 112 | return 1; 113 | 114 | return 0; 115 | } 116 | 117 | /* FIXME: comment. */ 118 | 119 | strtol_error 120 | __xstrtol (const char *s, char **ptr, int strtol_base, 121 | __strtol_t *val, const char *valid_suffixes) 122 | { 123 | char *t_ptr; 124 | char **p; 125 | __strtol_t tmp; 126 | 127 | assert (0 <= strtol_base && strtol_base <= 36); 128 | 129 | p = (ptr ? ptr : &t_ptr); 130 | 131 | if (! TYPE_SIGNED (__strtol_t)) 132 | { 133 | const char *q = s; 134 | while (ISSPACE ((unsigned char) *q)) 135 | ++q; 136 | if (*q == '-') 137 | return LONGINT_INVALID; 138 | } 139 | 140 | errno = 0; 141 | tmp = __strtol (s, p, strtol_base); 142 | if (errno != 0) 143 | return LONGINT_OVERFLOW; 144 | 145 | if (*p == s) 146 | { 147 | /* If there is no number but there is a valid suffix, assume the 148 | number is 1. The string is invalid otherwise. */ 149 | if (valid_suffixes && **p && strchr (valid_suffixes, **p)) 150 | tmp = 1; 151 | else 152 | return LONGINT_INVALID; 153 | } 154 | 155 | /* Let valid_suffixes == NULL mean `allow any suffix'. */ 156 | /* FIXME: update all callers except the ones that allow suffixes 157 | after the number, changing last parameter NULL to `""'. */ 158 | if (!valid_suffixes) 159 | { 160 | *val = tmp; 161 | return LONGINT_OK; 162 | } 163 | 164 | if (**p != '\0') 165 | { 166 | int base = 1024; 167 | int suffixes = 1; 168 | int overflow; 169 | 170 | if (!strchr (valid_suffixes, **p)) 171 | { 172 | *val = tmp; 173 | return LONGINT_INVALID_SUFFIX_CHAR; 174 | } 175 | 176 | if (strchr (valid_suffixes, '0')) 177 | { 178 | /* The ``valid suffix'' '0' is a special flag meaning that 179 | an optional second suffix is allowed, which can change 180 | the base. A suffix "B" (e.g. "100MB") stands for a power 181 | of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for 182 | a power of 1024. If no suffix (e.g. "100M"), assume 183 | power-of-1024. */ 184 | 185 | switch (p[0][1]) 186 | { 187 | case 'i': 188 | if (p[0][2] == 'B') 189 | suffixes += 2; 190 | break; 191 | 192 | case 'B': 193 | case 'D': /* 'D' is obsolescent */ 194 | base = 1000; 195 | suffixes++; 196 | break; 197 | } 198 | } 199 | 200 | switch (**p) 201 | { 202 | case 'b': 203 | overflow = bkm_scale (&tmp, 512); 204 | break; 205 | 206 | case 'B': 207 | overflow = bkm_scale (&tmp, 1024); 208 | break; 209 | 210 | case 'c': 211 | overflow = 0; 212 | break; 213 | 214 | case 'E': /* exa or exbi */ 215 | overflow = bkm_scale_by_power (&tmp, base, 6); 216 | break; 217 | 218 | case 'G': /* giga or gibi */ 219 | case 'g': /* 'g' is undocumented; for compatibility only */ 220 | overflow = bkm_scale_by_power (&tmp, base, 3); 221 | break; 222 | 223 | case 'k': /* kilo */ 224 | case 'K': /* kibi */ 225 | overflow = bkm_scale_by_power (&tmp, base, 1); 226 | break; 227 | 228 | case 'M': /* mega or mebi */ 229 | case 'm': /* 'm' is undocumented; for compatibility only */ 230 | overflow = bkm_scale_by_power (&tmp, base, 2); 231 | break; 232 | 233 | case 'P': /* peta or pebi */ 234 | overflow = bkm_scale_by_power (&tmp, base, 5); 235 | break; 236 | 237 | case 'T': /* tera or tebi */ 238 | case 't': /* 't' is undocumented; for compatibility only */ 239 | overflow = bkm_scale_by_power (&tmp, base, 4); 240 | break; 241 | 242 | case 'w': 243 | overflow = bkm_scale (&tmp, 2); 244 | break; 245 | 246 | case 'Y': /* yotta or 2**80 */ 247 | overflow = bkm_scale_by_power (&tmp, base, 8); 248 | break; 249 | 250 | case 'Z': /* zetta or 2**70 */ 251 | overflow = bkm_scale_by_power (&tmp, base, 7); 252 | break; 253 | 254 | default: 255 | *val = tmp; 256 | return LONGINT_INVALID_SUFFIX_CHAR; 257 | break; 258 | } 259 | 260 | if (overflow) 261 | return LONGINT_OVERFLOW; 262 | 263 | (*p) += suffixes; 264 | } 265 | 266 | *val = tmp; 267 | return LONGINT_OK; 268 | } 269 | 270 | #ifdef TESTING_XSTRTO 271 | 272 | # include 273 | # include "error.h" 274 | 275 | char *program_name; 276 | 277 | int 278 | main (int argc, char** argv) 279 | { 280 | strtol_error s_err; 281 | int i; 282 | 283 | program_name = argv[0]; 284 | for (i=1; i%lu (%s)\n", argv[i], val, p); 293 | } 294 | else 295 | { 296 | STRTOL_FATAL_ERROR (argv[i], "arg", s_err); 297 | } 298 | } 299 | exit (0); 300 | } 301 | 302 | #endif /* TESTING_XSTRTO */ 303 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SAC: A Co-Design Cache Algorithm for Emerging SMR-based High-Density Disks 2 | [![DOI](https://zenodo.org/badge/224646281.svg)](https://zenodo.org/badge/latestdoi/224646281) 3 | 4 | SAC is a cache algorithm used in SSD-SMR hybrid storage systems. It has good performance in both read and write I / O mode. This project is a prototype of user-mode cache system used to verify the performance of different cache algorithms, in which we have 4 built-in algorithms - SAC, LRU, MOST, MOST+CDC(a module of SAC). 5 | 6 | Follow this instruction, you will have a quick exploration of play-with-SAC! 7 | 8 | ## Preparation 9 | 10 | ### Hardware dependencies 11 | 12 | - There is no dedicated hardware required if just to play with the SAC. We provide a pure emulation way including to emulate the cache and SMR device so that you can quickly see and compare the characteristic for each algorithm. However, you won't see any information of the I/O time. 13 | 14 | - If you want to verify the cache algorithm on real device, we suggest the hardware configuration as follow: 15 | 16 | - The Seagate 8TB SMR drive model ST0008AS0002 is recommended. 17 | 18 | - An SSD device is required to as the cache layer of the SMR, or use a memory file or Ramdisk instead, but it is recommended to be at least 40GiB. 19 | 20 | ### Software dependencies 21 | 22 | - The SAC project has been tested on CentOS Linux release 7.6.1810 (Core) based on Kernel 4.20.13-1 environment and is expected to run correctly in other Linux distributions. 23 | - FIO benchmark required for real SMR drive tests. 24 | 25 | 26 | 27 | ## Installation 28 | 29 | ### Source Code: 30 | 31 | Clone this project and goto the project root directory, then build the source code. 32 | 33 | ```shell 34 | git clone https://github.com/dcstrange/sac.git && cd sac 35 | make 36 | ``` 37 | 38 | 39 | 40 | ### DataSet 41 | 42 | You have to download the trace files as workloads needed by the program. The trace files are available on the Google Drive, [Click here](https://drive.google.com/drive/folders/1zwZYwGB9PbuqAs3wlIE4cpIrYkdgUtYB). Put all the trace files into the project directory ``traces/``. (If the link fails, just let us know by Email: diansensun@gmail.com. ) 43 | 44 | 45 | 46 | ## Experiment 47 | 48 | ### Options 49 | 50 | It generates the binary file `sac` after build. Now you can play with the SAC. The following table describes the option argument you may or have to specify. With different options, you will run the cases including SMR emulator, no real I/O, small or big trace, read or write only mode, etc. 51 | 52 | Before the test, you need to ensure that the SSD and SMR device files are present, and set the files path to the option `--cache-dev` and `--smr-dev`. 53 | If you don't have the SMR device, you still can use a regular HDD device file instead and use the SMR emulator module. Please for sure you have the permission to access the files, otherwise, you will get the error information like `errno:13`. 54 | 55 | 56 | | Options | Need Arg? | Arguments | Default | 57 | | ----------------- | --------- | ------------------------------------------------------------ | ------- | 58 | | `--cache-dev` | ✅ | Device file path of the ssd/ramdisk for cache layer. | NULL | 59 | | `--smr-dev` | ✅ | Device file path of the SMR drive or HDD for SMR emulator. | NULL | 60 | | `--algorithm` | ✅ | One of [SAC], [LRU], [MOST], [MOST+CDC] | SAC | 61 | | `--no-cache` | ❌ | | | 62 | | `--use-emulator` | ❌ | | | 63 | | `--no-real-io` | ❌ | | | 64 | | `--workload` | ✅ | Workload number for [1~11] corresponding to different trace file. | -1 | 65 | | `--workload-file` | ✅ | Or you can specofy the trace file path manually. | NULL | 66 | | `--workload-mode` | ✅ | Three workload mode: [**R**]:read-only, [**W**]:write-only, [**RW**]:read-write | RW | 67 | | `--cache-size` | ✅ | Cache size: [size]{+M,G}. E.g. 32G | 32GB | 68 | | `--pb-size` | ✅ | SMR Persistent Buffer size: [size]{+M,G}. E.g. 600M | 600MB | 69 | | `--offset` | ✅ | Start LBA offset of the SMR: [size]{+M,G}. E.g. 1G | 0 | 70 | | `--requests` | ✅ | Requst number you want to run: [Nunmber]. | -1 | 71 | | `--help` | ✅ | Show Help Manual. | | 72 | ### Examples 73 | 74 | #### Example 1: Quick Run! 75 | 76 | ```shell 77 | ./sac --algorithm SAC --workload 11 --use-emulator --no-real-io --requests 125000000 78 | ./sac --algorithm LRU --workload 11 --use-emulator --no-real-io --requests 125000000 79 | ./sac --algorithm MOST --workload 11 --use-emulator --no-real-io --requests 125000000 80 | ``` 81 | 82 | This command let you quickly verify and compare the effectiveness of a cache algorithm, and its performance will be reflected from the results of the SMR emulator where the most critical indicator is RMW trigger count. Note that, this command will not generate the real disk I/O due to the option `--no-real-io` which discards the request before it is sent to the device, while the metadata structure of the cache algorithm and emulator still running in memory. 83 | In this case, your test environment does not need to deploy the SSD and the SMR devices, nor does it need to specify the `--cache-dev` and `--smr-dev`. 84 | 85 | Also, we provide other comparison cache algorithms including LRU, MOST, and MOST with CDC. Change the `--algorithm` option to test other algorithms, such as `--algorithm LRU`. 86 | 87 | Or, you can just run the script `scripts/quicktest_compare_algorithms.sh` for a quick comparason test among LRU, MOST, SAC in write-only mode, run: 88 | 89 | ```shell 90 | cd scripts && ./quicktest_compare_algorithms.sh 91 | ``` 92 | 93 | The outputs can be found in `logs/`. 94 | 95 | #### Example 2: Big dataset and real disk I/O 96 | 97 | ```shell 98 | ./sac --cache-dev [FILE] --smr-dev [FILE] --workload 11 --algorithm SAC --requests 100000000 99 | ``` 100 | 101 | For the typical test on SSD-SMR hybrid storage, run the this command which will run the workload number 11 (which is a big dataset) for 100 million requests in read and write mix mode (default), and using the SAC cache algorithm (default). Note that, the real disk I/O will cost a long time, approximately 10+ hours for SAC algorithm, and much more for other algorithms. 102 | 103 | #### Example 3: Small dataset and write-only mode 104 | 105 | ```shell 106 | ./sac --cache-dev [FILE] --smr-dev [FILE] --algorithm SAC --workload 5 --workload-mode W 107 | ``` 108 | 109 | If you need the workload running in write-only mode, you should add the option `--workload-mode W` which will only execute write requests in the trace file. 110 | 111 | There are three optional value of `--workload-mode`, they are `W` for write-only mode, `R`for read-only mode, and `RW` for read-write mix mode. 112 | 113 | #### Example 4: Use emulator instead of SMR drive 114 | 115 | ```shell 116 | ./sac --cache-dev [FILE] --smr-dev [FILE] --workload 5 --workload-mode W --use-emulator 117 | ``` 118 | 119 | We enable you to verify the SAC and other cache algorithms without a SMR drive. With the option `--use-emulator`, the program will emulate the behavior of the STL (Shingle Translation Layer) on the regular HDD you specify. The SMR emulator module will then output the information about the I/O time, write amplification, RMW counts, etc. 120 | 121 | Note that, if you use the option `--use-emulator` without `--no-real-io`, the program will use the HDD device specified by `--smr-dev` to execute the behavior of STL and generate the I/O. In this case, you must to specify a real HDD. 122 | 123 | ### Note! 124 | Before testing the real SMR drive, you need to force clean the PB area of the SMR, otherwise the remaining data will affect the performance of the next test. We provide the PB cleaning script in the project scripts/smr-pb-forceclean.sh, run 125 | 126 | ```shell 127 | ./smr-pb-forceclean.sh [SMR FILE] 128 | ``` 129 | 130 | But be careful NOT to use this script in any production environment, it will overwrite the data of the `[SMR FILE]`. 131 | 132 | ### Contact: 133 | 134 | Author: Sun, Diansen and Chai, Yunpeng 135 | 136 | Affiliation: Renmin University of China 137 | 138 | Email: diansensun@gmail.com 139 | -------------------------------------------------------------------------------- /src/trace2call.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "global.h" 8 | #include "statusDef.h" 9 | 10 | #include "timerUtils.h" 11 | #include "cache.h" 12 | #include "strategy/lru.h" 13 | #include "trace2call.h" 14 | #include "report.h" 15 | #include "strategy/strategies.h" 16 | #include "smr-emulator/emulator_v2.h" 17 | 18 | //#include "/home/fei/git/Func-Utils/pipelib.h" 19 | // #include "hrc.h" 20 | extern struct RuntimeSTAT *STT; 21 | #define REPORT_INTERVAL 250000 // 1GB for blksize=4KB 22 | 23 | static void reportCurInfo(); 24 | static void report_ontime(); 25 | static void resetStatics(); 26 | 27 | static timeval tv_trace_start, tv_trace_end; 28 | static double time_trace; 29 | 30 | /** single request statistic information **/ 31 | #ifdef LOG_SINGLE_REQ 32 | static timeval tv_req_start, tv_req_stop; 33 | double io_latency; // latency of each IO 34 | static char log[256]; 35 | static microsecond_t msec_req; 36 | #endif //LOG_SINGLE_REQ 37 | 38 | extern microsecond_t msec_r_hdd, msec_w_hdd, msec_r_ssd, msec_w_ssd; 39 | extern int IsHit; 40 | char logbuf[512]; 41 | FILE *log_lat; 42 | char log_lat_path[] = "./logs/iolat.log"; 43 | 44 | void trace_to_iocall(FILE *trace, off_t startLBA) 45 | { 46 | log_lat = fopen(log_lat_path, "w+"); 47 | 48 | 49 | if (log_lat == NULL) 50 | sac_error_exit("log file open failure."); 51 | 52 | char action; 53 | off_t offset; 54 | char *ssd_buffer; 55 | int returnCode; 56 | int isFullSSDcache = 0; 57 | char pipebuf[128]; 58 | static struct timeval tv_start_io, tv_stop_io; 59 | 60 | 61 | // #ifdef CG_THROTTLE 62 | // static char *cgbuf; 63 | // int returncode = posix_memalign(&cgbuf, 512, 4096); 64 | // #endif // CG_THROTTLE 65 | 66 | returnCode = posix_memalign((void**)&ssd_buffer, 1024, 16 * sizeof(char) * BLKSZ); 67 | if (returnCode < 0) 68 | { 69 | sac_warning("posix memalign error\n"); 70 | //free(ssd_buffer); 71 | exit(-1); 72 | } 73 | int i; 74 | for (i = 0; i < 16 * BLKSZ; i++) 75 | { 76 | ssd_buffer[i] = '1'; 77 | } 78 | 79 | _TimerLap(&tv_trace_start); 80 | 81 | blkcnt_t total_n_req; 82 | if(Request_limit > 0) 83 | total_n_req = Request_limit; 84 | else 85 | total_n_req = REPORT_INTERVAL * 500 * 3; //isWriteOnly ? (blkcnt_t)REPORT_INTERVAL*500*3 : REPORT_INTERVAL*500*3; 86 | 87 | blkcnt_t skiprows = 0; //isWriteOnly ? 50000000 : 100000000; 88 | 89 | while (!feof(trace) && STT->reqcnt_s < total_n_req) 90 | { 91 | 92 | returnCode = fscanf(trace, "%c %d %lu\n", &action, &i, &offset); 93 | if (returnCode < 0) 94 | { 95 | sac_warning("error while reading trace file."); 96 | break; 97 | } 98 | if (skiprows > 0) 99 | { 100 | skiprows--; 101 | continue; 102 | } 103 | 104 | offset = (offset + startLBA) * BLKSZ; 105 | if (!isFullSSDcache && (STT->flush_clean_blocks + STT->flush_hdd_blocks) > 0) 106 | { 107 | printf("-------------------Cache Space is Full-----------------\n"); 108 | reportCurInfo(); 109 | resetStatics(); // Reset the statistics of warming phrase, cuz we don't care. 110 | isFullSSDcache = 1; 111 | } 112 | 113 | #ifdef LOG_SINGLE_REQ 114 | _TimerLap(&tv_req_start); 115 | #endif // TIMER_SINGLE_REQ 116 | _TimerLap(&tv_start_io); 117 | sprintf(pipebuf, "%c,%lu\n", action, offset); 118 | if (action == ACT_WRITE && (Workload_Mode & IOMODE_W)) 119 | { 120 | write_block(offset, ssd_buffer); 121 | 122 | _TimerLap(&tv_stop_io); 123 | 124 | STT->reqcnt_w++; 125 | STT->reqcnt_s++; 126 | 127 | #ifdef LOG_IO_LAT 128 | io_latency = TimerInterval_SECOND(&tv_start_io, &tv_stop_io); 129 | sprintf(log, "%f,%c\n", io_latency, action); 130 | sac_log(log, log_lat); 131 | #endif // LOG_IO_LAT 132 | } 133 | else if (action == ACT_READ && (Workload_Mode & IOMODE_R)) 134 | { 135 | read_block(offset, ssd_buffer); 136 | 137 | _TimerLap(&tv_stop_io); 138 | 139 | STT->reqcnt_r++; 140 | STT->reqcnt_s++; 141 | 142 | #ifdef LOG_IO_LAT 143 | io_latency = TimerInterval_SECOND(&tv_start_io, &tv_stop_io); 144 | sprintf(log, "%f,%c\n", io_latency, action); 145 | sac_log(log, log_lat); 146 | #endif //LOG_IO_LAT 147 | } 148 | else if (action != ACT_READ) 149 | { 150 | printf("Trace file gets a wrong result: action = %c.\n", action); 151 | sac_error_exit("Trace file gets a wrong result"); 152 | } 153 | #ifdef LOG_SINGLE_REQ //Legacy 154 | _TimerLap(&tv_req_stop); 155 | msec_req = TimerInterval_MICRO(&tv_req_start, &tv_req_stop); 156 | /* 157 | print log 158 | format: 159 | 160 | */ 161 | // sprintf(logbuf,"%lu,%c,%d,%ld,%ld,%ld,%ld,%ld\n",STT->reqcnt_s,action,IsHit,msec_req,msec_r_ssd,msec_w_ssd,msec_r_hdd,msec_w_hdd); 162 | msec_r_ssd = msec_w_ssd = msec_r_hdd = msec_w_hdd = 0; 163 | #endif // TIMER_SINGLE_REQ 164 | 165 | if (STT->reqcnt_s > 0 && STT->reqcnt_s % REPORT_INTERVAL == 0) 166 | { 167 | report_ontime(); 168 | if (STT->reqcnt_s % ((blkcnt_t)REPORT_INTERVAL * 500) == 0) 169 | { 170 | reportCurInfo(); 171 | // resetStatics(); 172 | if (EMULATION) 173 | { 174 | Emu_PrintStatistic(); 175 | // Emu_ResetStatisic(); 176 | } 177 | } 178 | } 179 | } 180 | 181 | _TimerLap(&tv_trace_end); 182 | time_trace = Mirco2Sec(TimerInterval_MICRO(&tv_trace_start, &tv_trace_end)); 183 | reportCurInfo(); 184 | 185 | #ifdef HRC_PROCS_N 186 | for (i = 0; i < HRC_PROCS_N; i++) 187 | { 188 | sprintf(pipebuf, "EOF\n"); 189 | pipe_write(PipeEnds_of_MAIN[i], pipebuf, 64); 190 | } 191 | #endif // HRC_PROCS_N 192 | free(ssd_buffer); 193 | fclose(trace); 194 | fclose(log_lat); 195 | } 196 | 197 | 198 | static void reportCurInfo() 199 | { 200 | printf("--------------------RESULT---------------------\n"); 201 | printf(" totalreqNum:%lu\n read_req_count: %lu\n write_req_count: %lu\n", 202 | STT->reqcnt_s, STT->reqcnt_r, STT->reqcnt_w); 203 | 204 | printf(" hit num:%lu\n hitnum_r:%lu\n hitnum_w:%lu\n", 205 | STT->hitnum_s, STT->hitnum_r, STT->hitnum_w); 206 | 207 | printf(" read_ssd_blocks:%lu\n flush_ssd_blocks:%lu\n read_hdd_blocks:%lu\n flush_dirty_blocks:%lu\n flush_clean_blocks:%lu\n", 208 | STT->load_ssd_blocks, STT->flush_ssd_blocks, STT->load_hdd_blocks, STT->flush_hdd_blocks, STT->flush_clean_blocks); 209 | 210 | // printf(" hash_miss:%lu\n hashmiss_read:%lu\n hashmiss_write:%lu\n", 211 | // STT->hashmiss_sum, STT->hashmiss_read, STT->hashmiss_write); 212 | 213 | printf(" total run time (s): %lf\n time_read_ssd : %lf\n time_write_ssd : %lf\n time_read_smr : %lf\n time_write_smr : %lf\n", 214 | time_trace, STT->time_read_ssd, STT->time_write_ssd, STT->time_read_hdd, STT->time_write_hdd); 215 | printf(" Batch flush HDD time:%u\n", msec_bw_hdd); 216 | 217 | printf(" Cache Proportion(R/W): [%ld/%ld]\n", STT->incache_n_clean, STT->incache_n_dirty); 218 | printf(" wt_hit_rd: %lu\n rd_hit_wt: %lu\n", STT->wt_hit_rd, STT->rd_hit_wt); 219 | } 220 | 221 | static void report_ontime() 222 | { 223 | // _TimerLap(&tv_checkpoint); 224 | // double timecost = Mirco2Sec(TimerInterval_SECOND(&tv_trace_start,&tv_checkpoint)); 225 | 226 | // printf("totalreq:%lu, readreq:%lu, hit:%lu, readhit:%lu, flush_ssd_blk:%lu flush_hdd_blk:%lu, hashmiss:%lu, readhassmiss:%lu writehassmiss:%lu\n", 227 | // STT->reqcnt_s,STT->reqcnt_r, STT->hitnum_s, STT->hitnum_r, STT->flush_ssd_blocks, STT->flush_hdd_blocks, STT->hashmiss_sum, STT->hashmiss_read, STT->hashmiss_write); 228 | printf("totalreq:%lu, readreq:%lu, wrtreq:%lu, hit:%lu, readhit:%lu, flush_ssd_blk:%lu flush_dirty_blk:%lu\n", 229 | STT->reqcnt_s, STT->reqcnt_r, STT->reqcnt_w, STT->hitnum_s, STT->hitnum_r, STT->flush_ssd_blocks, STT->flush_hdd_blocks); 230 | _TimerLap(&tv_trace_end); 231 | double timecost = Mirco2Sec(TimerInterval_MICRO(&tv_trace_start, &tv_trace_end)); 232 | printf("current run time: %.0f\n", timecost); 233 | } 234 | 235 | static void resetStatics() 236 | { 237 | 238 | // STT->hitnum_s = 0; 239 | // STT->hitnum_r = 0; 240 | // STT->hitnum_w = 0; 241 | STT->load_ssd_blocks = 0; 242 | STT->flush_ssd_blocks = 0; 243 | STT->flush_hdd_blocks = 0; 244 | STT->flush_clean_blocks = 0; 245 | STT->load_hdd_blocks = 0; 246 | 247 | STT->reqcnt_r = STT->reqcnt_w = 0; 248 | STT->hitnum_s = STT->hitnum_r = STT->hitnum_w = 0; 249 | 250 | STT->time_read_hdd = 0.0; 251 | STT->time_write_hdd = 0.0; 252 | STT->time_read_ssd = 0.0; 253 | STT->time_write_ssd = 0.0; 254 | STT->hashmiss_sum = 0; 255 | STT->hashmiss_read = 0; 256 | STT->hashmiss_write = 0; 257 | msec_bw_hdd = 0; 258 | } 259 | -------------------------------------------------------------------------------- /src/strategy/most.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "most.h" 3 | #include "../statusDef.h" 4 | #include "../report.h" 5 | typedef struct Dscptr 6 | { 7 | long serial_id; 8 | SSDBufTag ssd_buf_tag; 9 | unsigned flag; 10 | long pre, next; 11 | unsigned long heat; 12 | long stamp; 13 | unsigned long zoneId; 14 | } Dscptr; 15 | 16 | typedef struct ZoneCtrl 17 | { 18 | unsigned long zoneId; 19 | long heat; 20 | long pagecnt_dirty; 21 | long pagecnt_clean; 22 | long head, tail; 23 | int activate_after_n_cycles; 24 | unsigned long score; 25 | } ZoneCtrl; 26 | 27 | static Dscptr *GlobalDespArray; 28 | static ZoneCtrl *ZoneCtrlArray; 29 | 30 | static unsigned long *ZoneSortArray; /* The zone ID array sorted by weight(calculated customized). it is used to determine the open zones */ 31 | static int OpenZoneCnt; /* It represent the number of open zones and the first number elements in 'ZoneSortArray' is the open zones ID */ 32 | 33 | extern long Cycle_Length; /* The period lenth which defines the times of eviction triggered */ 34 | static long PeriodProgress; /* Current times of eviction in a period lenth */ 35 | static long StampGlobal; /* Current io sequenced number in a period lenth, used to distinct the degree of heat among zones */ 36 | static int IsNewPeriod; 37 | 38 | static void add2ArrayHead(Dscptr *desp, ZoneCtrl *zoneCtrl); 39 | static void move2ArrayHead(Dscptr *desp, ZoneCtrl *zoneCtrl); 40 | static long stamp(Dscptr *desp); 41 | static void unloadfromZone(Dscptr *desp, ZoneCtrl *zoneCtrl); 42 | static void clearDesp(Dscptr *desp); 43 | static void hit(Dscptr *desp, ZoneCtrl *zoneCtrl); 44 | /** PORE **/ 45 | static int redefineOpenZones(); 46 | static ZoneCtrl *getEvictZone(); 47 | static long stamp(Dscptr *desp); 48 | 49 | static unsigned long 50 | getZoneNum(size_t offset) 51 | { 52 | return offset / ZONESZ; 53 | } 54 | 55 | /* Process Function */ 56 | int Init_most() 57 | { 58 | Cycle_Length = NBLOCK_SMR_PB; 59 | StampGlobal = PeriodProgress = 0; 60 | IsNewPeriod = 0; 61 | GlobalDespArray = (Dscptr *)malloc(sizeof(Dscptr) * NBLOCK_SSD_CACHE); 62 | ZoneCtrlArray = (ZoneCtrl *)malloc(sizeof(ZoneCtrl) * NZONES); 63 | 64 | ZoneSortArray = (unsigned long *)malloc(sizeof(unsigned long) * NZONES); 65 | 66 | int i = 0; 67 | while (i < NBLOCK_SSD_CACHE) 68 | { 69 | Dscptr *desp = GlobalDespArray + i; 70 | desp->serial_id = i; 71 | desp->ssd_buf_tag.offset = -1; 72 | desp->next = desp->pre = -1; 73 | desp->heat = 0; 74 | desp->stamp = 0; 75 | desp->flag = 0; 76 | i++; 77 | } 78 | i = 0; 79 | while (i < NZONES) 80 | { 81 | ZoneCtrl *ctrl = ZoneCtrlArray + i; 82 | ctrl->zoneId = i; 83 | ctrl->heat = ctrl->pagecnt_clean = ctrl->pagecnt_dirty = 0; 84 | ctrl->head = ctrl->tail = -1; 85 | ctrl->score = -1; 86 | ZoneSortArray[i] = 0; 87 | i++; 88 | } 89 | return 0; 90 | } 91 | 92 | int LogIn_most(long despId, SSDBufTag tag, unsigned flag) 93 | { 94 | /* activate the decriptor */ 95 | Dscptr *myDesp = GlobalDespArray + despId; 96 | ZoneCtrl *myZone = ZoneCtrlArray + getZoneNum(tag.offset); 97 | myDesp->ssd_buf_tag = tag; 98 | myDesp->flag |= flag; 99 | 100 | /* add into chain */ 101 | add2ArrayHead(myDesp, myZone); 102 | 103 | if ((flag & SSD_BUF_DIRTY) != 0) 104 | myZone->pagecnt_dirty++; 105 | else 106 | { 107 | myZone->pagecnt_clean++; 108 | } 109 | return 1; 110 | } 111 | 112 | int LogOut_most(long *out_despid_array, int max_n_batch) 113 | { 114 | static int periodCnt = 0; 115 | static ZoneCtrl *chosenOpZone; 116 | if (PeriodProgress % Cycle_Length == 0 || chosenOpZone->tail < 0) 117 | { 118 | redefineOpenZones(); 119 | PeriodProgress = 0; 120 | periodCnt++; 121 | chosenOpZone = getEvictZone(); 122 | printf("Period [%d], OpenZones_cnt=%d\n", periodCnt, OpenZoneCnt); 123 | } 124 | 125 | Dscptr *evitedDesp; 126 | int evict_grain = 64; 127 | int cnt = 0; 128 | while (cnt < evict_grain) 129 | { 130 | if (chosenOpZone->tail < 0) 131 | break; 132 | evitedDesp = GlobalDespArray + chosenOpZone->tail; 133 | out_despid_array[cnt] = evitedDesp->serial_id; 134 | 135 | unloadfromZone(evitedDesp, chosenOpZone); 136 | chosenOpZone->heat -= evitedDesp->heat; /**< Decision indicators */ 137 | if ((evitedDesp->flag & SSD_BUF_DIRTY) != 0) 138 | { 139 | chosenOpZone->pagecnt_dirty--; /**< Decision indicators */ 140 | } 141 | else 142 | { 143 | chosenOpZone->pagecnt_clean--; /**< Decision indicators */ 144 | } 145 | clearDesp(evitedDesp); 146 | PeriodProgress++; 147 | cnt++; 148 | } 149 | return cnt; 150 | } 151 | 152 | int Hit_most(long despId, unsigned flag) 153 | { 154 | Dscptr *myDesp = GlobalDespArray + despId; 155 | ZoneCtrl *myZone = ZoneCtrlArray + getZoneNum(myDesp->ssd_buf_tag.offset); 156 | 157 | move2ArrayHead(myDesp, myZone); 158 | hit(myDesp, myZone); 159 | stamp(myDesp); 160 | if ((myDesp->flag & SSD_BUF_DIRTY) == 0 && (flag & SSD_BUF_DIRTY) != 0) 161 | { 162 | myZone->pagecnt_dirty++; 163 | myZone->pagecnt_clean--; 164 | } 165 | myDesp->flag |= flag; 166 | 167 | return 1; 168 | } 169 | 170 | /**************** 171 | ** Utilities **** 172 | *****************/ 173 | 174 | static void 175 | hit(Dscptr *desp, ZoneCtrl *zoneCtrl) 176 | { 177 | desp->heat++; 178 | zoneCtrl->heat++; 179 | } 180 | 181 | static void 182 | add2ArrayHead(Dscptr *desp, ZoneCtrl *zoneCtrl) 183 | { 184 | if (zoneCtrl->head < 0) 185 | { 186 | //empty 187 | zoneCtrl->head = zoneCtrl->tail = desp->serial_id; 188 | } 189 | else 190 | { 191 | //unempty 192 | Dscptr *headDesp = GlobalDespArray + zoneCtrl->head; 193 | desp->pre = -1; 194 | desp->next = zoneCtrl->head; 195 | headDesp->pre = desp->serial_id; 196 | zoneCtrl->head = desp->serial_id; 197 | } 198 | } 199 | 200 | static void 201 | unloadfromZone(Dscptr *desp, ZoneCtrl *zoneCtrl) 202 | { 203 | if (desp->pre < 0) 204 | { 205 | zoneCtrl->head = desp->next; 206 | } 207 | else 208 | { 209 | GlobalDespArray[desp->pre].next = desp->next; 210 | } 211 | 212 | if (desp->next < 0) 213 | { 214 | zoneCtrl->tail = desp->pre; 215 | } 216 | else 217 | { 218 | GlobalDespArray[desp->next].pre = desp->pre; 219 | } 220 | desp->pre = desp->next = -1; 221 | } 222 | 223 | static void 224 | move2ArrayHead(Dscptr *desp, ZoneCtrl *zoneCtrl) 225 | { 226 | unloadfromZone(desp, zoneCtrl); 227 | add2ArrayHead(desp, zoneCtrl); 228 | } 229 | 230 | static void 231 | clearDesp(Dscptr *desp) 232 | { 233 | desp->ssd_buf_tag.offset = -1; 234 | desp->next = desp->pre = -1; 235 | desp->heat = 0; 236 | desp->stamp = 0; 237 | desp->flag &= ~(SSD_BUF_DIRTY | SSD_BUF_VALID); 238 | } 239 | 240 | /* Decision Method */ 241 | /** \brief 242 | * Quick-Sort method to sort the zones by score. 243 | NOTICE! 244 | If the gap between variable 'start' and 'end', it will PROBABLY cause call stack OVERFLOW! 245 | So this function need to modify for better. 246 | */ 247 | static void 248 | qsort_zone(long start, long end) 249 | { 250 | long i = start; 251 | long j = end; 252 | 253 | long S = ZoneSortArray[start]; 254 | ZoneCtrl *curCtrl = ZoneCtrlArray + S; 255 | unsigned long sWeight = curCtrl->score; 256 | while (i < j) 257 | { 258 | while (!(ZoneCtrlArray[ZoneSortArray[j]].score > sWeight) && i < j) 259 | { 260 | j--; 261 | } 262 | ZoneSortArray[i] = ZoneSortArray[j]; 263 | 264 | while (!(ZoneCtrlArray[ZoneSortArray[i]].score < sWeight) && i < j) 265 | { 266 | i++; 267 | } 268 | ZoneSortArray[j] = ZoneSortArray[i]; 269 | } 270 | 271 | ZoneSortArray[i] = S; 272 | if (i - 1 > start) 273 | qsort_zone(start, i - 1); 274 | if (j + 1 < end) 275 | qsort_zone(j + 1, end); 276 | } 277 | 278 | static long 279 | extractNonEmptyZoneId() 280 | { 281 | int zoneId = 0, cnt = 0; 282 | while (zoneId < NZONES) 283 | { 284 | ZoneCtrl *zone = ZoneCtrlArray + zoneId; 285 | if (zone->pagecnt_dirty + zone->pagecnt_clean > 0) 286 | { 287 | ZoneSortArray[cnt] = zoneId; 288 | cnt++; 289 | } 290 | zoneId++; 291 | } 292 | return cnt; 293 | } 294 | 295 | static void 296 | pause_and_caculate_weight_sizedivhot() 297 | { 298 | int n = 0; 299 | while (n < NZONES) 300 | { 301 | ZoneCtrl *ctrl = ZoneCtrlArray + n; 302 | ctrl->score = ctrl->pagecnt_dirty + ctrl->pagecnt_clean; 303 | n++; 304 | } 305 | } 306 | 307 | static int 308 | redefineOpenZones() 309 | { 310 | pause_and_caculate_weight_sizedivhot(); /**< Method 1 */ 311 | long nonEmptyZoneCnt = extractNonEmptyZoneId(); 312 | qsort_zone(0, nonEmptyZoneCnt - 1); 313 | 314 | /** lookup sort result **/ 315 | // int i; 316 | // for(i = 0; i<100; i++) 317 | // { 318 | // printf("%d: weight=%ld\t\theat=%ld\t\tndirty=%ld\t\tnclean=%ld\n", 319 | // i, 320 | // ZoneCtrlArray[ZoneSortArray[i]].weight, 321 | // ZoneCtrlArray[ZoneSortArray[i]].heat, 322 | // ZoneCtrlArray[ZoneSortArray[i]].pagecnt_dirty, 323 | // ZoneCtrlArray[ZoneSortArray[i]].pagecnt_clean); 324 | // } 325 | 326 | OpenZoneCnt = 1; 327 | IsNewPeriod = 1; 328 | printf("NonEmptyZoneCnt = %ld.\n", nonEmptyZoneCnt); 329 | return 0; 330 | } 331 | 332 | static ZoneCtrl * 333 | getEvictZone() 334 | { 335 | return ZoneCtrlArray + ZoneSortArray[0]; 336 | } 337 | 338 | static long 339 | stamp(Dscptr *desp) 340 | { 341 | desp->stamp = ++StampGlobal; 342 | return StampGlobal; 343 | } 344 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * main.c 3 | */ 4 | #define _GNU_SOURCE 1 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../lib/xstrtol.h" 17 | #include "report.h" 18 | #include "shmlib.h" 19 | #include "global.h" 20 | #include "cache.h" 21 | #include "smr-emulator/emulator_v2.h" 22 | #include "trace2call.h" 23 | #include "timerUtils.h" 24 | 25 | int analyze_opts(int argc, char **argv); 26 | 27 | unsigned int INIT_PROCESS = 0; 28 | 29 | const char *tracefile[] = { 30 | "./traces/src1_2.csv.req", 31 | "./traces/wdev_0.csv.req", 32 | "./traces/hm_0.csv.req", 33 | "./traces/mds_0.csv.req", 34 | "./traces/prn_0.csv.req", 35 | "./traces/rsrch_0.csv.req", 36 | "./traces/stg_0.csv.req", 37 | "./traces/ts_0.csv.req", 38 | "./traces/usr_0.csv.req", 39 | "./traces/web_0.csv.req", 40 | // "./traces/production-LiveMap-Backend-4K.req", // --> not in used. 41 | "./traces/long.csv.req" // default set: cache size = 8M*blksize; persistent buffer size = 1.6M*blksize. 42 | }; 43 | /* Return the value of STR, interpreted as a non-negative decimal integer, 44 | optionally multiplied by various values. 45 | Assign nonzero to *INVALID if STR does not represent a number in 46 | this format. */ 47 | 48 | static uintmax_t 49 | parse_integer (const char *str, int *invalid) 50 | { 51 | uintmax_t n; 52 | char *suffix; 53 | enum strtol_error e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0"); 54 | 55 | if (e == LONGINT_INVALID_SUFFIX_CHAR && *suffix == 'x') 56 | { 57 | uintmax_t multiplier = parse_integer (suffix + 1, invalid); 58 | 59 | if (multiplier != 0 && n * multiplier / multiplier != n) 60 | { 61 | *invalid = 1; 62 | return 0; 63 | } 64 | 65 | n *= multiplier; 66 | } 67 | else if (e != LONGINT_OK) 68 | { 69 | *invalid = 1; 70 | return 0; 71 | } 72 | 73 | return n; 74 | } 75 | 76 | int analyze_opts(int argc, char **argv) 77 | { 78 | static struct option long_options[] = { 79 | {"cache-dev", required_argument, NULL, 'C'}, // FORCE 80 | {"smr-dev", required_argument, NULL, 'S'}, // FORCE 81 | {"no-cache", no_argument, NULL, 'N'}, 82 | {"use-emulator", no_argument, NULL, 'E'}, 83 | {"workload", required_argument, NULL, 'W'}, // FORCE 84 | {"workload-file", required_argument, NULL, 'T'}, // FORCE 85 | {"workload-mode", required_argument, NULL, 'M'}, 86 | {"no-real-io", no_argument, NULL, 'D'}, 87 | {"cache-size", required_argument, NULL, 'c'}, 88 | {"pb-size", required_argument, NULL, 'p'}, 89 | {"algorithm", required_argument, NULL, 'A'}, 90 | {"offset", required_argument, NULL, 'O'}, 91 | {"requests", required_argument, NULL, 'R'}, 92 | {"help", no_argument, NULL, 'h'}, 93 | {0, 0, 0, 0} 94 | }; 95 | 96 | const char *optstr = "NE:W:M:F:DC:S:T:c:p:A:O:R:h"; 97 | int longIndex; 98 | 99 | while (1) 100 | { 101 | int opt = getopt_long(argc, argv, optstr, long_options, &longIndex); 102 | if (opt == -1) 103 | break; 104 | //printf("opt=%c,\nlongindex=%d,\nnext arg index: optind=%d,\noptarg=%s,\nopterr=%d,\noptopt=%c\n", 105 | //opt, longIndex, optind, optarg, opterr, optopt); 106 | 107 | uintmax_t n = 0; 108 | int invalid = 0; 109 | switch (opt) 110 | { 111 | case 'N': // no-cache 112 | NO_CACHE = 1; 113 | printf("[User Setting] Not to use the cache layer.\n"); 114 | break; 115 | 116 | case 'E': // use emulator 117 | EMULATION = 1; 118 | printf("[User Setting] Use SMR emulator on file: %s (Be sure your emulator file has at least 5TiB capacity.)\n", optarg); 119 | break; 120 | 121 | case 'A': // algorithm 122 | if (strcmp(optarg, "LRU") == 0) 123 | EvictStrategy = LRU_private; 124 | else if (strcmp(optarg, "SAC") == 0) 125 | EvictStrategy = SAC; 126 | else if (strcmp(optarg, "MOST") == 0) 127 | EvictStrategy = MOST; 128 | else if (strcmp(optarg, "MOST_CDC") == 0) 129 | EvictStrategy = MOST_CDC; 130 | else 131 | sac_error_exit("No such algorithm matched: %s.", optarg); 132 | 133 | printf("[User Setting] Cache Algorithm: %s.\n", optarg); 134 | break; 135 | 136 | case 'W': // workload 137 | TraceID = atoi(optarg); 138 | if (TraceID > 11 || TraceID <= 0) 139 | { 140 | printf("ERROR: Workload number is illegal, please tpye with 1~11: \n."); 141 | printf("[1]: src1_2.csv.req\n"); 142 | printf("[2]: wdev_0.csv.req\n"); 143 | printf("[3]: hm_0.csv.req\n"); 144 | printf("[4]: mds_0.csv.req\n"); 145 | printf("[5]: prn_0.csv.req\n"); 146 | printf("[6]: rsrch_0.csv.req\n"); 147 | printf("[7]: stg_0.csv.req\n"); 148 | printf("[8]: ts_0.csv.req\n"); 149 | printf("[9]: usr_0.csv.req\n"); 150 | printf("[10]: web_0.csv.req\n"); 151 | printf("[11]: long.req\n"); 152 | sac_exit(EXIT_FAILURE); 153 | } 154 | 155 | if ((TraceFile = fopen(tracefile[TraceID - 1], "rt")) == NULL) 156 | { 157 | 158 | printf("ERROR: Failed to open the trace file: %s\n", tracefile[TraceID - 1]); 159 | sac_exit(EXIT_FAILURE); 160 | } 161 | printf("[User Setting] Workload file path: %s\n", tracefile[TraceID - 1]); 162 | break; 163 | 164 | case 'T': // workload file 165 | printf("[User Setting] Workload file path: %s\n", optarg); 166 | if ((TraceFile = fopen(optarg, "rt")) == NULL) 167 | { 168 | sac_error_exit("ERROR: Failed to open the trace file: %s\n", optarg); 169 | } 170 | break; 171 | 172 | case 'M': // workload I/O mode 173 | printf("[User Setting] Workload mode "); 174 | if (strcmp(optarg, "r") == 0 || strcmp(optarg, "R") == 0) 175 | { 176 | Workload_Mode = IOMODE_R; 177 | printf("[r]: read-only. \n"); 178 | } 179 | else if (strcmp(optarg, "w") == 0 || strcmp(optarg, "W") == 0) 180 | { 181 | Workload_Mode = IOMODE_W; 182 | printf("[w]: write-only\n"); 183 | } 184 | else if (strcmp(optarg, "rw") == 0 || strcmp(optarg, "RW") == 0) 185 | { 186 | Workload_Mode = IOMODE_RW; 187 | printf("[rw]: read-write\n"); 188 | } 189 | else{ 190 | printf("ERROR: unrecongnizd workload mode \"%s\", please assign mode [r]: read-only, [w]: write-only or [rw]: read-write.\n", 191 | optarg); 192 | sac_exit(EXIT_FAILURE); 193 | } 194 | break; 195 | 196 | case 'D': // no-real-io 197 | NO_REAL_DISK_IO = 1; 198 | printf("[User Setting] No real disk IO. This option discards the request before it is sent to the device, so no real IO will be generated, \n\tbut the data structure including the cache algorithm and SMR Emulator statistics still works.\n"); 199 | break; 200 | 201 | case 'C': // cache-dev 202 | printf("optarg 1 = %s\n",optarg); 203 | cache_dev_path = optarg; 204 | 205 | printf("[User Setting] Cache device file: %s\n\t(You can still use ramdisk or memory fs for testing.)\n", cache_dev_path); 206 | break; 207 | 208 | case 'S': // SMR-dev 209 | smr_dev_path = optarg; 210 | 211 | printf("[User Setting] SMR device file: %s\n\t(You can still use conventional hard drives for testing.)\n", optarg); 212 | break; 213 | case 'c': // blkcnt of cache 214 | n = parse_integer(optarg, &invalid); 215 | if(invalid){ 216 | sac_error_exit("invalid cache size number %s", optarg); 217 | } 218 | NBLOCK_SSD_CACHE = NTABLE_SSD_CACHE = n / BLKSZ; 219 | printf("[User Setting] Cache Size = %s, NBLOCK_SSD_CACHE = %ld.\n", optarg, NBLOCK_SSD_CACHE); 220 | break; 221 | 222 | case 'p': // blkcnt of smr's pb 223 | n = parse_integer(optarg, &invalid); 224 | if(invalid){ 225 | sac_error_exit("invalid PB size number %s", optarg); 226 | } 227 | NBLOCK_SMR_PB = n / BLKSZ; 228 | printf("[User Setting] PB Size = %s, NBLOCK_SMR_PB = %ld.\n", optarg, NBLOCK_SMR_PB); 229 | break; 230 | 231 | case 'O': // offset, the started LBA of the workload. 232 | StartLBA = atol(optarg); 233 | printf("[User Setting] offset (the started LBA of the workload) is = %ld.\n", StartLBA); 234 | break; 235 | 236 | case 'R': // request number 237 | Request_limit = atol(optarg); 238 | printf("[User Setting] request number = %ld.\n", Request_limit); 239 | break; 240 | case 'h': 241 | printf("\ 242 | \n\ 243 | Usage: sac [OPTIONS] [Arguments]\n\ 244 | \n\ 245 | The Options include: \n\ 246 | \t--cache-dev Device file path of the ssd/ramdisk for cache layer. \n\ 247 | \t--smr-dev Device file path of the SMR drive or HDD for SMR emulator. \n\ 248 | \t--algorithm One of [SAC], [LRU], [MOST], [MOST_CDC]. \n\ 249 | \t--no-cache No cache layer, i.e. SMR only. \n\ 250 | \t--use-emulator Use emulator. \n\ 251 | \t--no-real-io No real disk I/O generated. \n\ 252 | \t--workload Workload number for [1~11] corresponding to different trace files, in which the [11] is the big dataset. \n\ 253 | \t--workload-file Or you can specofy the trace file path manually. \n\ 254 | \t--workload-mode Three workload mode: [R]:read-only, [W]:write-only, [RW]:read-write RW. \n\ 255 | \t--cache-size Cache size: [size]{+M,G}. E.g. 32G. \n\ 256 | \t--pb-size SMR Persistent Buffer size: [size]{+M,G}. E.g. 600M. \n\ 257 | \t--offset Start LBA offset of the SMR: [size]{+M,G}. E.g. 10G. \n\ 258 | \t--requests Requst number you want to run: [Nunmber]. \n\ 259 | \t--help show this menu. \n\ 260 | "); 261 | sac_exit(EXIT_SUCCESS); 262 | 263 | case '?': 264 | printf("There is an unrecognized option or option without argument: %s\n", argv[optind - 1]); 265 | sac_exit(EXIT_FAILURE); 266 | break; 267 | 268 | default: 269 | printf("There is an unrecognized option: %c\n", opt); 270 | break; 271 | } 272 | } 273 | 274 | /* Default Setting. */ 275 | if (TraceFile == NULL) 276 | { 277 | printf("ERROR: No workload file, you need to point out the workload number by the arg: --workload N, where N is 1 ~ 10: \n"); 278 | printf("[1]: src1_2.csv.req\n"); 279 | printf("[2]: wdev_0.csv.req\n"); 280 | printf("[3]: hm_0.csv.req\n"); 281 | printf("[4]: mds_0.csv.req\n"); 282 | printf("[5]: prn_0.csv.req\n"); 283 | printf("[6]: rsrch_0.csv.req\n"); 284 | printf("[7]: stg_0.csv.req\n"); 285 | printf("[8]: ts_0.csv.req\n"); 286 | printf("[9]: usr_0.csv.req\n"); 287 | printf("[10]: web_0.csv.req\n"); 288 | sac_exit(EXIT_FAILURE); 289 | } 290 | if(NO_REAL_DISK_IO != 1) 291 | { 292 | if((cache_fd = open(cache_dev_path, O_RDWR | O_DIRECT)) < 0){ 293 | sac_error_exit("Unable to open CACHE device file: %s", optarg); 294 | } 295 | if((smr_fd = open(smr_dev_path, O_RDWR | O_DIRECT)) < 0){ 296 | sac_error_exit("Unable to open SMR device file: %s", smr_dev_path); 297 | } 298 | } 299 | 300 | 301 | Cycle_Length = NBLOCK_SMR_PB; 302 | /* checking user option. */ 303 | 304 | return 0; 305 | } 306 | 307 | 308 | int initRuntimeInfo() 309 | { 310 | STT = (struct RuntimeSTAT *)multi_SHM_alloc(str_STT, sizeof(struct RuntimeSTAT)); 311 | if (STT == NULL) 312 | return errno; 313 | 314 | STT->traceId = TraceID; 315 | STT->startLBA = StartLBA; 316 | STT->workload_mode = Workload_Mode; 317 | STT->cacheUsage = 0; 318 | STT->cacheLimit = NBLOCK_SSD_CACHE; 319 | 320 | STT->wtrAmp_cur = 0; 321 | STT->WA_sum = 0; 322 | STT->n_RMW = 0; 323 | return 0; 324 | } 325 | 326 | 327 | int main(int argc, char **argv) 328 | { 329 | analyze_opts(argc, argv); 330 | 331 | /* Open Device */ 332 | initRuntimeInfo(); 333 | CacheLayer_Init(); 334 | 335 | if (EMULATION) 336 | { 337 | /* Emulator */ 338 | InitEmulator(); 339 | } 340 | 341 | trace_to_iocall(TraceFile, StartLBA); 342 | 343 | // Finish Job. Exit Safely. 344 | if (EMULATION) 345 | { 346 | Emu_PrintStatistic(); 347 | CloseSMREmu(); 348 | } 349 | if(NO_REAL_DISK_IO) 350 | { 351 | close(smr_fd); 352 | close(cache_fd); 353 | } 354 | 355 | exit(EXIT_SUCCESS); 356 | } 357 | -------------------------------------------------------------------------------- /src/smr-emulator/emulator_v2.c: -------------------------------------------------------------------------------- 1 | /* 2 | This is a updated versoin (v2) based on log-simulater. 3 | There are two most important new designs: 4 | 1. FIFO Write adopts APPEND_ONLY, instead of in-place updates. 5 | 2. When the block choosing to evict out of FIFO is a "old version", 6 | then drop it off,won't activate write-back, and move head pointer to deal with next one. 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../global.h" 17 | #include "../statusDef.h" 18 | #include "../report.h" 19 | #include "../cache.h" 20 | #include "../timerUtils.h" 21 | #include "hashtb_pb.h" 22 | 23 | 24 | #define OFF_BAND_TMP_PERSISIT 0 // The head 80MB of FIFO for temp persistence band needed to clean. 25 | #define OFF_PB 80*1024*1024 26 | #define OFF_BAND_REGION 20*1024*1000*1000 // 20GB 27 | 28 | #ifdef EMU_NO_DISK_IO 29 | #define DISK_READ(fd,buf,size,offset) (size) 30 | #define DISK_WRITE(fd,buf,size,offset) (size) 31 | #else 32 | #define DISK_READ(fd,buf,size,offset) (pread(fd,buf,size,offset)) 33 | #define DISK_WRITE(fd,buf,size,offset) (pwrite(fd,buf,size,offset)) 34 | #endif 35 | 36 | static FIFOCtrl global_fifo_ctrl; 37 | static FIFODesc* fifo_desp_array; 38 | static char* BandBuffer; 39 | static blksize_t NSMRBands = 266600; // smr band cnt = 266600; almost 8TB data size. 40 | static unsigned long BNDSZ = 40*1000*1024; // bandsize =40MB (20MB~40MB) 41 | 42 | 43 | int ACCESS_FLAG = 1; 44 | 45 | pthread_mutex_t simu_smr_fifo_mutex; 46 | 47 | static long band_size_num; 48 | static long num_each_size; 49 | 50 | static FIFODesc* getFIFODesp(); 51 | // static void* smr_fifo_monitor_thread(); 52 | static void flushFIFO(); 53 | 54 | static long simu_read_smr_bands; 55 | static long simu_flush_bands; 56 | static long simu_flush_band_size; 57 | 58 | static blkcnt_t simu_n_collect_fifo; 59 | static blkcnt_t simu_n_read_fifo; 60 | static blkcnt_t simu_n_write_fifo; 61 | static blkcnt_t simu_n_read_smr; 62 | 63 | static blkcnt_t simu_n_fifo_write_HIT = 0; 64 | static double simu_time_read_fifo; 65 | static double simu_time_read_smr; 66 | static double simu_time_write_smr; 67 | static double simu_time_write_fifo; 68 | 69 | static double simu_time_collectFIFO = 0; /** To monitor the efficent of read all blocks in same band **/ 70 | 71 | 72 | static int invalidDespInFIFO(FIFODesc* desp); 73 | #define isFIFOEmpty (global_fifo_ctrl.head == global_fifo_ctrl.tail) 74 | #define isFIFOFull ((global_fifo_ctrl.tail + 1) % (NBLOCK_SMR_PB + 1) == global_fifo_ctrl.head) 75 | 76 | static unsigned long getBandSize(off_t offset); 77 | static off_t getBandOffset(off_t blk_off); 78 | 79 | /** AIO related for blocks collected in FIFO **/ 80 | #define max_aio_count 6000 81 | struct aiocb aiolist[max_aio_count]; 82 | struct aiocb* aiocb_addr_list[max_aio_count];/* >= band block size */ 83 | 84 | 85 | /* 86 | * init inner ssd buffer hash table, strategy_control, buffer, work_mem 87 | */ 88 | void InitEmulator() 89 | { 90 | /* initialliz related constants */ 91 | band_size_num = (BNDSZ / 1024000) / 2 + 1; 92 | num_each_size = NSMRBands / band_size_num; 93 | 94 | global_fifo_ctrl.n_used = 0; 95 | global_fifo_ctrl.head = global_fifo_ctrl.tail = 0; 96 | 97 | posix_memalign((void**) &fifo_desp_array, 1024,sizeof(FIFODesc) * (NBLOCK_SMR_PB + 1)); 98 | 99 | 100 | FIFODesc* fifo_hdr = fifo_desp_array; 101 | long i; 102 | for (i = 0; i < (NBLOCK_SMR_PB + 1); fifo_hdr++, i++) 103 | { 104 | fifo_hdr->despId = i; 105 | fifo_hdr->isValid = 0; 106 | } 107 | 108 | posix_memalign((void**) &BandBuffer, 512, sizeof(char) * BNDSZ * 50); 109 | 110 | pthread_mutex_init(&simu_smr_fifo_mutex, NULL); 111 | 112 | /** AIO related **/ 113 | 114 | /** statistic related **/ 115 | simu_read_smr_bands = 0; 116 | simu_flush_bands = 0; 117 | simu_flush_band_size = 0; 118 | 119 | simu_n_collect_fifo = 0; 120 | simu_n_read_fifo = 0; 121 | simu_n_write_fifo = 0; 122 | simu_n_read_smr = 0; 123 | simu_time_read_fifo = 0; 124 | simu_time_read_smr = 0; 125 | simu_time_write_smr = 0; 126 | simu_time_write_fifo = 0; 127 | 128 | initSSDTable(NBLOCK_SMR_PB + 1); 129 | 130 | Log_emu = fopen(Log_emu_path, "w+"); 131 | if(Log_emu == NULL) 132 | sac_error_exit("cannot open log: Log_emu."); 133 | } 134 | 135 | /** \brief 136 | * LEGACY: To monitor the FIFO in SMR, and do cleanning operation when idle status. 137 | */ 138 | // static void* 139 | // smr_fifo_monitor_thread() 140 | // { 141 | // pthread_t th = pthread_self(); 142 | // pthread_detach(th); 143 | // int interval = 10; 144 | // printf("Simulator Auto-clean thread [%lu], clean interval %d seconds.\n",th,interval); 145 | 146 | // while (1) 147 | // { 148 | // pthread_mutex_lock(&simu_smr_fifo_mutex); 149 | // if (!ACCESS_FLAG) 150 | // { 151 | // flushFIFO(); 152 | // pthread_mutex_unlock(&simu_smr_fifo_mutex); 153 | // if (DEBUG) 154 | // printf("[INFO] freeStrategySSD():--------after clean\n"); 155 | // } 156 | // else 157 | // { 158 | // ACCESS_FLAG = 0; 159 | // pthread_mutex_unlock(&simu_smr_fifo_mutex); 160 | // sleep(interval); 161 | // } 162 | // } 163 | // return NULL; 164 | // } 165 | 166 | int 167 | simu_smr_read(char *buffer, size_t size, off_t offset) 168 | { 169 | pthread_mutex_lock(&simu_smr_fifo_mutex); 170 | DespTag tag; 171 | FIFODesc *ssd_hdr; 172 | size_t i; 173 | int returnCode; 174 | long ssd_hash; 175 | long despId; 176 | struct timeval tv_start,tv_stop; 177 | 178 | for (i = 0; i * BLKSZ < size; i++) 179 | { 180 | tag.offset = offset + i * BLKSZ; 181 | ssd_hash = ssdtableHashcode(tag); 182 | despId = ssdtableLookup(tag, ssd_hash); 183 | 184 | if (despId >= 0) 185 | { 186 | /* read from fifo */ 187 | simu_n_read_fifo++; 188 | ssd_hdr = fifo_desp_array + despId; 189 | 190 | _TimerLap(&tv_start); 191 | returnCode = DISK_READ(smr_fd, buffer, BLKSZ, ssd_hdr->despId * BLKSZ + OFF_PB); 192 | if (returnCode < 0) 193 | { 194 | printf("[ERROR] smrread():-------read from PB: fd=%d, errorcode=%d, offset=%lu\n", smr_fd, returnCode, ssd_hdr->despId * BLKSZ); 195 | exit(-1); 196 | } 197 | _TimerLap(&tv_stop); 198 | simu_time_read_fifo += TimerInterval_SECOND(&tv_start,&tv_stop); 199 | } 200 | else 201 | { 202 | /* read from actual smr */ 203 | simu_n_read_smr++; 204 | _TimerLap(&tv_start); 205 | 206 | returnCode = DISK_READ(smr_fd, buffer, BLKSZ, offset + i * BLKSZ + OFF_BAND_REGION); 207 | if (returnCode < 0) 208 | { 209 | printf("[ERROR] smrread():-------read from smr disk: fd=%d, errorcode=%d, offset=%lu\n", smr_fd, returnCode, offset + i * BLKSZ); 210 | exit(-1); 211 | } 212 | _TimerLap(&tv_stop); 213 | simu_time_read_smr += TimerInterval_SECOND(&tv_start,&tv_stop); 214 | } 215 | } 216 | ACCESS_FLAG = 1; 217 | pthread_mutex_unlock(&simu_smr_fifo_mutex); 218 | return 0; 219 | } 220 | 221 | int 222 | simu_smr_write(char *buffer, size_t size, off_t offset) 223 | { 224 | pthread_mutex_lock(&simu_smr_fifo_mutex); 225 | DespTag tag; 226 | FIFODesc *ssd_hdr; 227 | size_t i; 228 | int returnCode = 0; 229 | long ssd_hash; 230 | static struct timeval tv_start,tv_stop; 231 | 232 | for (i = 0; i * BLKSZ < size; i++) 233 | { 234 | tag.offset = offset + i * BLKSZ; 235 | 236 | /* APPEND_ONLY */ 237 | ssd_hdr = getFIFODesp(); 238 | ssd_hdr->tag = tag; 239 | 240 | /* Update HashTable and Descriptor array */ 241 | ssd_hash = ssdtableHashcode(tag); 242 | long old_despId = ssdtableUpdate(tag, ssd_hash, ssd_hdr->despId); 243 | if(old_despId>=0){ 244 | FIFODesc* oldDesp = fifo_desp_array + old_despId; 245 | invalidDespInFIFO(oldDesp); ///invalid the old desp 246 | } 247 | 248 | _TimerLap(&tv_start); 249 | returnCode = DISK_WRITE(smr_fd, buffer, BLKSZ, ssd_hdr->despId * BLKSZ + OFF_PB); 250 | if (returnCode < 0) 251 | { 252 | printf("[ERROR] smrwrite():-------write to smr disk: fd=%d, errorcode=%d, offset=%lu\n", smr_fd, returnCode, offset + i * BLKSZ); 253 | exit(-1); 254 | } 255 | _TimerLap(&tv_stop); 256 | simu_time_write_fifo += TimerInterval_SECOND(&tv_start,&tv_stop); 257 | simu_n_write_fifo ++; 258 | } 259 | ACCESS_FLAG = 1; 260 | pthread_mutex_unlock(&simu_smr_fifo_mutex); 261 | 262 | 263 | return 0; 264 | } 265 | 266 | static int 267 | invalidDespInFIFO(FIFODesc* desp) 268 | { 269 | desp->isValid = 0; 270 | global_fifo_ctrl.n_used--; 271 | int isHeadChanged = 0; 272 | while(!fifo_desp_array[global_fifo_ctrl.head].isValid && !isFIFOEmpty) 273 | { 274 | global_fifo_ctrl.head = (global_fifo_ctrl.head + 1) % (NBLOCK_SMR_PB + 1); 275 | isHeadChanged = 1; 276 | } 277 | return isHeadChanged; 278 | } 279 | 280 | static FIFODesc * 281 | getFIFODesp() 282 | { 283 | FIFODesc* newDesp; 284 | if(isFIFOFull) 285 | { 286 | /* Log structure array is full fill */ 287 | flushFIFO(); 288 | } 289 | 290 | /* Append to tail */ 291 | newDesp = fifo_desp_array + global_fifo_ctrl.tail; 292 | newDesp->isValid = 1; 293 | global_fifo_ctrl.tail = (global_fifo_ctrl.tail + 1) % (NBLOCK_SMR_PB + 1); 294 | 295 | return newDesp; 296 | } 297 | 298 | /** Persistent Buffer write-back dirty blocks using RMW Model: Read->Modify->Write. **/ 299 | static void 300 | flushFIFO() 301 | { 302 | if(global_fifo_ctrl.head == global_fifo_ctrl.tail) // Log structure array is empty. 303 | return; 304 | 305 | int returnCode; 306 | struct timeval tv_start, tv_stop; 307 | long dirty_n_inBand = 0; 308 | double wtrAmp; 309 | 310 | FIFODesc* leader = fifo_desp_array + global_fifo_ctrl.head; 311 | 312 | /* Create a band-sized buffer for readind and flushing whole band bytes */ 313 | long thisBandSize = getBandSize(leader->tag.offset); 314 | off_t thisBandOff = getBandOffset(leader->tag.offset); 315 | 316 | /** R **/ 317 | /* read whole band from smr to buffer*/ 318 | _TimerLap(&tv_start); 319 | returnCode = DISK_READ(smr_fd, BandBuffer, thisBandSize, thisBandOff + OFF_BAND_REGION); 320 | if((returnCode) != thisBandSize) 321 | { 322 | printf("[ERROR] flushFIFO():---------read from smr: fd=%d, errorcode=%d, offset=%lu\n", 323 | smr_fd, returnCode, thisBandOff); 324 | exit(-1); 325 | } 326 | _TimerLap(&tv_stop); 327 | simu_time_read_smr += TimerInterval_SECOND(&tv_start, &tv_stop); 328 | simu_read_smr_bands++; 329 | 330 | /** M **/ 331 | /* Combine cached pages from FIFO which are belong to the same band */ 332 | 333 | /** ---------------DEBUG BLOCK 1----------------------- **/ 334 | struct timeval tv_collect_start, tv_collect_stop; 335 | double collect_time; 336 | _TimerLap(&tv_collect_start); 337 | /**--------------------------------------------------- **/ 338 | long curPos = leader->despId; 339 | while(curPos != global_fifo_ctrl.tail) 340 | { 341 | FIFODesc* curDesp = fifo_desp_array + curPos; 342 | long nextPos = (curDesp->despId + 1) % (NBLOCK_SMR_PB + 1); 343 | 344 | /* If the block belongs the same band with the header of fifo. */ 345 | if (curDesp->isValid && (curDesp->tag.offset - thisBandOff) < thisBandSize && curDesp->tag.offset >= thisBandOff) 346 | { 347 | #ifdef EMULATION_AIO 348 | off_t offset_inband = curDesp->tag.offset - thisBandOff; 349 | static int aio_read_cnt = 0; 350 | 351 | struct aiocb* aio_n = aiolist + aio_read_cnt; 352 | aio_n->aio_fildes = fd_fifo_part; 353 | aio_n->aio_offset = curPos * BLKSZ; 354 | aio_n->aio_buf = BandBuffer + offset_inband * BLKSZ; 355 | aio_n->aio_nbytes = BLKSZ; 356 | aio_n->aio_lio_opcode = LIO_READ; 357 | aiocb_addr_list[aio_read_cnt] = aio_n; 358 | aio_read_cnt++; 359 | #else 360 | _TimerLap(&tv_start); 361 | returnCode = DISK_READ(smr_fd, BandBuffer + (curDesp->tag.offset - thisBandOff) * BLKSZ, BLKSZ, curPos * BLKSZ + OFF_PB); //ff_t offset_inband = curDesp->tag.offset - thisBandOff; 362 | if (returnCode < 0) 363 | { 364 | printf("[ERROR] flushFIFO():-------read from PB: fd=%d, errorcode=%d, offset=%lu\n", smr_fd, returnCode, curPos * BLKSZ); 365 | exit(-1); 366 | } 367 | _TimerLap(&tv_stop); 368 | simu_time_read_fifo += TimerInterval_SECOND(&tv_start,&tv_stop); 369 | #endif // EMULATION_AIO 370 | /* clean the meta data */ 371 | dirty_n_inBand++; 372 | unsigned long hash_code = ssdtableHashcode(curDesp->tag); 373 | ssdtableDelete(curDesp->tag, hash_code); 374 | 375 | int isHeadChanged = invalidDespInFIFO(curDesp); // Invalidate the current block and check if the head block get changed. 376 | if(isHeadChanged) 377 | { 378 | curPos = global_fifo_ctrl.head; 379 | continue; 380 | } 381 | } 382 | curPos = nextPos; 383 | } 384 | simu_n_collect_fifo += dirty_n_inBand; 385 | #ifdef EMULATION_AIO 386 | #ifndef EMU_NO_DISK_IO 387 | _TimerLap(&tv_start); 388 | static int cnt = 0; 389 | printf("start aio read [%d]...\n",++cnt); 390 | int ret_aio = lio_listio(LIO_WAIT,aiocb_addr_list,aio_read_cnt,NULL); 391 | printf("end aio\n",++cnt); 392 | if(ret_aio < 0) 393 | { 394 | char log[128]; 395 | sprintf(log,"Flush [%ld] times ERROR: AIO read list from FIFO Failure[%d].\n",simu_flush_bands+1,ret_aio); 396 | sac_log(log, Log_emu); 397 | } 398 | _TimerLap(&tv_stop); 399 | simu_time_read_fifo += TimerInterval_SECOND(&tv_start,&tv_stop); 400 | #endif // EMU_NO_DISK_IO 401 | #endif // EMULATION_AIO 402 | /**--------------------------------------------------- **/ 403 | _TimerLap(&tv_collect_stop); 404 | collect_time = TimerInterval_SECOND(&tv_collect_start, &tv_collect_stop); 405 | simu_time_collectFIFO += collect_time; 406 | /** ---------------DEBUG BLOCK 1----------------------- **/ 407 | 408 | /* flush whole band to smr */ 409 | _TimerLap(&tv_start); 410 | 411 | /** W **/ 412 | returnCode = DISK_WRITE(smr_fd, BandBuffer, thisBandSize, thisBandOff + OFF_BAND_REGION); 413 | if (returnCode < thisBandSize) 414 | { 415 | printf("[ERROR] flushFIFO():-------write to smr: fd=%d, errorcode=%d, offset=%lu\n", smr_fd, returnCode, thisBandOff); 416 | exit(-1); 417 | } 418 | _TimerLap(&tv_stop); 419 | 420 | simu_time_write_smr += TimerInterval_SECOND(&tv_start,&tv_stop); 421 | simu_flush_bands++; 422 | simu_flush_band_size += thisBandSize; 423 | 424 | wtrAmp = (double)thisBandSize / (dirty_n_inBand * BLKSZ); 425 | STT->wtrAmp_cur = wtrAmp; 426 | STT->WA_sum += wtrAmp; 427 | STT->n_RMW ++; 428 | 429 | char log[256]; 430 | sprintf(log,"[Emulator]: RMW number:%lu, write amplifcation:%f\n",STT->n_RMW, wtrAmp); 431 | sac_log(log, Log_emu); 432 | sprintf(log,"[Emulator]: dirty blocks in band colect=%ld, bandsize=%ld\n", dirty_n_inBand, thisBandSize/BLKSZ); 433 | sac_log(log, Log_emu); 434 | } 435 | 436 | static unsigned long 437 | getBandSize(off_t offset) 438 | { 439 | long i, size, total_size = 0; 440 | for (i = 0; i < band_size_num; i++) 441 | { 442 | size = BNDSZ / 2 + i * 1024000; 443 | if (total_size + size * num_each_size >= offset) 444 | return size; 445 | total_size += size * num_each_size; 446 | } 447 | return 0; 448 | } 449 | static off_t 450 | getBandOffset(off_t blk_off) 451 | { 452 | long i, size, total_size = 0; 453 | for (i = 0; i < band_size_num; i++) 454 | { 455 | size = BNDSZ / 2 + i * 1024000; 456 | if (total_size + size * num_each_size > blk_off) 457 | return total_size + ((blk_off - total_size) / size) * size; 458 | total_size += size * num_each_size; 459 | } 460 | return 0; 461 | } 462 | 463 | void Emu_PrintStatistic() 464 | { 465 | printf("----------------EMULATION------------\n"); 466 | #ifndef EMU_NO_DISK_IO 467 | printf("Time:\n"); 468 | printf("PB_read_blks:\t%lf\nPB_write_blks:\t%lf\nSMR_read_blks:\t%lf\nFlush SMR:\t%lf\n",simu_time_read_fifo, simu_time_write_fifo, simu_time_read_smr, simu_time_write_smr); 469 | printf("Total I/O:\t%lf\n", simu_time_read_fifo+simu_time_write_fifo+simu_time_read_smr+simu_time_write_smr); 470 | printf("FIFO Collect:\t%lf\n",simu_time_collectFIFO); 471 | #endif 472 | printf("Block/Band Count:\n"); 473 | printf("PB_read_blks:\t%ld\nPB_write_blks:\t%ld\nGC_collect_blks:\t%ld\nSMR_read_blks:\t%ld\nPB_write_hits:\t%ld\n",simu_n_read_fifo, simu_n_write_fifo,simu_n_collect_fifo, simu_n_read_smr, simu_n_fifo_write_HIT); 474 | printf("RMWs:\t%ld\nGC_bandsize(Byte):\t%ld\n", simu_flush_bands, simu_flush_band_size); 475 | printf("WA_avg:\t%lf\n",(float)(simu_flush_band_size / BLKSZ) / STT->flush_hdd_blocks); 476 | } 477 | 478 | void Emu_ResetStatisic() 479 | { 480 | simu_n_read_fifo = simu_n_write_fifo = simu_n_collect_fifo = simu_n_read_smr = simu_n_fifo_write_HIT = 481 | simu_read_smr_bands = simu_flush_bands = simu_flush_band_size = 482 | STT->flush_hdd_blocks = 0; 483 | } 484 | 485 | void CloseSMREmu() 486 | { 487 | fclose(Log_emu); 488 | } 489 | -------------------------------------------------------------------------------- /src/cache.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "trace2call.h" 7 | #include "timerUtils.h" 8 | #include "cache.h" 9 | #include "hashtable_utils.h" 10 | 11 | #include "strategy/strategies.h" 12 | 13 | #include "smr-emulator/emulator_v2.h" 14 | 15 | #include "shmlib.h" 16 | #include "report.h" 17 | 18 | SSDBufDespCtrl *ssd_buf_desp_ctrl; 19 | SSDBufDesp *ssd_buf_desps; 20 | 21 | /* If Defined R/W Cache Space Static Allocated */ 22 | 23 | static int init_SSDDescriptorBuffer(); 24 | static int init_StatisticObj(); 25 | static void flushSSDBuffer(SSDBufDesp *ssd_buf_hdr); 26 | static SSDBufDesp *allocSSDBuf(SSDBufTag ssd_buf_tag, int *found, int alloc4What); 27 | static SSDBufDesp *pop_freebuf(); 28 | static int push_freebuf(SSDBufDesp *freeDesp); 29 | 30 | static int initStrategySSDBuffer(); 31 | // static long Strategy_Desp_LogOut(); 32 | static int Strategy_Desp_HitIn(SSDBufDesp *desp); 33 | static int Strategy_Desp_LogIn(SSDBufDesp *desp); 34 | //#define isSamebuf(SSDBufTag tag1, SSDBufTag tag2) (tag1 == tag2) 35 | #define CopySSDBufTag(objectTag, sourceTag) (objectTag = sourceTag) 36 | #define IsDirty(flag) ((flag & SSD_BUF_DIRTY) != 0) 37 | #define IsClean(flag) ((flag & SSD_BUF_DIRTY) == 0) 38 | 39 | void _LOCK(pthread_mutex_t *lock); 40 | void _UNLOCK(pthread_mutex_t *lock); 41 | 42 | /* stopwatch */ 43 | static timeval tv_start, tv_stop; 44 | // static timeval tv_bastart, tv_bastop; 45 | // static timeval tv_cmstart, tv_cmstop; 46 | int IsHit; 47 | microsecond_t msec_r_hdd, msec_w_hdd, msec_r_ssd, msec_w_ssd, msec_bw_hdd = 0; 48 | 49 | /* Device I/O operation with Timer */ 50 | static int dev_pread(int fd, void *buf, size_t nbytes, off_t offset); 51 | static int dev_pwrite(int fd, void *buf, size_t nbytes, off_t offset); 52 | static int dev_simu_read(void *buf, size_t nbytes, off_t offset); 53 | static int dev_simu_write(void *buf, size_t nbytes, off_t offset); 54 | 55 | static char *ssd_buffer; 56 | 57 | extern struct RuntimeSTAT *STT; 58 | extern struct InitUsrInfo UsrInfo; 59 | 60 | /* 61 | * init buffer hash table, strategy_control, buffer, work_mem 62 | */ 63 | void CacheLayer_Init() 64 | { 65 | int r_initdesp = init_SSDDescriptorBuffer(); 66 | int r_initstrategybuf = initStrategySSDBuffer(); 67 | int r_initbuftb = HashTab_Init(); 68 | int r_initstt = init_StatisticObj(); 69 | 70 | printf("init_Strategy: %d, init_table: %d, init_desp: %d, inti_Stt: %d\n", 71 | r_initstrategybuf, r_initbuftb, r_initdesp, r_initstt); 72 | 73 | if (r_initdesp == -1 || r_initstrategybuf == -1 || r_initbuftb == -1 || r_initstt == -1) 74 | exit(EXIT_FAILURE); 75 | 76 | int returnCode = posix_memalign((void **)&ssd_buffer, 512, sizeof(char) * BLKSZ); 77 | if (returnCode < 0) 78 | { 79 | printf("[ERROR] flushSSDBuffer():--------posix memalign\n"); 80 | exit(EXIT_FAILURE); 81 | } 82 | } 83 | 84 | static int 85 | init_SSDDescriptorBuffer() 86 | { 87 | int stat = multi_SHM_lock_n_check("LOCK_SSDBUF_DESP"); 88 | if (stat == 0) 89 | { 90 | ssd_buf_desp_ctrl = (SSDBufDespCtrl *)multi_SHM_alloc(SHM_SSDBUF_DESP_CTRL, sizeof(SSDBufDespCtrl)); 91 | ssd_buf_desps = (SSDBufDesp *)multi_SHM_alloc(SHM_SSDBUF_DESPS, sizeof(SSDBufDesp) * NBLOCK_SSD_CACHE); 92 | 93 | ssd_buf_desp_ctrl->n_usedssd = 0; 94 | ssd_buf_desp_ctrl->first_freessd = 0; 95 | multi_SHM_mutex_init(&ssd_buf_desp_ctrl->lock); 96 | 97 | long i; 98 | SSDBufDesp *ssd_buf_hdr = ssd_buf_desps; 99 | for (i = 0; i < NBLOCK_SSD_CACHE; ssd_buf_hdr++, i++) 100 | { 101 | ssd_buf_hdr->serial_id = i; 102 | ssd_buf_hdr->ssd_buf_id = i; 103 | ssd_buf_hdr->ssd_buf_flag = 0; 104 | ssd_buf_hdr->next_freessd = i + 1; 105 | multi_SHM_mutex_init(&ssd_buf_hdr->lock); 106 | } 107 | ssd_buf_desps[NBLOCK_SSD_CACHE - 1].next_freessd = -1; 108 | } 109 | else 110 | { 111 | ssd_buf_desp_ctrl = (SSDBufDespCtrl *)multi_SHM_get(SHM_SSDBUF_DESP_CTRL, sizeof(SSDBufDespCtrl)); 112 | ssd_buf_desps = (SSDBufDesp *)multi_SHM_get(SHM_SSDBUF_DESPS, sizeof(SSDBufDesp) * NBLOCK_SSD_CACHE); 113 | } 114 | multi_SHM_unlock("LOCK_SSDBUF_DESP"); 115 | return stat; 116 | } 117 | 118 | static int 119 | init_StatisticObj() 120 | { 121 | STT->hitnum_s = 0; 122 | STT->hitnum_r = 0; 123 | STT->hitnum_w = 0; 124 | STT->load_ssd_blocks = 0; 125 | STT->flush_ssd_blocks = 0; 126 | STT->load_hdd_blocks = 0; 127 | STT->flush_hdd_blocks = 0; 128 | STT->flush_clean_blocks = 0; 129 | 130 | STT->time_read_hdd = 0.0; 131 | STT->time_write_hdd = 0.0; 132 | STT->time_read_ssd = 0.0; 133 | STT->time_write_ssd = 0.0; 134 | STT->hashmiss_sum = 0; 135 | STT->hashmiss_read = 0; 136 | STT->hashmiss_write = 0; 137 | 138 | STT->wt_hit_rd = STT->rd_hit_wt = 0; 139 | STT->incache_n_clean = STT->incache_n_dirty = 0; 140 | return 0; 141 | } 142 | 143 | static void 144 | flushSSDBuffer(SSDBufDesp *ssd_buf_hdr) 145 | { 146 | if (IsClean(ssd_buf_hdr->ssd_buf_flag)) 147 | { 148 | STT->flush_clean_blocks++; 149 | // CM_Reg_EvictBlk(ssd_buf_hdr->ssd_buf_tag, ssd_buf_hdr->ssd_buf_flag, 0); 150 | return; 151 | } 152 | 153 | dev_pread(cache_fd, ssd_buffer, BLKSZ, ssd_buf_hdr->ssd_buf_id * BLKSZ); 154 | msec_r_ssd = TimerInterval_MICRO(&tv_start, &tv_stop); 155 | STT->time_read_ssd += Mirco2Sec(msec_r_ssd); 156 | STT->load_ssd_blocks++; 157 | // IO 158 | if (EMULATION) 159 | dev_simu_write(ssd_buffer, BLKSZ, ssd_buf_hdr->ssd_buf_tag.offset); 160 | else 161 | dev_pwrite(smr_fd, ssd_buffer, BLKSZ, ssd_buf_hdr->ssd_buf_tag.offset); 162 | 163 | msec_w_hdd = TimerInterval_MICRO(&tv_start, &tv_stop); 164 | STT->time_write_hdd += Mirco2Sec(msec_w_hdd); 165 | STT->flush_hdd_blocks++; 166 | 167 | //CM_Reg_EvictBlk(ssd_buf_hdr->ssd_buf_tag, ssd_buf_hdr->ssd_buf_flag, msec_w_hdd + msec_r_ssd); 168 | // 169 | // static char log[256]; 170 | // static unsigned long cnt = 0; 171 | // cnt++; 172 | // sprintf(log, "%ld, %d\n", cnt, msec_w_hdd); 173 | // sac_log(log, log_lat_pb); 174 | } 175 | 176 | static void flagOp(SSDBufDesp *ssd_buf_hdr, int opType) 177 | { 178 | ssd_buf_hdr->ssd_buf_flag |= SSD_BUF_VALID; 179 | if (opType) 180 | { 181 | // write operation 182 | ssd_buf_hdr->ssd_buf_flag |= SSD_BUF_DIRTY; 183 | } 184 | } 185 | 186 | static SSDBufDesp * 187 | allocSSDBuf(SSDBufTag ssd_buf_tag, int *found, int alloc4What) 188 | { 189 | 190 | /* Lookup if already cached. */ 191 | SSDBufDesp *ssd_buf_hdr; //returned value. 192 | unsigned long ssd_buf_hash = HashTab_GetHashCode(ssd_buf_tag); 193 | long ssd_buf_id = HashTab_Lookup(ssd_buf_tag, ssd_buf_hash); 194 | 195 | /* Cache HIT */ 196 | if (ssd_buf_id >= 0) 197 | { 198 | ssd_buf_hdr = &ssd_buf_desps[ssd_buf_id]; 199 | _LOCK(&ssd_buf_hdr->lock); 200 | 201 | /* count wt_hit_rd and rd_hit_wt */ 202 | if (alloc4What == 0 && IsDirty(ssd_buf_hdr->ssd_buf_flag)) 203 | STT->rd_hit_wt++; 204 | else if (alloc4What != 0 && IsClean(ssd_buf_hdr->ssd_buf_flag)) 205 | { 206 | STT->wt_hit_rd++; 207 | STT->incache_n_clean--; 208 | STT->incache_n_dirty++; 209 | } 210 | 211 | flagOp(ssd_buf_hdr, alloc4What); // tell strategy block's flag changes. 212 | Strategy_Desp_HitIn(ssd_buf_hdr); // need lock. 213 | 214 | STT->hitnum_s++; 215 | *found = 1; 216 | 217 | return ssd_buf_hdr; 218 | } 219 | 220 | /* Cache MISS */ 221 | *found = 0; 222 | //*isCallBack = CM_TryCallBack(ssd_buf_tag); 223 | enum_t_vict suggest_type = ENUM_B_Any; 224 | 225 | /* When there is NO free SSD space for cache, 226 | * pick serveral in-used cache block to evict according to strategy */ 227 | if ((ssd_buf_hdr = pop_freebuf()) == NULL) 228 | { 229 | // Cache Out: 230 | if (STT->incache_n_clean == 0) 231 | suggest_type = ENUM_B_Dirty; 232 | else if (STT->incache_n_dirty == 0) 233 | suggest_type = ENUM_B_Clean; 234 | 235 | static int max_n_batch = 1024; 236 | long buf_despid_array[max_n_batch]; 237 | int n_evict; 238 | switch (EvictStrategy) 239 | { 240 | 241 | case SAC: 242 | n_evict = LogOut_SAC(buf_despid_array, max_n_batch, suggest_type); 243 | break; 244 | case MOST: 245 | n_evict = LogOut_most(buf_despid_array, max_n_batch); 246 | break; 247 | case MOST_CDC: 248 | n_evict = LogOut_most_cdc(buf_despid_array, max_n_batch, suggest_type); 249 | break; 250 | case LRU_private: 251 | n_evict = Unload_Buf_LRU_private(buf_despid_array, max_n_batch); 252 | break; 253 | default: 254 | sac_warning("Current cache algorithm dose not support batched process."); 255 | exit(EXIT_FAILURE); 256 | } 257 | 258 | int k = 0; 259 | while (k < n_evict) 260 | { 261 | long out_despId = buf_despid_array[k]; 262 | ssd_buf_hdr = &ssd_buf_desps[out_despId]; 263 | 264 | // TODO Flush 265 | flushSSDBuffer(ssd_buf_hdr); 266 | 267 | /* Reset Metadata */ 268 | // Clear Hashtable item. 269 | SSDBufTag oldtag = ssd_buf_hdr->ssd_buf_tag; 270 | unsigned long hash = HashTab_GetHashCode(oldtag); 271 | HashTab_Delete(oldtag, hash); 272 | 273 | // Reset buffer descriptor info. 274 | IsDirty(ssd_buf_hdr->ssd_buf_flag) ? STT->incache_n_dirty-- : STT->incache_n_clean--; 275 | ssd_buf_hdr->ssd_buf_flag &= ~(SSD_BUF_VALID | SSD_BUF_DIRTY); 276 | 277 | // Push back to free list 278 | push_freebuf(ssd_buf_hdr); 279 | k++; 280 | } 281 | ssd_buf_hdr = pop_freebuf(); 282 | } 283 | 284 | // Set Metadata for the new block. 285 | flagOp(ssd_buf_hdr, alloc4What); 286 | CopySSDBufTag(ssd_buf_hdr->ssd_buf_tag, ssd_buf_tag); 287 | 288 | HashTab_Insert(ssd_buf_tag, ssd_buf_hash, ssd_buf_hdr->serial_id); 289 | Strategy_Desp_LogIn(ssd_buf_hdr); 290 | IsDirty(ssd_buf_hdr->ssd_buf_flag) ? STT->incache_n_dirty++ : STT->incache_n_clean++; 291 | 292 | return ssd_buf_hdr; 293 | } 294 | 295 | static int 296 | initStrategySSDBuffer() 297 | { 298 | switch (EvictStrategy) 299 | { 300 | case LRU_private: 301 | return initSSDBufferFor_LRU_private(); 302 | case SAC: 303 | return Init_SAC(); 304 | // case OLDPORE: 305 | // return Init_oldpore(); 306 | case MOST: 307 | return Init_most(); 308 | case MOST_CDC: 309 | return Init_most_cdc(); 310 | } 311 | return -1; 312 | } 313 | 314 | 315 | // static long 316 | // Strategy_Desp_LogOut(unsigned flag) // LEGACY 317 | // { 318 | // STT->cacheUsage--; 319 | // switch (EvictStrategy) 320 | // { 321 | // // case LRU_global: return Unload_LRUBuf(); 322 | // case LRU_private: 323 | // sac_warning("LRU wrong time function revoke, please use BATHCH configure.\n"); 324 | // case LRU_CDC: 325 | // sac_warning("LRU_CDC wrong time function revoke\n"); 326 | // case SAC: 327 | // sac_warning("SAC wrong time function revoke\n"); 328 | // case MOST: 329 | // sac_warning("MOST wrong time function revoke\n"); 330 | // case MOST_CDC: 331 | // sac_warning("MOST_CDC wrong time functioFn revoke\n"); 332 | // } 333 | // return -1; 334 | // } 335 | 336 | static int 337 | Strategy_Desp_HitIn(SSDBufDesp *desp) 338 | { 339 | switch (EvictStrategy) 340 | { 341 | // case LRU_global: return hitInLRUBuffer(desp->serial_id); 342 | case LRU_private: 343 | return hitInBuffer_LRU_private(desp->serial_id); 344 | case SAC: 345 | return Hit_SAC(desp->serial_id, desp->ssd_buf_flag); 346 | case MOST: 347 | return Hit_most(desp->serial_id, desp->ssd_buf_flag); 348 | case MOST_CDC: 349 | return Hit_most_cdc(desp->serial_id, desp->ssd_buf_flag); 350 | } 351 | return -1; 352 | } 353 | 354 | static int 355 | Strategy_Desp_LogIn(SSDBufDesp *desp) 356 | { 357 | STT->cacheUsage++; 358 | switch (EvictStrategy) 359 | { 360 | // case LRU_global: return insertLRUBuffer(serial_id); 361 | case LRU_private: 362 | return insertBuffer_LRU_private(desp->serial_id); 363 | // case LRU_batch: return insertBuffer_LRU_batch(serial_id); 364 | // case Most: return LogInMostBuffer(desp->serial_id,desp->ssd_buf_tag); 365 | // case PORE: 366 | // return LogInPoreBuffer(desp->serial_id, desp->ssd_buf_tag, desp->ssd_buf_flag); 367 | // case PORE_PLUS: 368 | // return LogInPoreBuffer_plus(desp->serial_id, desp->ssd_buf_tag, desp->ssd_buf_flag); 369 | // case PORE_PLUS_V2: 370 | // return LogIn_poreplus_v2(desp->serial_id, desp->ssd_buf_tag, desp->ssd_buf_flag); 371 | case SAC: 372 | return LogIn_SAC(desp->serial_id, desp->ssd_buf_tag, desp->ssd_buf_flag); 373 | // case OLDPORE: 374 | // return LogIn_oldpore(desp->serial_id, desp->ssd_buf_tag, desp->ssd_buf_flag); 375 | case MOST: 376 | return LogIn_most(desp->serial_id, desp->ssd_buf_tag, desp->ssd_buf_flag); 377 | case MOST_CDC: 378 | return LogIn_most_cdc(desp->serial_id, desp->ssd_buf_tag, desp->ssd_buf_flag); 379 | } 380 | return -1; 381 | } 382 | /* 383 | * read--return the buf_id of buffer according to buf_tag 384 | */ 385 | 386 | void read_block(off_t offset, char *ssd_buffer) 387 | { 388 | 389 | if (NO_CACHE) 390 | { 391 | if (EMULATION) 392 | dev_simu_read(ssd_buffer, BLKSZ, offset); 393 | else 394 | dev_pread(smr_fd, ssd_buffer, BLKSZ, offset); 395 | 396 | msec_r_hdd = TimerInterval_MICRO(&tv_start, &tv_stop); 397 | STT->time_read_hdd += Mirco2Sec(msec_r_hdd); 398 | STT->load_hdd_blocks++; 399 | return; 400 | } 401 | 402 | // _TimerLap(&tv_cmstart); 403 | int found = 0; 404 | static SSDBufTag ssd_buf_tag; 405 | static SSDBufDesp *ssd_buf_hdr; 406 | 407 | ssd_buf_tag.offset = offset; 408 | if (DEBUG) 409 | printf("[INFO] read():-------offset=%lu\n", offset); 410 | 411 | ssd_buf_hdr = allocSSDBuf(ssd_buf_tag, &found, 0); 412 | 413 | IsHit = found; 414 | if (found) 415 | { 416 | dev_pread(cache_fd, ssd_buffer, BLKSZ, ssd_buf_hdr->ssd_buf_id * BLKSZ); 417 | msec_r_ssd = TimerInterval_MICRO(&tv_start, &tv_stop); 418 | 419 | STT->hitnum_r++; 420 | STT->time_read_ssd += Mirco2Sec(msec_r_ssd); 421 | STT->load_ssd_blocks++; 422 | } 423 | else 424 | { 425 | if (EMULATION) 426 | dev_simu_read(ssd_buffer, BLKSZ, offset); 427 | else 428 | dev_pread(smr_fd, ssd_buffer, BLKSZ, offset); 429 | 430 | msec_r_hdd = TimerInterval_MICRO(&tv_start, &tv_stop); 431 | STT->time_read_hdd += Mirco2Sec(msec_r_hdd); 432 | STT->load_hdd_blocks++; 433 | /* ----- Cost Model Reg------------- */ 434 | // if (isCallBack) 435 | // { 436 | // CM_T_rand_Reg(msec_r_hdd); 437 | // } 438 | // _TimerLap(&tv_cmstop); 439 | // microsecond_t miss_usetime = TimerInterval_MICRO(&tv_cmstart, &tv_cmstop); 440 | // CM_T_hitmiss_Reg(miss_usetime); 441 | /* ------------------ */ 442 | 443 | dev_pwrite(cache_fd, ssd_buffer, BLKSZ, ssd_buf_hdr->ssd_buf_id * BLKSZ); 444 | msec_w_ssd = TimerInterval_MICRO(&tv_start, &tv_stop); 445 | STT->time_write_ssd += Mirco2Sec(msec_w_ssd); 446 | STT->flush_ssd_blocks++; 447 | } 448 | 449 | _UNLOCK(&ssd_buf_hdr->lock); 450 | } 451 | 452 | /* 453 | * write--return the buf_id of buffer according to buf_tag 454 | */ 455 | void write_block(off_t offset, char *ssd_buffer) 456 | { 457 | if (NO_CACHE) 458 | { 459 | if (EMULATION) 460 | dev_simu_write(ssd_buffer, BLKSZ, offset); 461 | else 462 | dev_pwrite(smr_fd, ssd_buffer, BLKSZ, offset); 463 | 464 | msec_w_hdd = TimerInterval_MICRO(&tv_start, &tv_stop); 465 | STT->time_write_hdd += Mirco2Sec(msec_w_hdd); 466 | STT->flush_hdd_blocks++; 467 | return; 468 | } 469 | 470 | // _TimerLap(&tv_cmstart); 471 | int found; 472 | // int isCallBack; 473 | static SSDBufTag ssd_buf_tag; 474 | static SSDBufDesp *ssd_buf_hdr; 475 | 476 | ssd_buf_tag.offset = offset; 477 | ssd_buf_hdr = allocSSDBuf(ssd_buf_tag, &found, 1); 478 | 479 | //if(!found && isCallBack) 480 | // if (!found) 481 | // { 482 | // /* ----- Cost Model Reg------------- */ 483 | // // _TimerLap(&tv_cmstop); 484 | // // microsecond_t miss_usetime = TimerInterval_MICRO(&tv_cmstart, &tv_cmstop); 485 | // // CM_T_hitmiss_Reg(miss_usetime); 486 | // /* ------------------ */ 487 | // } 488 | 489 | IsHit = found; 490 | STT->hitnum_w += IsHit; 491 | 492 | dev_pwrite(cache_fd, ssd_buffer, BLKSZ, ssd_buf_hdr->ssd_buf_id * BLKSZ); 493 | msec_w_ssd = TimerInterval_MICRO(&tv_start, &tv_stop); 494 | STT->time_write_ssd += Mirco2Sec(msec_w_ssd); 495 | STT->flush_ssd_blocks++; 496 | 497 | _UNLOCK(&ssd_buf_hdr->lock); 498 | } 499 | 500 | /****************** 501 | **** Utilities***** 502 | *******************/ 503 | 504 | static int dev_pread(int fd, void *buf, size_t nbytes, off_t offset) 505 | { 506 | if (NO_REAL_DISK_IO) 507 | return nbytes; 508 | 509 | int r; 510 | _TimerLap(&tv_start); 511 | r = pread(fd, buf, nbytes, offset); 512 | _TimerLap(&tv_stop); 513 | if (r < 0) 514 | { 515 | printf("[ERROR] read():-------read from device: fd=%d, errorcode=%d, offset=%lu\n", fd, r, offset); 516 | exit(-1); 517 | } 518 | return r; 519 | } 520 | 521 | static int dev_pwrite(int fd, void *buf, size_t nbytes, off_t offset) 522 | { 523 | if (NO_REAL_DISK_IO) 524 | return nbytes; 525 | 526 | int w; 527 | _TimerLap(&tv_start); 528 | w = pwrite(fd, buf, nbytes, offset); 529 | _TimerLap(&tv_stop); 530 | if (w < 0) 531 | { 532 | printf("[ERROR] read():-------write to device: fd=%d, errorcode=%d, offset=%lu\n", fd, w, offset); 533 | exit(-1); 534 | } 535 | return w; 536 | } 537 | 538 | static int dev_simu_write(void *buf, size_t nbytes, off_t offset) 539 | { 540 | int w; 541 | _TimerLap(&tv_start); 542 | w = simu_smr_write(buf, nbytes, offset); 543 | _TimerLap(&tv_stop); 544 | return w; 545 | } 546 | 547 | static int dev_simu_read(void *buf, size_t nbytes, off_t offset) 548 | { 549 | int r; 550 | _TimerLap(&tv_start); 551 | r = simu_smr_read(buf, nbytes, offset); 552 | _TimerLap(&tv_stop); 553 | return r; 554 | } 555 | 556 | static SSDBufDesp * 557 | pop_freebuf() 558 | { 559 | if (ssd_buf_desp_ctrl->first_freessd < 0) 560 | return NULL; 561 | SSDBufDesp *ssd_buf_hdr = &ssd_buf_desps[ssd_buf_desp_ctrl->first_freessd]; 562 | ssd_buf_desp_ctrl->first_freessd = ssd_buf_hdr->next_freessd; 563 | ssd_buf_hdr->next_freessd = -1; 564 | ssd_buf_desp_ctrl->n_usedssd++; 565 | return ssd_buf_hdr; 566 | } 567 | static int 568 | push_freebuf(SSDBufDesp *freeDesp) 569 | { 570 | freeDesp->next_freessd = ssd_buf_desp_ctrl->first_freessd; 571 | ssd_buf_desp_ctrl->first_freessd = freeDesp->serial_id; 572 | return ssd_buf_desp_ctrl->first_freessd; 573 | } 574 | 575 | void _LOCK(pthread_mutex_t *lock) 576 | { 577 | #ifdef MULTIUSER 578 | SHM_mutex_lock(lock); 579 | #endif // MULTIUSER 580 | } 581 | 582 | void _UNLOCK(pthread_mutex_t *lock) 583 | { 584 | #ifdef MULTIUSER 585 | SHM_mutex_unlock(lock); 586 | #endif // MULTIUSER 587 | } 588 | -------------------------------------------------------------------------------- /src/strategy/most_cdc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../statusDef.h" 4 | #include "../timerUtils.h" 5 | #include "sac.h" 6 | #include "../report.h" 7 | #include 8 | //#define random(x) (rand()%x) 9 | #define IsDirty(flag) ( (flag & SSD_BUF_DIRTY) != 0 ) 10 | #define IsClean(flag) ( (flag & SSD_BUF_DIRTY) == 0 ) 11 | 12 | #define EVICT_DITRY_GRAIN 64 // The grain of once dirty blocks eviction 13 | 14 | typedef struct CleanDespCtrl 15 | { 16 | blkcnt_t pagecnt_clean; 17 | blkcnt_t head,tail; 18 | pthread_mutex_t lock; 19 | } CleanDespCtrl; 20 | 21 | static blkcnt_t ZONEBLKSZ; 22 | 23 | static Dscptr_sac* GlobalDespArray; 24 | static ZoneCtrl_pual* ZoneCtrl_pualArray; 25 | static CleanDespCtrl CleanCtrl; 26 | 27 | static unsigned long* ZoneSortArray; /* The zone ID array sorted by weight(calculated customized). it is used to determine the open zones */ 28 | static int NonEmptyZoneCnt = 0; 29 | static unsigned long* OpenZoneSet; /* The decided open zones in current period, which chosed by both the weight-sorted array and the access threshold. */ 30 | static int OpenZoneCnt; /* It represent the number of open zones and the first number elements in 'ZoneSortArray' is the open zones ID */ 31 | 32 | static long CycleID; 33 | extern long Cycle_Length; /* Which defines the upper limit of the block amount of selected OpenZone and of Evicted blocks. */ 34 | static long Cycle_Progress; /* Current times to evict clean/dirty block in a period lenth */ 35 | static long StampGlobal; /* Current io sequenced number in a period lenth, used to distinct the degree of heat among zones */ 36 | #define stamp(desp) (desp->stamp = StampGlobal ++) 37 | 38 | 39 | static void add2ArrayHead(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual); 40 | static void move2ArrayHead(Dscptr_sac* desp,ZoneCtrl_pual* ZoneCtrl_pual); 41 | 42 | static int start_new_cycle(); 43 | 44 | static void unloadfromZone(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual); 45 | static void clearDesp(Dscptr_sac* desp); 46 | static void hit(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual); 47 | static void add2CleanArrayHead(Dscptr_sac* desp); 48 | static void unloadfromCleanArray(Dscptr_sac* desp); 49 | static void move2CleanArrayHead(Dscptr_sac* desp); 50 | 51 | /* 52 | Out of Date(OOD) 53 | Alpha: We call those blocks(drt or cln) which are already out of the recent history window as Out of Date (OOD), 54 | and 'think' of they are won't be reused. All the blocks stamp less than 'OOD stamp' are 'OOD blocks'. 55 | 56 | Here we treat the last 80% of accesses are popular and the rest of 20%s are OOD. 57 | The OOD will be used to calculate the representation of Recall Ratio. 58 | */ 59 | static long OODstamp; // = StampGlobal - (long)(NBLOCK_SSD_CACHE * 0.8) 60 | struct blk_cm_info 61 | { 62 | int num_OODblks; 63 | int num_totalblks; 64 | }; 65 | 66 | 67 | 68 | /** SAC**/ 69 | static double redefineOpenZones(); 70 | static int get_FrozenOpZone_Seq(); 71 | 72 | typedef enum EvictPhrase_t 73 | { 74 | EP_Clean, 75 | EP_Dirty, 76 | EP_Reset 77 | } EvictPhrase_t; 78 | static EvictPhrase_t WhoEvict_Now, WhoEvict_Before; // Used to mark which type (r/w) of blocks should be evict in the [alpha] costmodel. (-1,clean), (1, dirty), (0, unknown) 79 | static int NumEvict_thistime_apprx = 5000; 80 | 81 | /** Cost Model(alpha) **/ 82 | struct COSTMODEL_Alpha 83 | { 84 | microsecond_t Lat_SMR_read; 85 | microsecond_t (*FX_WA) (int blkcnt); 86 | 87 | double (*Cost_Dirty) (struct blk_cm_info * dirty, int num); 88 | double (*Cost_Clean) (struct blk_cm_info clean); 89 | }; 90 | static microsecond_t costmodel_fx_wa(int blkcnt); 91 | static double costmodel_evaDirty_alpha(struct blk_cm_info * dirty, int num); 92 | static double costmodel_evaClean_alpha(struct blk_cm_info clean); 93 | static struct COSTMODEL_Alpha CM_Alpha = { 94 | .Lat_SMR_read = 14000, //14ms per read 95 | .FX_WA = costmodel_fx_wa, 96 | .Cost_Dirty = costmodel_evaDirty_alpha, 97 | .Cost_Clean = costmodel_evaClean_alpha, 98 | }; 99 | static EvictPhrase_t run_cm_alpha(); 100 | 101 | 102 | static unsigned long 103 | getZoneNum(size_t offset) 104 | { 105 | return offset / ZONESZ; 106 | } 107 | 108 | /* Process Function */ 109 | int 110 | Init_most_cdc() 111 | { 112 | ZONEBLKSZ = ZONESZ / BLKSZ; 113 | 114 | CycleID = StampGlobal = Cycle_Progress = OODstamp = 0; 115 | GlobalDespArray = (Dscptr_sac*) malloc(sizeof(Dscptr_sac) * NBLOCK_SSD_CACHE); 116 | ZoneCtrl_pualArray = (ZoneCtrl_pual*) malloc(sizeof(ZoneCtrl_pual) * NZONES); 117 | 118 | NonEmptyZoneCnt = OpenZoneCnt = 0; 119 | ZoneSortArray = (unsigned long*)malloc(sizeof(unsigned long) * NZONES); 120 | OpenZoneSet = (unsigned long*)malloc(sizeof(unsigned long) * NZONES); 121 | int i = 0; 122 | while(i < NBLOCK_SSD_CACHE) 123 | { 124 | Dscptr_sac* desp = GlobalDespArray + i; 125 | desp->serial_id = i; 126 | desp->ssd_buf_tag.offset = -1; 127 | desp->next = desp->pre = -1; 128 | desp->stamp = 0; 129 | desp->flag = 0; 130 | desp->zoneId = -1; 131 | i++; 132 | } 133 | i = 0; 134 | while(i < NZONES) 135 | { 136 | ZoneCtrl_pual* ctrl = ZoneCtrl_pualArray + i; 137 | ctrl->zoneId = i; 138 | ctrl->pagecnt_dirty = 0; 139 | ctrl->head = ctrl->tail = -1; 140 | ctrl->OOD_num = 0; 141 | ZoneSortArray[i] = 0; 142 | i++; 143 | } 144 | CleanCtrl.pagecnt_clean = 0; 145 | CleanCtrl.head = CleanCtrl.tail = -1; 146 | 147 | WhoEvict_Now = WhoEvict_Before = EP_Reset; 148 | return 0; 149 | } 150 | 151 | int 152 | LogIn_most_cdc(long despId, SSDBufTag tag, unsigned flag) 153 | { 154 | /* activate the decriptor */ 155 | Dscptr_sac* myDesp = GlobalDespArray + despId; 156 | unsigned long myZoneId = getZoneNum(tag.offset); 157 | ZoneCtrl_pual* myZone = ZoneCtrl_pualArray + myZoneId; 158 | myDesp->zoneId = myZoneId; 159 | myDesp->ssd_buf_tag = tag; 160 | myDesp->flag |= flag; 161 | 162 | /* add into chain */ 163 | stamp(myDesp); 164 | 165 | if(IsDirty(flag)) 166 | { 167 | /* add into Zone LRU as it's dirty tag */ 168 | add2ArrayHead(myDesp, myZone); 169 | myZone->pagecnt_dirty++; 170 | //myZone->score ++ ; 171 | } 172 | else 173 | { 174 | /* add into Global Clean LRU as it's clean tag */ 175 | add2CleanArrayHead(myDesp); 176 | CleanCtrl.pagecnt_clean++; 177 | } 178 | 179 | return 1; 180 | } 181 | 182 | int 183 | Hit_most_cdc(long despId, unsigned flag) 184 | { 185 | Dscptr_sac* myDesp = GlobalDespArray + despId; 186 | ZoneCtrl_pual* myZone = ZoneCtrl_pualArray + getZoneNum(myDesp->ssd_buf_tag.offset); 187 | 188 | if (IsClean(myDesp->flag) && IsDirty(flag)) 189 | { 190 | /* clean --> dirty */ 191 | unloadfromCleanArray(myDesp); 192 | add2ArrayHead(myDesp,myZone); 193 | myZone->pagecnt_dirty++; 194 | CleanCtrl.pagecnt_clean--; 195 | hit(myDesp,myZone); 196 | } 197 | else if (IsClean(myDesp->flag) && IsClean(flag)) 198 | { 199 | /* clean --> clean */ 200 | move2CleanArrayHead(myDesp); 201 | } 202 | else 203 | { 204 | /* dirty hit again*/ 205 | move2ArrayHead(myDesp,myZone); 206 | hit(myDesp,myZone); 207 | } 208 | stamp(myDesp); 209 | myDesp->flag |= flag; 210 | 211 | return 0; 212 | } 213 | 214 | static int 215 | start_new_cycle() 216 | { 217 | CycleID++; 218 | redefineOpenZones(); 219 | 220 | printf("-------------New Cycle!-----------\n"); 221 | printf("Cycle ID [%ld], Non-Empty Zone_Cnt=%d, OpenZones_cnt=%d, CleanBlks=%ld(%0.2lf)\n",CycleID, NonEmptyZoneCnt, OpenZoneCnt,CleanCtrl.pagecnt_clean, (double)CleanCtrl.pagecnt_clean/NBLOCK_SSD_CACHE); 222 | 223 | return 0; 224 | } 225 | 226 | /** \brief 227 | */ 228 | int 229 | LogOut_most_cdc(long * out_despid_array, int max_n_batch, enum_t_vict suggest_type) 230 | { 231 | static int CurEvictZoneSeq; 232 | static int Num_evict_clean_cycle = 0, Num_evict_dirty_cycle = 0; 233 | int evict_cnt = 0; 234 | 235 | ZoneCtrl_pual* evictZone; 236 | 237 | if(suggest_type == ENUM_B_Clean) 238 | { 239 | if(CleanCtrl.pagecnt_clean == 0 || WhoEvict_Now == EP_Dirty) // Consistency judgment 240 | sac_error_exit("Illegal to evict CLEAN cache."); 241 | 242 | if(WhoEvict_Now == EP_Reset) 243 | { 244 | WhoEvict_Now = WhoEvict_Before = EP_Clean; 245 | } 246 | goto FLAG_EVICT_CLEAN; 247 | } 248 | else if(suggest_type == ENUM_B_Dirty) 249 | { 250 | if(STT->incache_n_dirty == 0 || WhoEvict_Now == EP_Clean ) // Consistency judgment 251 | sac_error_exit("Illegal to evict DIRTY cache."); 252 | 253 | 254 | if(WhoEvict_Now == EP_Reset){ 255 | start_new_cycle(); 256 | WhoEvict_Now = WhoEvict_Before = EP_Dirty; 257 | } 258 | goto FLAG_EVICT_DIRTYZONE; 259 | } 260 | else if(suggest_type == ENUM_B_Any) 261 | { 262 | /* Here we use the Cost Model as the default strategy */ 263 | if(WhoEvict_Now == EP_Reset) 264 | { // unknown. So the costmodel[alpha] runs! 265 | WhoEvict_Now = WhoEvict_Before = run_cm_alpha(); 266 | } 267 | 268 | if(WhoEvict_Now == EP_Clean) 269 | { // clean 270 | WhoEvict_Now = EP_Reset; 271 | goto FLAG_EVICT_CLEAN; 272 | } 273 | else if(WhoEvict_Now == EP_Dirty) 274 | { //dirty 275 | WhoEvict_Now = EP_Reset; 276 | goto FLAG_EVICT_DIRTYZONE; 277 | } 278 | } 279 | else 280 | sac_error_exit("SAC catched an unsupported eviction type."); 281 | 282 | FLAG_EVICT_CLEAN: 283 | while(evict_cnt < EVICT_DITRY_GRAIN && CleanCtrl.pagecnt_clean > 0) 284 | { 285 | Dscptr_sac * cleanDesp = GlobalDespArray + CleanCtrl.tail; 286 | out_despid_array[evict_cnt] = cleanDesp->serial_id; 287 | unloadfromCleanArray(cleanDesp); 288 | clearDesp(cleanDesp); 289 | 290 | Num_evict_clean_cycle ++; 291 | CleanCtrl.pagecnt_clean --; 292 | evict_cnt ++; 293 | } 294 | 295 | if(CleanCtrl.pagecnt_clean == 0 || (Num_evict_clean_cycle >= NumEvict_thistime_apprx)){ 296 | Num_evict_clean_cycle = 0; 297 | WhoEvict_Now = EP_Reset; 298 | } 299 | return evict_cnt; 300 | 301 | FLAG_EVICT_DIRTYZONE: 302 | if((CurEvictZoneSeq = get_FrozenOpZone_Seq()) < 0) 303 | sac_error_exit("FLAG_EVICT_DIRTYZONE error"); 304 | evictZone = ZoneCtrl_pualArray + OpenZoneSet[CurEvictZoneSeq]; 305 | 306 | while(evict_cnt < EVICT_DITRY_GRAIN && evictZone->pagecnt_dirty > 0) 307 | { 308 | Dscptr_sac* frozenDesp = GlobalDespArray + evictZone->tail; 309 | 310 | unloadfromZone(frozenDesp,evictZone); 311 | out_despid_array[evict_cnt] = frozenDesp->serial_id; 312 | 313 | Cycle_Progress ++; 314 | evictZone->pagecnt_dirty--; 315 | Num_evict_dirty_cycle++; 316 | 317 | clearDesp(frozenDesp); 318 | evict_cnt++; 319 | } 320 | 321 | /* If end the dirty eviction */ 322 | if(Cycle_Progress >= Cycle_Length || (CurEvictZoneSeq = get_FrozenOpZone_Seq()) < 0){ 323 | /* End and set the eviction type to *Unknown*. */ 324 | printf(">> Output of last Cycle: clean:%d, dirty:%d\n",Num_evict_clean_cycle,Num_evict_dirty_cycle); 325 | 326 | Num_evict_dirty_cycle = 0; 327 | Cycle_Progress = 0; 328 | WhoEvict_Now = EP_Reset; 329 | } 330 | 331 | //printf("pore+V2: batch flush dirty cnt [%d] from zone[%lu]\n", j,evictZone->zoneId); 332 | 333 | // printf("SCORE REPORT: zone id[%d], score[%lu]\n", evictZone->zoneId, evictZone->score); 334 | return evict_cnt; 335 | } 336 | 337 | static EvictPhrase_t run_cm_alpha() 338 | { 339 | if(STT->incache_n_dirty == 0 || STT->incache_n_clean == 0) 340 | sac_error_exit("Illegal to run CostModel:alpha"); 341 | 342 | /* Get number of dirty OODs. NOTICE! Have to get the dirty first and then the clean, the order cannot be reverted.*/ 343 | int zoneid; 344 | if((zoneid = get_FrozenOpZone_Seq()) < 0) 345 | redefineOpenZones(); 346 | 347 | ZoneCtrl_pual* evictZone = ZoneCtrl_pualArray + OpenZoneSet[zoneid]; 348 | Dscptr_sac* frozenDesp = GlobalDespArray + evictZone->tail; 349 | unsigned long stamp_dirty = frozenDesp->stamp; 350 | 351 | /* Get number of clean OODs. NOTICE! Have to get the dirty first and then the clean, the order cannot be reverted. */ 352 | Dscptr_sac* cleandesp = GlobalDespArray + CleanCtrl.tail; 353 | unsigned long stamp_clean = cleandesp->stamp; 354 | 355 | 356 | printf(">>>[NEWCYCLE] timestamp C:%lu vs. D:%lu<<<\n", stamp_clean, stamp_dirty); 357 | printf(">>>[NEWCYCLE] Cached Number C:%lu vs. D:%lu<<<\n", STT->incache_n_clean, STT->incache_n_dirty); 358 | 359 | /* Compare. */ 360 | 361 | if(stamp_clean < stamp_dirty){ 362 | printf("~CLEAN\n\n"); 363 | return EP_Clean; 364 | } 365 | else{ 366 | printf("~DIRTY\n\n"); 367 | return EP_Dirty; 368 | } 369 | } 370 | 371 | 372 | /**************** 373 | ** Utilities **** 374 | *****************/ 375 | /* Utilities for Dirty descriptors Array in each Zone*/ 376 | 377 | static void 378 | hit(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual) 379 | { 380 | //ZoneCtrl_pual->heat++; 381 | //ZoneCtrl_pual->score -= (double) 1 / (1 << desp->heat); 382 | } 383 | 384 | static void 385 | add2ArrayHead(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual) 386 | { 387 | if(ZoneCtrl_pual->head < 0) 388 | { 389 | //empty 390 | ZoneCtrl_pual->head = ZoneCtrl_pual->tail = desp->serial_id; 391 | } 392 | else 393 | { 394 | //unempty 395 | Dscptr_sac* headDesp = GlobalDespArray + ZoneCtrl_pual->head; 396 | desp->pre = -1; 397 | desp->next = ZoneCtrl_pual->head; 398 | headDesp->pre = desp->serial_id; 399 | ZoneCtrl_pual->head = desp->serial_id; 400 | } 401 | } 402 | 403 | static void 404 | unloadfromZone(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual) 405 | { 406 | if(desp->pre < 0) 407 | { 408 | ZoneCtrl_pual->head = desp->next; 409 | } 410 | else 411 | { 412 | GlobalDespArray[desp->pre].next = desp->next; 413 | } 414 | 415 | if(desp->next < 0) 416 | { 417 | ZoneCtrl_pual->tail = desp->pre; 418 | } 419 | else 420 | { 421 | GlobalDespArray[desp->next].pre = desp->pre; 422 | } 423 | desp->pre = desp->next = -1; 424 | } 425 | 426 | static void 427 | move2ArrayHead(Dscptr_sac* desp,ZoneCtrl_pual* ZoneCtrl_pual) 428 | { 429 | unloadfromZone(desp, ZoneCtrl_pual); 430 | add2ArrayHead(desp, ZoneCtrl_pual); 431 | } 432 | 433 | static void 434 | clearDesp(Dscptr_sac* desp) 435 | { 436 | desp->ssd_buf_tag.offset = -1; 437 | desp->next = desp->pre = -1; 438 | desp->stamp = 0; 439 | desp->flag &= ~(SSD_BUF_DIRTY | SSD_BUF_VALID); 440 | desp->zoneId = -1; 441 | } 442 | 443 | /* Utilities for Global Clean Descriptors Array */ 444 | static void 445 | add2CleanArrayHead(Dscptr_sac* desp) 446 | { 447 | if(CleanCtrl.head < 0) 448 | { 449 | //empty 450 | CleanCtrl.head = CleanCtrl.tail = desp->serial_id; 451 | } 452 | else 453 | { 454 | //unempty 455 | Dscptr_sac* headDesp = GlobalDespArray + CleanCtrl.head; 456 | desp->pre = -1; 457 | desp->next = CleanCtrl.head; 458 | headDesp->pre = desp->serial_id; 459 | CleanCtrl.head = desp->serial_id; 460 | } 461 | } 462 | 463 | static void 464 | unloadfromCleanArray(Dscptr_sac* desp) 465 | { 466 | if(desp->pre < 0) 467 | { 468 | CleanCtrl.head = desp->next; 469 | } 470 | else 471 | { 472 | GlobalDespArray[desp->pre].next = desp->next; 473 | } 474 | 475 | if(desp->next < 0) 476 | { 477 | CleanCtrl.tail = desp->pre; 478 | } 479 | else 480 | { 481 | GlobalDespArray[desp->next].pre = desp->pre; 482 | } 483 | desp->pre = desp->next = -1; 484 | } 485 | 486 | static void 487 | move2CleanArrayHead(Dscptr_sac* desp) 488 | { 489 | unloadfromCleanArray(desp); 490 | add2CleanArrayHead(desp); 491 | } 492 | 493 | /* Decision Method */ 494 | /** \brief 495 | * Quick-Sort method to sort the zones by score. 496 | NOTICE! 497 | If the gap between variable 'start' and 'end'is too long, it will PROBABLY cause call stack OVERFLOW! 498 | So this function need to modify for better. 499 | */ 500 | static void 501 | qsort_zone(long start, long end) 502 | { 503 | long i = start; 504 | long j = end; 505 | 506 | long S = ZoneSortArray[start]; 507 | ZoneCtrl_pual* curCtrl = ZoneCtrl_pualArray + S; 508 | int score = curCtrl->pagecnt_dirty; // curCtrl->OOD_num; 509 | while (i < j) 510 | { 511 | while (!(ZoneCtrl_pualArray[ZoneSortArray[j]].pagecnt_dirty > score) && i 512 | { 513 | j--; 514 | } 515 | ZoneSortArray[i] = ZoneSortArray[j]; 516 | 517 | while (!(ZoneCtrl_pualArray[ZoneSortArray[i]].pagecnt_dirty < score) && i 518 | { 519 | i++; 520 | } 521 | ZoneSortArray[j] = ZoneSortArray[i]; 522 | } 523 | 524 | ZoneSortArray[i] = S; 525 | if (i - 1 > start) 526 | qsort_zone(start, i - 1); 527 | if (j + 1 < end) 528 | qsort_zone(j + 1, end); 529 | } 530 | 531 | 532 | /* 533 | extract the non-empty zones and record them into the ZoneSortArray 534 | */ 535 | static long 536 | extractNonEmptyZoneId() 537 | { 538 | int zoneId = 0, cnt = 0; 539 | while(zoneId < NZONES) 540 | { 541 | ZoneCtrl_pual* zone = ZoneCtrl_pualArray + zoneId; 542 | if(zone->pagecnt_dirty > 0) 543 | { 544 | ZoneSortArray[cnt] = zoneId; 545 | cnt++; 546 | } 547 | zoneId++; 548 | 549 | if(zone->activate_after_n_cycles > 0) 550 | zone->activate_after_n_cycles --; 551 | } 552 | return cnt; 553 | } 554 | 555 | static void 556 | pause_and_score() 557 | { 558 | /* For simplicity, searching all the zones of SMR, 559 | actually it's only needed to search the zones which had been cached. 560 | But it doesn't matter because of only 200~500K meta data of zones in memory for searching, it's not a big number. 561 | */ 562 | /* Score all zones. */ 563 | // ZoneCtrl_pual* izone; 564 | // Dscptr_sac* desp; 565 | // blkcnt_t n = 0; 566 | 567 | } 568 | 569 | 570 | static double redefineOpenZones() 571 | { 572 | double cost_ret = 0; 573 | NonEmptyZoneCnt = extractNonEmptyZoneId(); // >< #ugly way. 574 | if(NonEmptyZoneCnt == 0) 575 | sac_error_exit("There are no zone for open."); 576 | pause_and_score(); /** ARS (Actually Release Space) */ 577 | qsort_zone(0,NonEmptyZoneCnt-1); 578 | 579 | long max_n_zones = Cycle_Length / (ZONESZ / BLKSZ); 580 | if(max_n_zones == 0) 581 | max_n_zones = 1; // This is for Emulation on small traces, some of their fifo size are lower than a zone size. 582 | 583 | OpenZoneCnt = 0; 584 | long i = 0; 585 | while(OpenZoneCnt < max_n_zones && i < NonEmptyZoneCnt) 586 | { 587 | ZoneCtrl_pual* zone = ZoneCtrl_pualArray + ZoneSortArray[i]; 588 | 589 | /* According to the RULE 2, zones which have already be in PB cannot be choosed into this cycle. */ 590 | if(zone->activate_after_n_cycles == 0) 591 | { 592 | // zone->activate_after_n_cycles = 2; // Deactivate the zone for the next 2 cycles. 593 | OpenZoneSet[OpenZoneCnt] = zone->zoneId; 594 | OpenZoneCnt++; 595 | } 596 | else if(zone->activate_after_n_cycles > 0) 597 | sac_info("SAC FILTERS A REPEAT ZONE."); 598 | i++; 599 | } 600 | 601 | return cost_ret; 602 | } 603 | 604 | static int 605 | get_FrozenOpZone_Seq() 606 | { 607 | int seq = 0; 608 | blkcnt_t frozenSeq = -1; 609 | long frozenStamp = StampGlobal; 610 | while(seq < OpenZoneCnt) 611 | { 612 | ZoneCtrl_pual* ctrl = ZoneCtrl_pualArray + OpenZoneSet[seq]; 613 | if(ctrl->pagecnt_dirty <= 0) 614 | { 615 | seq ++; 616 | continue; 617 | } 618 | 619 | Dscptr_sac* tail = GlobalDespArray + ctrl->tail; 620 | if(tail->stamp < frozenStamp) 621 | { 622 | frozenStamp = tail->stamp; 623 | frozenSeq = seq; 624 | } 625 | seq ++; 626 | } 627 | 628 | return frozenSeq; // If return value <= 0, it means 1. here already has no any dirty block in the selected bands. 2. here has not started the cycle. 629 | } 630 | 631 | /************** 632 | * Cost Model * 633 | * ************/ 634 | 635 | static microsecond_t costmodel_fx_wa(int blkcnt){ 636 | microsecond_t lat_for_blkcnt = 728*blkcnt + 435833; // F(blkcnt) = RMW + k*blkcnt; 637 | return lat_for_blkcnt; 638 | } 639 | 640 | static double costmodel_evaDirty_alpha(struct blk_cm_info * dirty, int num){ 641 | if(num <= 0) 642 | return -1; 643 | double evaDirty = 0; 644 | int ood_num = 0; 645 | int i = 0; 646 | while(i < num){ 647 | evaDirty += (double)CM_Alpha.FX_WA(dirty[i].num_totalblks); 648 | ood_num += dirty[i].num_OODblks; 649 | i++; 650 | } 651 | 652 | return evaDirty / (ood_num + 1); 653 | } 654 | static double costmodel_evaClean_alpha(struct blk_cm_info clean){ 655 | double evaClean = \ 656 | ( \ 657 | (0 * clean.num_totalblks) + \ 658 | (clean.num_totalblks - clean.num_OODblks) * CM_Alpha.Lat_SMR_read \ 659 | ) / (clean.num_OODblks+1); 660 | return evaClean; 661 | } 662 | -------------------------------------------------------------------------------- /src/strategy/paul.c.most_+costmodel: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../timerUtils.h" 3 | #include "paul.h" 4 | #include "../statusDef.h" 5 | #include "../report.h" 6 | #include 7 | //#define random(x) (rand()%x) 8 | #define IsDirty(flag) ( (flag & SSD_BUF_DIRTY) != 0 ) 9 | #define IsClean(flag) ( (flag & SSD_BUF_DIRTY) == 0 ) 10 | 11 | #define EVICT_DITRY_GRAIN 64 // The grain of once dirty blocks eviction 12 | 13 | typedef struct CleanDespCtrl 14 | { 15 | blkcnt_t pagecnt_clean; 16 | blkcnt_t head,tail; 17 | pthread_mutex_t lock; 18 | } CleanDespCtrl; 19 | 20 | static blkcnt_t ZONEBLKSZ; 21 | 22 | static Dscptr_paul* GlobalDespArray; 23 | static ZoneCtrl_pual* ZoneCtrl_pualArray; 24 | static CleanDespCtrl CleanCtrl; 25 | 26 | static unsigned long* ZoneSortArray; /* The zone ID array sorted by weight(calculated customized). it is used to determine the open zones */ 27 | static int NonEmptyZoneCnt = 0; 28 | static unsigned long* OpenZoneSet; /* The decided open zones in current period, which chosed by both the weight-sorted array and the access threshold. */ 29 | static int OpenZoneCnt; /* It represent the number of open zones and the first number elements in 'ZoneSortArray' is the open zones ID */ 30 | 31 | static long CycleID; 32 | extern long Cycle_Length; /* Which defines the upper limit of the block amount of selected OpenZone and of Evicted blocks. */ 33 | static long Cycle_Progress; /* Current times to evict clean/dirty block in a period lenth */ 34 | static long StampGlobal; /* Current io sequenced number in a period lenth, used to distinct the degree of heat among zones */ 35 | #define stamp(desp) (desp->stamp = StampGlobal ++) 36 | 37 | 38 | static void add2ArrayHead(Dscptr_paul* desp, ZoneCtrl_pual* ZoneCtrl_pual); 39 | static void move2ArrayHead(Dscptr_paul* desp,ZoneCtrl_pual* ZoneCtrl_pual); 40 | 41 | static int start_new_cycle(); 42 | 43 | static void unloadfromZone(Dscptr_paul* desp, ZoneCtrl_pual* ZoneCtrl_pual); 44 | static void clearDesp(Dscptr_paul* desp); 45 | static void hit(Dscptr_paul* desp, ZoneCtrl_pual* ZoneCtrl_pual); 46 | static void add2CleanArrayHead(Dscptr_paul* desp); 47 | static void unloadfromCleanArray(Dscptr_paul* desp); 48 | static void move2CleanArrayHead(Dscptr_paul* desp); 49 | 50 | /* 51 | Out of Date(OOD) 52 | Alpha: We call those blocks(drt or cln) which are already out of the recent history window as Out of Date (OOD), 53 | and 'think' of they are won't be reused. All the blocks stamp less than 'OOD stamp' are 'OOD blocks'. 54 | 55 | Here we treat the last 80% of accesses are popular and the rest of 20%s are OOD. 56 | The OOD will be used to calculate the representation of Recall Ratio. 57 | */ 58 | static long OODstamp; // = StampGlobal - (long)(NBLOCK_SSD_CACHE * 0.8) 59 | struct blk_cm_info 60 | { 61 | int num_OODblks; 62 | int num_totalblks; 63 | }; 64 | 65 | 66 | 67 | /** PAUL**/ 68 | static double redefineOpenZones(); 69 | static int get_FrozenOpZone_Seq(); 70 | static int random_pick (float weight1, float weight2, float obey); 71 | static int restart_cm_alpha(); 72 | 73 | typedef enum EvictPhrase_t 74 | { 75 | EP_Clean, 76 | EP_Dirty, 77 | EP_Reset 78 | } EvictPhrase_t; 79 | static EvictPhrase_t WhoEvict_Now, WhoEvict_Before; // Used to mark which type (r/w) of blocks should be evict in the [alpha] costmodel. (-1,clean), (1, dirty), (0, unknown) 80 | static int NumEvict_thistime_apprx = 5000; 81 | 82 | /** Cost Model(alpha) **/ 83 | struct COSTMODEL_Alpha 84 | { 85 | microsecond_t Lat_SMR_read; 86 | microsecond_t (*FX_WA) (int blkcnt); 87 | 88 | double (*Cost_Dirty) (struct blk_cm_info * dirty, int num); 89 | double (*Cost_Clean) (struct blk_cm_info clean); 90 | }; 91 | static microsecond_t costmodel_fx_wa(int blkcnt); 92 | static double costmodel_evaDirty_alpha(struct blk_cm_info * dirty, int num); 93 | static double costmodel_evaClean_alpha(struct blk_cm_info clean); 94 | static struct COSTMODEL_Alpha CM_Alpha = { 95 | .Lat_SMR_read = 14000, //14ms per read 96 | .FX_WA = costmodel_fx_wa, 97 | .Cost_Dirty = costmodel_evaDirty_alpha, 98 | .Cost_Clean = costmodel_evaClean_alpha, 99 | }; 100 | static EvictPhrase_t run_cm_alpha(); 101 | 102 | 103 | static volatile unsigned long 104 | getZoneNum(size_t offset) 105 | { 106 | return offset / ZONESZ; 107 | } 108 | 109 | /* Process Function */ 110 | int 111 | Init_PUAL() 112 | { 113 | ZONEBLKSZ = ZONESZ / BLKSZ; 114 | 115 | CycleID = StampGlobal = Cycle_Progress = OODstamp = 0; 116 | GlobalDespArray = (Dscptr_paul*) malloc(sizeof(Dscptr_paul) * NBLOCK_SSD_CACHE); 117 | ZoneCtrl_pualArray = (ZoneCtrl_pual*) malloc(sizeof(ZoneCtrl_pual) * NZONES); 118 | 119 | NonEmptyZoneCnt = OpenZoneCnt = 0; 120 | ZoneSortArray = (unsigned long*)malloc(sizeof(unsigned long) * NZONES); 121 | OpenZoneSet = (unsigned long*)malloc(sizeof(unsigned long) * NZONES); 122 | int i = 0; 123 | while(i < NBLOCK_SSD_CACHE) 124 | { 125 | Dscptr_paul* desp = GlobalDespArray + i; 126 | desp->serial_id = i; 127 | desp->ssd_buf_tag.offset = -1; 128 | desp->next = desp->pre = -1; 129 | desp->stamp = 0; 130 | desp->flag = 0; 131 | desp->zoneId = -1; 132 | i++; 133 | } 134 | i = 0; 135 | while(i < NZONES) 136 | { 137 | ZoneCtrl_pual* ctrl = ZoneCtrl_pualArray + i; 138 | ctrl->zoneId = i; 139 | ctrl->pagecnt_dirty = 0; 140 | ctrl->head = ctrl->tail = -1; 141 | ctrl->OOD_num = 0; 142 | ZoneSortArray[i] = 0; 143 | i++; 144 | } 145 | CleanCtrl.pagecnt_clean = 0; 146 | CleanCtrl.head = CleanCtrl.tail = -1; 147 | 148 | WhoEvict_Now = WhoEvict_Before = EP_Reset; 149 | return 0; 150 | } 151 | 152 | int 153 | LogIn_PAUL(long despId, SSDBufTag tag, unsigned flag) 154 | { 155 | /* activate the decriptor */ 156 | Dscptr_paul* myDesp = GlobalDespArray + despId; 157 | unsigned long myZoneId = getZoneNum(tag.offset); 158 | ZoneCtrl_pual* myZone = ZoneCtrl_pualArray + myZoneId; 159 | myDesp->zoneId = myZoneId; 160 | myDesp->ssd_buf_tag = tag; 161 | myDesp->flag |= flag; 162 | 163 | /* add into chain */ 164 | stamp(myDesp); 165 | 166 | if(IsDirty(flag)) 167 | { 168 | /* add into Zone LRU as it's dirty tag */ 169 | add2ArrayHead(myDesp, myZone); 170 | myZone->pagecnt_dirty++; 171 | //myZone->score ++ ; 172 | } 173 | else 174 | { 175 | /* add into Global Clean LRU as it's clean tag */ 176 | add2CleanArrayHead(myDesp); 177 | CleanCtrl.pagecnt_clean++; 178 | } 179 | 180 | return 1; 181 | } 182 | 183 | int 184 | Hit_PAUL(long despId, unsigned flag) 185 | { 186 | Dscptr_paul* myDesp = GlobalDespArray + despId; 187 | ZoneCtrl_pual* myZone = ZoneCtrl_pualArray + getZoneNum(myDesp->ssd_buf_tag.offset); 188 | 189 | if (IsClean(myDesp->flag) && IsDirty(flag)) 190 | { 191 | /* clean --> dirty */ 192 | unloadfromCleanArray(myDesp); 193 | add2ArrayHead(myDesp,myZone); 194 | myZone->pagecnt_dirty++; 195 | CleanCtrl.pagecnt_clean--; 196 | hit(myDesp,myZone); 197 | } 198 | else if (IsClean(myDesp->flag) && IsClean(flag)) 199 | { 200 | /* clean --> clean */ 201 | move2CleanArrayHead(myDesp); 202 | } 203 | else 204 | { 205 | /* dirty hit again*/ 206 | move2ArrayHead(myDesp,myZone); 207 | hit(myDesp,myZone); 208 | } 209 | stamp(myDesp); 210 | myDesp->flag |= flag; 211 | 212 | return 0; 213 | } 214 | 215 | static int 216 | start_new_cycle() 217 | { 218 | CycleID++; 219 | redefineOpenZones(); 220 | 221 | printf("-------------New Cycle!-----------\n"); 222 | printf("Cycle ID [%ld], Non-Empty Zone_Cnt=%d, OpenZones_cnt=%d, CleanBlks=%ld(%0.2lf)\n",CycleID, NonEmptyZoneCnt, OpenZoneCnt,CleanCtrl.pagecnt_clean, (double)CleanCtrl.pagecnt_clean/NBLOCK_SSD_CACHE); 223 | 224 | return 0; 225 | } 226 | 227 | /** \brief 228 | */ 229 | int 230 | LogOut_PAUL(long * out_despid_array, int max_n_batch, enum_t_vict suggest_type) 231 | { 232 | OODstamp = StampGlobal - (NBLOCK_SSD_CACHE * 0.8); 233 | static int CurEvictZoneSeq; 234 | static int Num_evict_clean_cycle = 0, Num_evict_dirty_cycle = 0; 235 | int evict_cnt = 0; 236 | 237 | ZoneCtrl_pual* evictZone; 238 | 239 | if(suggest_type == ENUM_B_Clean) 240 | { 241 | if(CleanCtrl.pagecnt_clean == 0 || WhoEvict_Now == EP_Dirty) // Consistency judgment 242 | paul_error_exit("Illegal to evict CLEAN cache."); 243 | 244 | if(WhoEvict_Now == EP_Reset) 245 | { 246 | WhoEvict_Now = WhoEvict_Before = EP_Clean; 247 | } 248 | goto FLAG_EVICT_CLEAN; 249 | } 250 | else if(suggest_type == ENUM_B_Dirty) 251 | { 252 | if(STT->incache_n_dirty == 0 || WhoEvict_Now == EP_Clean ) // Consistency judgment 253 | paul_error_exit("Illegal to evict DIRTY cache."); 254 | 255 | 256 | if(WhoEvict_Now == EP_Reset){ 257 | start_new_cycle(); 258 | WhoEvict_Now = WhoEvict_Before = EP_Dirty; 259 | } 260 | goto FLAG_EVICT_DIRTYZONE; 261 | } 262 | else if(suggest_type == ENUM_B_Any) 263 | { 264 | /* Here we use the Cost Model as the default strategy */ 265 | if(WhoEvict_Now == EP_Reset) 266 | { // unknown. So the costmodel[alpha] runs! 267 | WhoEvict_Now = WhoEvict_Before = run_cm_alpha(); 268 | } 269 | 270 | if(WhoEvict_Now == EP_Clean) 271 | { // clean 272 | // WhoEvict_Now = EP_Reset; 273 | goto FLAG_EVICT_CLEAN; 274 | } 275 | else if(WhoEvict_Now == EP_Dirty) 276 | { //dirty 277 | // WhoEvict_Now = EP_Reset; 278 | goto FLAG_EVICT_DIRTYZONE; 279 | } 280 | } 281 | else 282 | paul_error_exit("PAUL catched an unsupported eviction type."); 283 | 284 | FLAG_EVICT_CLEAN: 285 | while(evict_cnt < EVICT_DITRY_GRAIN && CleanCtrl.pagecnt_clean > 0) 286 | { 287 | Dscptr_paul * cleanDesp = GlobalDespArray + CleanCtrl.tail; 288 | out_despid_array[evict_cnt] = cleanDesp->serial_id; 289 | unloadfromCleanArray(cleanDesp); 290 | clearDesp(cleanDesp); 291 | 292 | Num_evict_clean_cycle ++; 293 | CleanCtrl.pagecnt_clean --; 294 | evict_cnt ++; 295 | } 296 | 297 | if(CleanCtrl.pagecnt_clean == 0 || (Num_evict_clean_cycle >= NumEvict_thistime_apprx)){ 298 | printf(">> Output of last Cycle[%ld]: clean:%ld, dirty:%ld\n",CycleID,Num_evict_clean_cycle,Num_evict_dirty_cycle); 299 | Num_evict_clean_cycle = 0; 300 | WhoEvict_Now = EP_Reset; 301 | } 302 | return evict_cnt; 303 | 304 | FLAG_EVICT_DIRTYZONE: 305 | if((CurEvictZoneSeq = get_FrozenOpZone_Seq()) < 0) 306 | paul_error_exit("FLAG_EVICT_DIRTYZONE error"); 307 | evictZone = ZoneCtrl_pualArray + OpenZoneSet[CurEvictZoneSeq]; 308 | 309 | while(evict_cnt < EVICT_DITRY_GRAIN && evictZone->pagecnt_dirty > 0) 310 | { 311 | Dscptr_paul* frozenDesp = GlobalDespArray + evictZone->tail; 312 | 313 | unloadfromZone(frozenDesp,evictZone); 314 | out_despid_array[evict_cnt] = frozenDesp->serial_id; 315 | 316 | Cycle_Progress ++; 317 | evictZone->pagecnt_dirty--; 318 | Num_evict_dirty_cycle++; 319 | 320 | clearDesp(frozenDesp); 321 | evict_cnt++; 322 | } 323 | 324 | /* If end the dirty eviction */ 325 | if((CurEvictZoneSeq = get_FrozenOpZone_Seq()) < 0){ 326 | /* End and set the eviction type to *Unknown*. */ 327 | printf(">> Output of last Cycle[%ld]: clean:%ld, dirty:%ld\n",CycleID,Num_evict_clean_cycle,Num_evict_dirty_cycle); 328 | 329 | Num_evict_dirty_cycle = 0; 330 | Cycle_Progress = 0; 331 | WhoEvict_Now = EP_Reset; 332 | } 333 | 334 | //printf("pore+V2: batch flush dirty cnt [%d] from zone[%lu]\n", j,evictZone->zoneId); 335 | 336 | // printf("SCORE REPORT: zone id[%d], score[%lu]\n", evictZone->zoneId, evictZone->score); 337 | return evict_cnt; 338 | } 339 | 340 | static EvictPhrase_t run_cm_alpha() 341 | { 342 | CycleID++; 343 | if(STT->incache_n_dirty == 0 || STT->incache_n_clean == 0) 344 | paul_error_exit("Illegal to run CostModel:alpha"); 345 | struct blk_cm_info blk_cm_info_drt={0,0}, blk_cm_info_cln={0,0}; 346 | double cost_drt = -1, cost_cln = -1; 347 | 348 | /* Get number of dirty OODs. NOTICE! Have to get the dirty first and then the clean, the order cannot be reverted.*/ 349 | cost_drt = redefineOpenZones(); 350 | 351 | /* Get number of clean OODs. NOTICE! Have to get the dirty first and then the clean, the order cannot be reverted. */ 352 | blkcnt_t despId_cln = CleanCtrl.tail; 353 | Dscptr_paul* cleandesp; 354 | int cnt = 0; 355 | while(cnt < NumEvict_thistime_apprx && despId_cln >= 0) 356 | { 357 | cleandesp = GlobalDespArray + despId_cln; 358 | if(cleandesp->stamp < OODstamp){ 359 | blk_cm_info_cln.num_OODblks ++; 360 | } 361 | despId_cln = cleandesp->pre; 362 | cnt ++; 363 | } 364 | blk_cm_info_cln.num_totalblks = cnt; 365 | 366 | cost_cln = CM_Alpha.Cost_Clean(blk_cm_info_cln); 367 | 368 | printf(">>>[NEWCYCLE] CostModel(Alpha) C:%1f vs. D:%1f<<<\n", cost_cln, cost_drt); 369 | printf(">>>[NEWCYCLE] Cached Number C:%lu vs. D:%lu<<<\n", STT->incache_n_clean, STT->incache_n_dirty); 370 | 371 | /* Compare. */ 372 | if(cost_drt == -1 && cost_cln == -1) 373 | paul_error_exit("paul.c: alpha costmodel found the cost og dirty and clean are both -1"); 374 | 375 | if(cost_cln < cost_drt){ 376 | printf("~CLEAN\n\n"); 377 | return EP_Clean; 378 | } 379 | else{ 380 | printf("~DIRTY\n\n"); 381 | return EP_Dirty; 382 | } 383 | } 384 | 385 | 386 | /**************** 387 | ** Utilities **** 388 | *****************/ 389 | /* Utilities for Dirty descriptors Array in each Zone*/ 390 | 391 | static void 392 | hit(Dscptr_paul* desp, ZoneCtrl_pual* ZoneCtrl_pual) 393 | { 394 | //ZoneCtrl_pual->heat++; 395 | //ZoneCtrl_pual->score -= (double) 1 / (1 << desp->heat); 396 | } 397 | 398 | static void 399 | add2ArrayHead(Dscptr_paul* desp, ZoneCtrl_pual* ZoneCtrl_pual) 400 | { 401 | if(ZoneCtrl_pual->head < 0) 402 | { 403 | //empty 404 | ZoneCtrl_pual->head = ZoneCtrl_pual->tail = desp->serial_id; 405 | } 406 | else 407 | { 408 | //unempty 409 | Dscptr_paul* headDesp = GlobalDespArray + ZoneCtrl_pual->head; 410 | desp->pre = -1; 411 | desp->next = ZoneCtrl_pual->head; 412 | headDesp->pre = desp->serial_id; 413 | ZoneCtrl_pual->head = desp->serial_id; 414 | } 415 | } 416 | 417 | static void 418 | unloadfromZone(Dscptr_paul* desp, ZoneCtrl_pual* ZoneCtrl_pual) 419 | { 420 | if(desp->pre < 0) 421 | { 422 | ZoneCtrl_pual->head = desp->next; 423 | } 424 | else 425 | { 426 | GlobalDespArray[desp->pre].next = desp->next; 427 | } 428 | 429 | if(desp->next < 0) 430 | { 431 | ZoneCtrl_pual->tail = desp->pre; 432 | } 433 | else 434 | { 435 | GlobalDespArray[desp->next].pre = desp->pre; 436 | } 437 | desp->pre = desp->next = -1; 438 | } 439 | 440 | static void 441 | move2ArrayHead(Dscptr_paul* desp,ZoneCtrl_pual* ZoneCtrl_pual) 442 | { 443 | unloadfromZone(desp, ZoneCtrl_pual); 444 | add2ArrayHead(desp, ZoneCtrl_pual); 445 | } 446 | 447 | static void 448 | clearDesp(Dscptr_paul* desp) 449 | { 450 | desp->ssd_buf_tag.offset = -1; 451 | desp->next = desp->pre = -1; 452 | desp->stamp = 0; 453 | desp->flag &= ~(SSD_BUF_DIRTY | SSD_BUF_VALID); 454 | desp->zoneId = -1; 455 | } 456 | 457 | /* Utilities for Global Clean Descriptors Array */ 458 | static void 459 | add2CleanArrayHead(Dscptr_paul* desp) 460 | { 461 | if(CleanCtrl.head < 0) 462 | { 463 | //empty 464 | CleanCtrl.head = CleanCtrl.tail = desp->serial_id; 465 | } 466 | else 467 | { 468 | //unempty 469 | Dscptr_paul* headDesp = GlobalDespArray + CleanCtrl.head; 470 | desp->pre = -1; 471 | desp->next = CleanCtrl.head; 472 | headDesp->pre = desp->serial_id; 473 | CleanCtrl.head = desp->serial_id; 474 | } 475 | } 476 | 477 | static void 478 | unloadfromCleanArray(Dscptr_paul* desp) 479 | { 480 | if(desp->pre < 0) 481 | { 482 | CleanCtrl.head = desp->next; 483 | } 484 | else 485 | { 486 | GlobalDespArray[desp->pre].next = desp->next; 487 | } 488 | 489 | if(desp->next < 0) 490 | { 491 | CleanCtrl.tail = desp->pre; 492 | } 493 | else 494 | { 495 | GlobalDespArray[desp->next].pre = desp->pre; 496 | } 497 | desp->pre = desp->next = -1; 498 | } 499 | 500 | static void 501 | move2CleanArrayHead(Dscptr_paul* desp) 502 | { 503 | unloadfromCleanArray(desp); 504 | add2CleanArrayHead(desp); 505 | } 506 | 507 | /* Decision Method */ 508 | /** \brief 509 | * Quick-Sort method to sort the zones by score. 510 | NOTICE! 511 | If the gap between variable 'start' and 'end'is too long, it will PROBABLY cause call stack OVERFLOW! 512 | So this function need to modify for better. 513 | */ 514 | static void 515 | qsort_zone(long start, long end) 516 | { 517 | long i = start; 518 | long j = end; 519 | 520 | long S = ZoneSortArray[start]; 521 | ZoneCtrl_pual* curCtrl = ZoneCtrl_pualArray + S; 522 | unsigned long score = curCtrl->pagecnt_dirty; // curCtrl->OOD_num; 523 | while (i < j) 524 | { 525 | while (!(ZoneCtrl_pualArray[ZoneSortArray[j]].pagecnt_dirty > score) && i 526 | { 527 | j--; 528 | } 529 | ZoneSortArray[i] = ZoneSortArray[j]; 530 | 531 | while (!(ZoneCtrl_pualArray[ZoneSortArray[i]].pagecnt_dirty < score) && i 532 | { 533 | i++; 534 | } 535 | ZoneSortArray[j] = ZoneSortArray[i]; 536 | } 537 | 538 | ZoneSortArray[i] = S; 539 | if (i - 1 > start) 540 | qsort_zone(start, i - 1); 541 | if (j + 1 < end) 542 | qsort_zone(j + 1, end); 543 | } 544 | 545 | 546 | /* 547 | extract the non-empty zones and record them into the ZoneSortArray 548 | */ 549 | static long 550 | extractNonEmptyZoneId() 551 | { 552 | int zoneId = 0, cnt = 0; 553 | while(zoneId < NZONES) 554 | { 555 | ZoneCtrl_pual* zone = ZoneCtrl_pualArray + zoneId; 556 | if(zone->pagecnt_dirty > 0) 557 | { 558 | ZoneSortArray[cnt] = zoneId; 559 | cnt++; 560 | } 561 | zoneId++; 562 | 563 | if(zone->activate_after_n_cycles > 0) 564 | zone->activate_after_n_cycles --; 565 | } 566 | return cnt; 567 | } 568 | 569 | static void 570 | pause_and_score() 571 | { 572 | /* For simplicity, searching all the zones of SMR, 573 | actually it's only needed to search the zones which had been cached. 574 | But it doesn't matter because of only 200~500K meta data of zones in memory for searching, it's not a big number. 575 | */ 576 | /* Score all zones. */ 577 | ZoneCtrl_pual* izone; 578 | Dscptr_paul* desp; 579 | blkcnt_t n = 0; 580 | 581 | } 582 | 583 | 584 | static double redefineOpenZones() 585 | { 586 | double cost_ret = 0; 587 | struct blk_cm_info cm_drt[100]; 588 | NonEmptyZoneCnt = extractNonEmptyZoneId(); // >< #ugly way. 589 | if(NonEmptyZoneCnt == 0) 590 | paul_error_exit("There are no zone for open."); 591 | pause_and_score(); /** ARS (Actually Release Space) */ 592 | qsort_zone(0,NonEmptyZoneCnt-1); 593 | 594 | long max_n_zones = Cycle_Length / (ZONESZ / BLKSZ); 595 | if(max_n_zones == 0) 596 | max_n_zones = 1; // This is for Emulation on small traces, some of their fifo size are lower than a zone size. 597 | 598 | OpenZoneCnt = 0; 599 | long i = 0, k = 0; 600 | while(OpenZoneCnt < max_n_zones && i < NonEmptyZoneCnt) 601 | { 602 | ZoneCtrl_pual* zone = ZoneCtrl_pualArray + ZoneSortArray[i]; 603 | 604 | /* According to the RULE 2, zones which have already be in PB cannot be choosed into this cycle. */ 605 | if(zone->activate_after_n_cycles == 0) 606 | { 607 | // zone->activate_after_n_cycles = 2; // Deactivate the zone for the next 2 cycles. 608 | OpenZoneSet[OpenZoneCnt] = zone->zoneId; 609 | OpenZoneCnt++; 610 | 611 | cm_drt[k].num_OODblks = zone->OOD_num; 612 | cm_drt[k].num_totalblks = zone->pagecnt_dirty; 613 | k++; 614 | } 615 | else if(zone->activate_after_n_cycles > 0) 616 | //paul_info("PAUL FILTERS A REPEAT ZONE."); 617 | i++; 618 | printf("evict zoneid=%d, ood=%d\n",zone->zoneId,zone->OOD_num); 619 | } 620 | 621 | cost_ret = CM_Alpha.Cost_Dirty(cm_drt, OpenZoneCnt); 622 | return cost_ret; 623 | } 624 | 625 | static int 626 | get_FrozenOpZone_Seq() 627 | { 628 | int seq = 0; 629 | blkcnt_t frozenSeq = -1; 630 | long frozenStamp = StampGlobal; 631 | while(seq < OpenZoneCnt) 632 | { 633 | ZoneCtrl_pual* ctrl = ZoneCtrl_pualArray + OpenZoneSet[seq]; 634 | if(ctrl->pagecnt_dirty <= 0) 635 | { 636 | seq ++; 637 | continue; 638 | } 639 | 640 | Dscptr_paul* tail = GlobalDespArray + ctrl->tail; 641 | if(tail->stamp < frozenStamp) 642 | { 643 | frozenStamp = tail->stamp; 644 | frozenSeq = seq; 645 | } 646 | seq ++; 647 | } 648 | 649 | return frozenSeq; // If return value <= 0, it means 1. here already has no any dirty block in the selected bands. 2. here has not started the cycle. 650 | } 651 | 652 | /************** 653 | * Cost Model * 654 | * ************/ 655 | 656 | static microsecond_t costmodel_fx_wa(int blkcnt){ 657 | microsecond_t lat_for_blkcnt = 728*blkcnt + 435833; // F(blkcnt) = RMW + k*blkcnt; 658 | return lat_for_blkcnt; 659 | } 660 | 661 | static double costmodel_evaDirty_alpha(struct blk_cm_info * dirty, int num){ 662 | if(num <= 0) 663 | return -1; 664 | double evaDirty = 0; 665 | int ood_num = 0; 666 | int i = 0; 667 | while(i < num){ 668 | evaDirty += (double)CM_Alpha.FX_WA(dirty[i].num_totalblks); 669 | ood_num += dirty[i].num_OODblks; 670 | i++; 671 | } 672 | 673 | return evaDirty / (ood_num + 1); 674 | } 675 | static double costmodel_evaClean_alpha(struct blk_cm_info clean){ 676 | double evaClean = \ 677 | ( \ 678 | (0 * clean.num_totalblks) + \ 679 | (clean.num_totalblks - clean.num_OODblks) * CM_Alpha.Lat_SMR_read \ 680 | ) / (clean.num_OODblks+1); 681 | return evaClean; 682 | } 683 | -------------------------------------------------------------------------------- /src/strategy/sac.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../timerUtils.h" 3 | #include "sac.h" 4 | #include "../statusDef.h" 5 | #include "../report.h" 6 | #include 7 | //#define random(x) (rand()%x) 8 | #define IsDirty(flag) ( (flag & SSD_BUF_DIRTY) != 0 ) 9 | #define IsClean(flag) ( (flag & SSD_BUF_DIRTY) == 0 ) 10 | 11 | #define EVICT_DITRY_GRAIN 64 // The grain of once dirty blocks eviction 12 | 13 | typedef struct CleanDespCtrl 14 | { 15 | blkcnt_t pagecnt_clean; 16 | blkcnt_t head,tail; 17 | pthread_mutex_t lock; 18 | } CleanDespCtrl; 19 | 20 | static blkcnt_t ZONEBLKSZ; 21 | 22 | static Dscptr_sac* GlobalDespArray; 23 | static ZoneCtrl_pual* ZoneCtrl_pualArray; 24 | static CleanDespCtrl CleanCtrl; 25 | 26 | static unsigned long* ZoneSortArray; /* The zone ID array sorted by weight(calculated customized). it is used to determine the open zones */ 27 | static int NonEmptyZoneCnt = 0; 28 | static unsigned long* OpenZoneSet; /* The decided open zones in current period, which chosed by both the weight-sorted array and the access threshold. */ 29 | static int OpenZoneCnt; /* It represent the number of open zones and the first number elements in 'ZoneSortArray' is the open zones ID */ 30 | 31 | static long CycleID; 32 | extern long Cycle_Length; /* Which defines the upper limit of the block amount of selected OpenZone and of Evicted blocks. */ 33 | static long Cycle_Progress; /* Current times to evict clean/dirty block in a period lenth */ 34 | static long StampGlobal; /* Current io sequenced number in a period lenth, used to distinct the degree of heat among zones */ 35 | #define stamp(desp) (desp->stamp = StampGlobal ++) 36 | 37 | 38 | static void add2ArrayHead(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual); 39 | static void move2ArrayHead(Dscptr_sac* desp,ZoneCtrl_pual* ZoneCtrl_pual); 40 | 41 | static int start_new_cycle(); 42 | 43 | static void unloadfromZone(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual); 44 | static void clearDesp(Dscptr_sac* desp); 45 | static void hit(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual); 46 | static void add2CleanArrayHead(Dscptr_sac* desp); 47 | static void unloadfromCleanArray(Dscptr_sac* desp); 48 | static void move2CleanArrayHead(Dscptr_sac* desp); 49 | 50 | /* 51 | Out of Date(OOD) 52 | Alpha: We call those blocks(drt or cln) which are already out of the recent history window as Out of Date (OOD), 53 | and 'think' of they are won't be reused. All the blocks stamp less than 'OOD stamp' are 'OOD blocks'. 54 | 55 | Here we treat the last 80% of accesses are popular and the rest of 20%s are OOD. 56 | The OOD will be used to calculate the representation of Recall Ratio. 57 | */ 58 | static long OODstamp; // = StampGlobal - (long)(NBLOCK_SSD_CACHE * 0.8) 59 | struct blk_cm_info 60 | { 61 | int num_OODblks; 62 | int num_totalblks; 63 | }; 64 | 65 | 66 | 67 | /** SAC**/ 68 | static double redefineOpenZones(); 69 | static int get_FrozenOpZone_Seq(); 70 | 71 | typedef enum EvictPhrase_t 72 | { 73 | EP_Clean, 74 | EP_Dirty, 75 | EP_Reset 76 | } EvictPhrase_t; 77 | static EvictPhrase_t WhoEvict_Now, WhoEvict_Before; // Used to mark which type (r/w) of blocks should be evict in the [alpha] costmodel. (-1,clean), (1, dirty), (0, unknown) 78 | static int NumEvict_thistime_apprx = 100000; 79 | 80 | /** Cost Model(alpha) **/ 81 | struct COSTMODEL_Alpha 82 | { 83 | microsecond_t Lat_SMR_read; 84 | microsecond_t (*FX_WA) (int blkcnt); 85 | 86 | double (*Cost_Dirty) (struct blk_cm_info * dirty, int num); 87 | double (*Cost_Clean) (struct blk_cm_info clean); 88 | }; 89 | static microsecond_t costmodel_fx_wa(int blkcnt); 90 | static double costmodel_evaDirty_alpha(struct blk_cm_info * dirty, int num); 91 | static double costmodel_evaClean_alpha(struct blk_cm_info clean); 92 | static struct COSTMODEL_Alpha CM_Alpha = { 93 | .Lat_SMR_read = 14000, //14ms per read 94 | .FX_WA = costmodel_fx_wa, 95 | .Cost_Dirty = costmodel_evaDirty_alpha, 96 | .Cost_Clean = costmodel_evaClean_alpha, 97 | }; 98 | static EvictPhrase_t run_cm_alpha(); 99 | 100 | 101 | static unsigned long 102 | getZoneNum(size_t offset) 103 | { 104 | return offset / ZONESZ; 105 | } 106 | 107 | /* Process Function */ 108 | int 109 | Init_SAC() 110 | { 111 | ZONEBLKSZ = ZONESZ / BLKSZ; 112 | 113 | CycleID = StampGlobal = Cycle_Progress = OODstamp = 0; 114 | GlobalDespArray = (Dscptr_sac*) malloc(sizeof(Dscptr_sac) * NBLOCK_SSD_CACHE); 115 | ZoneCtrl_pualArray = (ZoneCtrl_pual*) malloc(sizeof(ZoneCtrl_pual) * NZONES); 116 | 117 | NonEmptyZoneCnt = OpenZoneCnt = 0; 118 | ZoneSortArray = (unsigned long*)malloc(sizeof(unsigned long) * NZONES); 119 | OpenZoneSet = (unsigned long*)malloc(sizeof(unsigned long) * NZONES); 120 | int i = 0; 121 | while(i < NBLOCK_SSD_CACHE) 122 | { 123 | Dscptr_sac* desp = GlobalDespArray + i; 124 | desp->serial_id = i; 125 | desp->ssd_buf_tag.offset = -1; 126 | desp->next = desp->pre = -1; 127 | desp->stamp = 0; 128 | desp->flag = 0; 129 | desp->zoneId = -1; 130 | i++; 131 | } 132 | i = 0; 133 | while(i < NZONES) 134 | { 135 | ZoneCtrl_pual* ctrl = ZoneCtrl_pualArray + i; 136 | ctrl->zoneId = i; 137 | ctrl->pagecnt_dirty = 0; 138 | ctrl->head = ctrl->tail = -1; 139 | ctrl->OOD_num = 0; 140 | ZoneSortArray[i] = 0; 141 | i++; 142 | } 143 | CleanCtrl.pagecnt_clean = 0; 144 | CleanCtrl.head = CleanCtrl.tail = -1; 145 | 146 | WhoEvict_Now = WhoEvict_Before = EP_Reset; 147 | return 0; 148 | } 149 | 150 | int 151 | LogIn_SAC(long despId, SSDBufTag tag, unsigned flag) 152 | { 153 | /* activate the decriptor */ 154 | Dscptr_sac* myDesp = GlobalDespArray + despId; 155 | unsigned long myZoneId = getZoneNum(tag.offset); 156 | ZoneCtrl_pual* myZone = ZoneCtrl_pualArray + myZoneId; 157 | myDesp->zoneId = myZoneId; 158 | myDesp->ssd_buf_tag = tag; 159 | myDesp->flag |= flag; 160 | 161 | /* add into chain */ 162 | stamp(myDesp); 163 | 164 | if(IsDirty(flag)) 165 | { 166 | /* add into Zone LRU as it's dirty tag */ 167 | add2ArrayHead(myDesp, myZone); 168 | myZone->pagecnt_dirty++; 169 | //myZone->score ++ ; 170 | } 171 | else 172 | { 173 | /* add into Global Clean LRU as it's clean tag */ 174 | add2CleanArrayHead(myDesp); 175 | CleanCtrl.pagecnt_clean++; 176 | } 177 | 178 | return 1; 179 | } 180 | 181 | int 182 | Hit_SAC(long despId, unsigned flag) 183 | { 184 | Dscptr_sac* myDesp = GlobalDespArray + despId; 185 | ZoneCtrl_pual* myZone = ZoneCtrl_pualArray + getZoneNum(myDesp->ssd_buf_tag.offset); 186 | 187 | if (IsClean(myDesp->flag) && IsDirty(flag)) 188 | { 189 | /* clean --> dirty */ 190 | unloadfromCleanArray(myDesp); 191 | add2ArrayHead(myDesp,myZone); 192 | myZone->pagecnt_dirty++; 193 | CleanCtrl.pagecnt_clean--; 194 | hit(myDesp,myZone); 195 | } 196 | else if (IsClean(myDesp->flag) && IsClean(flag)) 197 | { 198 | /* clean --> clean */ 199 | move2CleanArrayHead(myDesp); 200 | } 201 | else 202 | { 203 | /* dirty hit again*/ 204 | move2ArrayHead(myDesp,myZone); 205 | hit(myDesp,myZone); 206 | } 207 | stamp(myDesp); 208 | myDesp->flag |= flag; 209 | 210 | return 0; 211 | } 212 | 213 | static int 214 | start_new_cycle() 215 | { 216 | CycleID++; 217 | redefineOpenZones(); 218 | 219 | printf("-------------New Cycle!-----------\n"); 220 | printf("Cycle ID [%ld], Non-Empty Zone_Cnt=%d, OpenZones_cnt=%d, CleanBlks=%ld(%0.2lf)\n",CycleID, NonEmptyZoneCnt, OpenZoneCnt,CleanCtrl.pagecnt_clean, (double)CleanCtrl.pagecnt_clean/NBLOCK_SSD_CACHE); 221 | 222 | return 0; 223 | } 224 | 225 | /** \brief 226 | */ 227 | int 228 | LogOut_SAC(long * out_despid_array, int max_n_batch, enum_t_vict suggest_type) 229 | { 230 | static int CurEvictZoneSeq; 231 | static int Num_evict_clean_cycle = 0, Num_evict_dirty_cycle = 0; 232 | int evict_cnt = 0; 233 | 234 | ZoneCtrl_pual* evictZone; 235 | 236 | if(suggest_type == ENUM_B_Clean) 237 | { 238 | if(CleanCtrl.pagecnt_clean == 0 || WhoEvict_Now == EP_Dirty) // Consistency judgment 239 | sac_error_exit("Illegal to evict CLEAN cache."); 240 | 241 | if(WhoEvict_Now == EP_Reset) 242 | { 243 | WhoEvict_Now = WhoEvict_Before = EP_Clean; 244 | } 245 | goto FLAG_EVICT_CLEAN; 246 | } 247 | else if(suggest_type == ENUM_B_Dirty) 248 | { 249 | if(STT->incache_n_dirty == 0 || WhoEvict_Now == EP_Clean ) // Consistency judgment 250 | sac_error_exit("Illegal to evict DIRTY cache."); 251 | 252 | 253 | if(WhoEvict_Now == EP_Reset){ 254 | start_new_cycle(); 255 | WhoEvict_Now = WhoEvict_Before = EP_Dirty; 256 | } 257 | goto FLAG_EVICT_DIRTYZONE; 258 | } 259 | else if(suggest_type == ENUM_B_Any) 260 | { 261 | /* Here we use the Cost Model as the default strategy */ 262 | if(WhoEvict_Now == EP_Reset) 263 | { // unknown. So the costmodel[alpha] runs! 264 | WhoEvict_Now = WhoEvict_Before = run_cm_alpha(); 265 | } 266 | 267 | if(WhoEvict_Now == EP_Clean) 268 | { // clean 269 | goto FLAG_EVICT_CLEAN; 270 | } 271 | else if(WhoEvict_Now == EP_Dirty) 272 | { //dirty 273 | goto FLAG_EVICT_DIRTYZONE; 274 | } 275 | } 276 | else 277 | sac_error_exit("SAC catched an unsupported eviction type."); 278 | 279 | FLAG_EVICT_CLEAN: 280 | while(evict_cnt < EVICT_DITRY_GRAIN && CleanCtrl.pagecnt_clean > 0) 281 | { 282 | Dscptr_sac * cleanDesp = GlobalDespArray + CleanCtrl.tail; 283 | out_despid_array[evict_cnt] = cleanDesp->serial_id; 284 | unloadfromCleanArray(cleanDesp); 285 | clearDesp(cleanDesp); 286 | 287 | Num_evict_clean_cycle ++; 288 | CleanCtrl.pagecnt_clean --; 289 | evict_cnt ++; 290 | } 291 | 292 | if(CleanCtrl.pagecnt_clean == 0 || (Num_evict_clean_cycle >= NumEvict_thistime_apprx)){ 293 | Num_evict_clean_cycle = 0; 294 | WhoEvict_Now = EP_Reset; 295 | } 296 | return evict_cnt; 297 | 298 | FLAG_EVICT_DIRTYZONE: 299 | if((CurEvictZoneSeq = get_FrozenOpZone_Seq()) < 0) 300 | sac_error_exit("FLAG_EVICT_DIRTYZONE error"); 301 | evictZone = ZoneCtrl_pualArray + OpenZoneSet[CurEvictZoneSeq]; 302 | 303 | while(evict_cnt < EVICT_DITRY_GRAIN && evictZone->pagecnt_dirty > 0) 304 | { 305 | Dscptr_sac* frozenDesp = GlobalDespArray + evictZone->tail; 306 | 307 | unloadfromZone(frozenDesp,evictZone); 308 | out_despid_array[evict_cnt] = frozenDesp->serial_id; 309 | 310 | Cycle_Progress ++; 311 | evictZone->pagecnt_dirty--; 312 | Num_evict_dirty_cycle++; 313 | 314 | clearDesp(frozenDesp); 315 | evict_cnt++; 316 | } 317 | 318 | /* If end the dirty eviction */ 319 | //if(Cycle_Progress >= Cycle_Length || (CurEvictZoneSeq = get_FrozenOpZone_Seq()) < 0){ 320 | if((CurEvictZoneSeq = get_FrozenOpZone_Seq()) < 0){ 321 | 322 | /* End and set the eviction type to *Unknown*. */ 323 | printf(">> Output of last Cycle: clean:%d, dirty:%d\n",Num_evict_clean_cycle,Num_evict_dirty_cycle); 324 | 325 | Num_evict_dirty_cycle = 0; 326 | Cycle_Progress = 0; 327 | WhoEvict_Now = EP_Reset; 328 | } 329 | 330 | //printf("pore+V2: batch flush dirty cnt [%d] from zone[%lu]\n", j,evictZone->zoneId); 331 | 332 | // printf("SCORE REPORT: zone id[%d], score[%lu]\n", evictZone->zoneId, evictZone->score); 333 | return evict_cnt; 334 | } 335 | 336 | static EvictPhrase_t run_cm_alpha() 337 | { 338 | if(STT->incache_n_dirty == 0 || STT->incache_n_clean == 0) 339 | sac_error_exit("Illegal to run CostModel:alpha"); 340 | 341 | //struct blk_cm_info blk_cm_info_drt={0,0}; 342 | struct blk_cm_info blk_cm_info_cln={0,0}; 343 | double cost_drt = -1, cost_cln = -1; 344 | 345 | /* Get number of dirty OODs. NOTICE! Have to get the dirty first and then the clean, the order cannot be reverted.*/ 346 | cost_drt = redefineOpenZones(); 347 | 348 | /* Get number of clean OODs. NOTICE! Have to get the dirty first and then the clean, the order cannot be reverted. */ 349 | blkcnt_t despId_cln = CleanCtrl.tail; 350 | Dscptr_sac* cleandesp; 351 | int cnt = 0; 352 | while(cnt < NumEvict_thistime_apprx && despId_cln >= 0) 353 | { 354 | cleandesp = GlobalDespArray + despId_cln; 355 | if(cleandesp->stamp < OODstamp){ 356 | blk_cm_info_cln.num_OODblks ++; 357 | } 358 | despId_cln = cleandesp->pre; 359 | cnt ++; 360 | } 361 | blk_cm_info_cln.num_totalblks = cnt; 362 | 363 | cost_cln = CM_Alpha.Cost_Clean(blk_cm_info_cln); 364 | 365 | printf(">>>[NEWCYCLE] CostModel(Alpha) C:%1f vs. D:%1f<<<\n", cost_cln, cost_drt); 366 | printf(">>>[NEWCYCLE] Cached Number C:%lu vs. D:%lu<<<\n", STT->incache_n_clean, STT->incache_n_dirty); 367 | 368 | /* Compare. */ 369 | if(cost_drt == -1 && cost_cln == -1) 370 | sac_error_exit("sac.c: alpha costmodel found the cost og dirty and clean are both -1"); 371 | 372 | if(cost_cln < cost_drt){ 373 | printf("~CLEAN\n\n"); 374 | return EP_Clean; 375 | } 376 | else{ 377 | printf("~DIRTY\n\n"); 378 | return EP_Dirty; 379 | } 380 | } 381 | 382 | 383 | /**************** 384 | ** Utilities **** 385 | *****************/ 386 | /* Utilities for Dirty descriptors Array in each Zone*/ 387 | 388 | static void 389 | hit(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual) 390 | { 391 | //ZoneCtrl_pual->heat++; 392 | //ZoneCtrl_pual->score -= (double) 1 / (1 << desp->heat); 393 | } 394 | 395 | static void 396 | add2ArrayHead(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual) 397 | { 398 | if(ZoneCtrl_pual->head < 0) 399 | { 400 | //empty 401 | ZoneCtrl_pual->head = ZoneCtrl_pual->tail = desp->serial_id; 402 | } 403 | else 404 | { 405 | //unempty 406 | Dscptr_sac* headDesp = GlobalDespArray + ZoneCtrl_pual->head; 407 | desp->pre = -1; 408 | desp->next = ZoneCtrl_pual->head; 409 | headDesp->pre = desp->serial_id; 410 | ZoneCtrl_pual->head = desp->serial_id; 411 | } 412 | } 413 | 414 | static void 415 | unloadfromZone(Dscptr_sac* desp, ZoneCtrl_pual* ZoneCtrl_pual) 416 | { 417 | if(desp->pre < 0) 418 | { 419 | ZoneCtrl_pual->head = desp->next; 420 | } 421 | else 422 | { 423 | GlobalDespArray[desp->pre].next = desp->next; 424 | } 425 | 426 | if(desp->next < 0) 427 | { 428 | ZoneCtrl_pual->tail = desp->pre; 429 | } 430 | else 431 | { 432 | GlobalDespArray[desp->next].pre = desp->pre; 433 | } 434 | desp->pre = desp->next = -1; 435 | } 436 | 437 | static void 438 | move2ArrayHead(Dscptr_sac* desp,ZoneCtrl_pual* ZoneCtrl_pual) 439 | { 440 | unloadfromZone(desp, ZoneCtrl_pual); 441 | add2ArrayHead(desp, ZoneCtrl_pual); 442 | } 443 | 444 | static void 445 | clearDesp(Dscptr_sac* desp) 446 | { 447 | desp->ssd_buf_tag.offset = -1; 448 | desp->next = desp->pre = -1; 449 | desp->stamp = 0; 450 | desp->flag &= ~(SSD_BUF_DIRTY | SSD_BUF_VALID); 451 | desp->zoneId = -1; 452 | } 453 | 454 | /* Utilities for Global Clean Descriptors Array */ 455 | static void 456 | add2CleanArrayHead(Dscptr_sac* desp) 457 | { 458 | if(CleanCtrl.head < 0) 459 | { 460 | //empty 461 | CleanCtrl.head = CleanCtrl.tail = desp->serial_id; 462 | } 463 | else 464 | { 465 | //unempty 466 | Dscptr_sac* headDesp = GlobalDespArray + CleanCtrl.head; 467 | desp->pre = -1; 468 | desp->next = CleanCtrl.head; 469 | headDesp->pre = desp->serial_id; 470 | CleanCtrl.head = desp->serial_id; 471 | } 472 | } 473 | 474 | static void 475 | unloadfromCleanArray(Dscptr_sac* desp) 476 | { 477 | if(desp->pre < 0) 478 | { 479 | CleanCtrl.head = desp->next; 480 | } 481 | else 482 | { 483 | GlobalDespArray[desp->pre].next = desp->next; 484 | } 485 | 486 | if(desp->next < 0) 487 | { 488 | CleanCtrl.tail = desp->pre; 489 | } 490 | else 491 | { 492 | GlobalDespArray[desp->next].pre = desp->pre; 493 | } 494 | desp->pre = desp->next = -1; 495 | } 496 | 497 | static void 498 | move2CleanArrayHead(Dscptr_sac* desp) 499 | { 500 | unloadfromCleanArray(desp); 501 | add2CleanArrayHead(desp); 502 | } 503 | 504 | /* Decision Method */ 505 | /** \brief 506 | * Quick-Sort method to sort the zones by score. 507 | NOTICE! 508 | If the gap between variable 'start' and 'end'is too long, it will PROBABLY cause call stack OVERFLOW! 509 | So this function need to modify for better. 510 | */ 511 | static void 512 | qsort_zone(long start, long end) 513 | { 514 | long i = start; 515 | long j = end; 516 | 517 | long S = ZoneSortArray[start]; 518 | ZoneCtrl_pual* curCtrl = ZoneCtrl_pualArray + S; 519 | int score = curCtrl->OOD_num; 520 | while (i < j) 521 | { 522 | while (!(ZoneCtrl_pualArray[ZoneSortArray[j]].OOD_num > score) && i start) 537 | qsort_zone(start, i - 1); 538 | if (j + 1 < end) 539 | qsort_zone(j + 1, end); 540 | } 541 | 542 | /* 543 | extract the non-empty zones and record them into the ZoneSortArray 544 | */ 545 | static long 546 | extractNonEmptyZoneId() 547 | { 548 | int zoneId = 0, cnt = 0; 549 | while(zoneId < NZONES) 550 | { 551 | ZoneCtrl_pual* zone = ZoneCtrl_pualArray + zoneId; 552 | if(zone->pagecnt_dirty 553 | > 0) 554 | { 555 | ZoneSortArray[cnt] = zoneId; 556 | cnt++; 557 | } 558 | zoneId++; 559 | 560 | if(WhoEvict_Before == 1 && zone->activate_after_n_cycles > 0) 561 | zone->activate_after_n_cycles --; 562 | } 563 | return cnt; 564 | } 565 | 566 | static void 567 | pause_and_score() 568 | { 569 | /* For simplicity, searching all the zones of SMR, 570 | actually it's only needed to search the zones which had been cached. 571 | But it doesn't matter because of only 200~500K meta data of zones in memory for searching, it's not a big number. 572 | */ 573 | /* Score all zones. */ 574 | ZoneCtrl_pual* izone; 575 | Dscptr_sac* desp; 576 | blkcnt_t n = 0; 577 | 578 | OODstamp = StampGlobal - (long)(NBLOCK_SSD_CACHE * 0.8); 579 | while(n < NonEmptyZoneCnt) 580 | { 581 | izone = ZoneCtrl_pualArray + ZoneSortArray[n]; 582 | izone->OOD_num = 0; 583 | 584 | /* score each block of the non-empty zone */ 585 | 586 | blkcnt_t despId = izone->tail; 587 | while(despId >= 0) 588 | { 589 | desp = GlobalDespArray + despId; 590 | if(desp->stamp < OODstamp) 591 | izone->OOD_num ++; 592 | despId = desp->pre; 593 | } 594 | n++ ; 595 | } 596 | } 597 | 598 | 599 | static double redefineOpenZones() 600 | { 601 | double cost_ret = 0; 602 | struct blk_cm_info cm_drt[100]; 603 | NonEmptyZoneCnt = extractNonEmptyZoneId(); // >< #ugly way. 604 | if(NonEmptyZoneCnt == 0) 605 | sac_error_exit("There are no zone for open."); 606 | pause_and_score(); /** ARS (Actually Release Space) */ 607 | qsort_zone(0,NonEmptyZoneCnt-1); 608 | 609 | long max_n_zones = Cycle_Length / (ZONESZ / BLKSZ); 610 | if(max_n_zones == 0) 611 | max_n_zones = 1; // This is for Emulation on small traces, some of their fifo size are lower than a zone size. 612 | 613 | OpenZoneCnt = 0; 614 | long i = 0, k = 0; 615 | while(OpenZoneCnt < max_n_zones && i < NonEmptyZoneCnt) 616 | { 617 | ZoneCtrl_pual* zone = ZoneCtrl_pualArray + ZoneSortArray[i]; 618 | 619 | /* According to the RULE 2, zones which have already be in PB cannot be choosed into this cycle. */ 620 | if(zone->activate_after_n_cycles == 0) 621 | { 622 | zone->activate_after_n_cycles = 2; // Deactivate the zone for the next 2 cycles. 623 | OpenZoneSet[OpenZoneCnt] = zone->zoneId; 624 | OpenZoneCnt++; 625 | 626 | cm_drt[k].num_OODblks = zone->OOD_num; 627 | cm_drt[k].num_totalblks = zone->pagecnt_dirty; 628 | k++; 629 | } 630 | else if(zone->activate_after_n_cycles > 0) 631 | //sac_info("SAC FILTERS A REPEAT ZONE."); 632 | i++; 633 | } 634 | 635 | cost_ret = CM_Alpha.Cost_Dirty(cm_drt, OpenZoneCnt); 636 | return cost_ret; 637 | } 638 | 639 | static int 640 | get_FrozenOpZone_Seq() 641 | { 642 | int seq = 0; 643 | blkcnt_t frozenSeq = -1; 644 | long frozenStamp = StampGlobal; 645 | while(seq < OpenZoneCnt) 646 | { 647 | ZoneCtrl_pual* ctrl = ZoneCtrl_pualArray + OpenZoneSet[seq]; 648 | if(ctrl->pagecnt_dirty <= 0) 649 | { 650 | seq ++; 651 | continue; 652 | } 653 | 654 | Dscptr_sac* tail = GlobalDespArray + ctrl->tail; 655 | if(tail->stamp < frozenStamp) 656 | { 657 | frozenStamp = tail->stamp; 658 | frozenSeq = seq; 659 | } 660 | seq ++; 661 | } 662 | 663 | return frozenSeq; // If return value <= 0, it means 1. here already has no any dirty block in the selected bands. 2. here has not started the cycle. 664 | } 665 | 666 | /************** 667 | * Cost Model * 668 | * ************/ 669 | 670 | static microsecond_t costmodel_fx_wa(int blkcnt){ 671 | microsecond_t lat_for_blkcnt = 728*blkcnt + 435833; // F(blkcnt) = RMW + k*blkcnt; 672 | return lat_for_blkcnt; 673 | } 674 | 675 | static double costmodel_evaDirty_alpha(struct blk_cm_info * dirty, int num){ 676 | if(num <= 0) 677 | return -1; 678 | double evaDirty = 0; 679 | int ood_num = 0; 680 | int i = 0; 681 | while(i < num){ 682 | evaDirty += (double)CM_Alpha.FX_WA(dirty[i].num_totalblks); 683 | ood_num += dirty[i].num_OODblks; 684 | i++; 685 | } 686 | 687 | return evaDirty / (ood_num + 1); 688 | } 689 | static double costmodel_evaClean_alpha(struct blk_cm_info clean){ 690 | double evaClean = \ 691 | ( \ 692 | (0 * clean.num_totalblks) + \ 693 | (clean.num_totalblks - clean.num_OODblks) * CM_Alpha.Lat_SMR_read \ 694 | ) / (clean.num_OODblks+1); 695 | return evaClean; 696 | } 697 | --------------------------------------------------------------------------------