#define _NAND_DEV_C_ #include "nand_blk.h" #include "nand_dev.h" #define NAND_SCHEDULE_TIMEOUT (HZ >> 3) #define NFTL_SCHEDULE_TIMEOUT (HZ >> 2) #define NFTL_FLUSH_DATA_TIME 1 #define WEAR_LEVELING 1 static int dev_num = 0; unsigned long nand_active_time = 0; unsigned long nand_write_active_time = 0; /*static unsigned int ioctl_flush_request_cnt = 0;*/ /*static unsigned long last_ioctl_flush_time = 0;*/ struct nand_kobject *s_nand_kobj; struct _nand_info *p_nand_info = NULL; extern struct nand_blk_ops mytr; extern struct kobj_type ktype; extern struct _nand_partition *build_nand_partition(struct _nand_phy_partition *phy_partition); extern void add_nftl_blk_list(struct _nftl_blk *head, struct _nftl_blk *nftl_blk); extern struct _nftl_blk *del_last_nftl_blk(struct _nftl_blk *head); extern int add_nand_blktrans_dev(struct nand_blk_dev *dev); extern int del_nand_blktrans_dev(struct nand_blk_dev *dev); extern struct _nand_disk *get_disk_from_phy_partition(struct _nand_phy_partition *phy_partition); extern uint16 get_partitionNO(struct _nand_phy_partition *phy_partition); extern int nftl_exit(struct _nftl_blk *nftl_blk); extern struct _nftl_blk *get_nftl_need_read_claim(struct _nftl_blk *start_blk, uint32 utc); extern int read_reclaim(struct _nftl_blk *start_blk, struct _nftl_blk *nftl_blk, uchar *buf, uint32 utc); int _dev_nand_read(struct _nand_dev *nand_dev, __u32 start_sector, __u32 len, unsigned char *buf); int _dev_nand_write(struct _nand_dev *nand_dev, __u32 start_sector, __u32 len, unsigned char *buf); int _dev_nand_discard(struct _nand_dev *nand_dev, __u32 start_sector, __u32 len); int _dev_flush_write_cache(struct _nand_dev *nand_dev, __u32 num); int _dev_flush_sector_write_cache(struct _nand_dev *nand_dev, __u32 num); int dev_initialize(struct _nand_dev *nand_dev, struct _nftl_blk *nftl_blk, __u32 offset, __u32 size); int nand_flush(struct nand_blk_dev *dev); int add_nand(struct nand_blk_ops *tr, struct _nand_phy_partition *phy_partition); int remove_nand(struct nand_blk_ops *tr); unsigned int nand_read_reclaim(struct _nftl_blk *nftl_blk, unsigned char *buf, uint32 utc); int panic_write(struct _nftl_blk *nftl_blk, uint32 start_sector, uint32 sector_num, uchar *buf); int panic_read(struct _nftl_blk *nftl_blk, uint32 start_sector, uint32 sector_num, uchar *buf); /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int nand_thread(void *arg) { unsigned long time, utc, ret, time_val; struct nand_blk_ops *tr = (struct nand_blk_ops *)arg; unsigned int start_time = 4800; unsigned char *temp_buf = kmalloc(64 * 1024, GFP_KERNEL); time_val = NAND_SCHEDULE_TIMEOUT; while (!kthread_should_stop()) { if (start_time != 0) { start_time--; if (start_time < 3) { utc = get_seconds(); } goto nand_thread_exit; } time = jiffies; if (time_after(time, nand_active_time + HZ) != 0) { ret = nand_read_reclaim(tr->nftl_blk_head.nftl_blk_next, temp_buf, utc); if (ret == 1) { time_val = HZ << 5; /*32s*/ utc = get_seconds(); } else { time_val = NAND_SCHEDULE_TIMEOUT; } } nand_thread_exit: set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(time_val); } return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ static int nftl_thread(void *arg) { struct _nftl_blk *nftl_blk = arg; unsigned long time; int cache_nums; unsigned long swl_time = jiffies; int first_miss_swl = 0; int need_swl = 0; int swl_done = 0; while (!kthread_should_stop()) { mutex_lock(nftl_blk->blk_lock); cache_nums = nftl_get_zone_write_cache_nums(nftl_blk->nftl_zone); time = jiffies; if (cache_nums > 300) { if (time_after((unsigned long)time, (unsigned long)(nand_write_active_time + HZ)) != 0) { nftl_blk->flush_write_cache(nftl_blk, 32); } } else if (cache_nums > 200) { if (time_after((unsigned long)time, (unsigned long)(nand_write_active_time + HZ)) != 0) { nftl_blk->flush_write_cache(nftl_blk, 8); } } else if (cache_nums > 100) { if (time_after((unsigned long)time, (unsigned long)(nand_write_active_time + HZ)) != 0) { nftl_blk->flush_write_cache(nftl_blk, 4); } } else if (cache_nums > 50) { if (time_after((unsigned long)time, (unsigned long)(nand_write_active_time + HZ)) != 0) { nftl_blk->flush_write_cache(nftl_blk, 2); } } else { if (time_after((unsigned long)time, (unsigned long)(nand_write_active_time + HZ + HZ)) != 0) { nftl_blk->flush_write_cache(nftl_blk, 2); } } #if WEAR_LEVELING /** add static WL: * we do the static WL when nftl ops is idle over 4s; * if missing the chance over 64s, we will do it mandatorily!!! * if we have done the static WL successfully, the time interval * to next static WL must be over 64s. */ time = jiffies; if (swl_done) { if (time_after(time, swl_time + (HZ << 6))) { /*nand_dbg_err("[ND]swl: over time(64s) after last swl done\n");*/ need_swl = 1; /* next static WL is over 64s */ } } else if (time_after(time, (unsigned long)(nftl_blk->ops_time + (HZ << 2)))) { /*nand_dbg_err("[ND]swl: nftl ops is idle over 4s\n");*/ need_swl = 1; /* nftl ops is idle over 4s */ } else if (first_miss_swl) { if (time_after(time, swl_time + (HZ << 6))) { /*nand_dbg_err("[ND]swl: over time(64s) after missing swl\n");*/ need_swl = 1; /* next static WL is over 64s */ } } else { first_miss_swl = 1; swl_time = jiffies; /*nand_dbg_err("[ND]swl: miss swl set time\n");*/ } if (need_swl) { first_miss_swl = 0; need_swl = 0; swl_done = do_static_wear_leveling(nftl_blk->nftl_zone); if (!swl_done) { /*nand_dbg_err("[ND]swl: nftl_thread swl ok!\n");*/ swl_done = 1; swl_time = jiffies; /*nand_dbg_err("[ND]swl: done swl set time\n");*/ } else { /*nand_dbg_err("[ND]swl: nftl_thread swl fail(%i)!\n", swl_done);*/ swl_done = 0; } } #endif if (garbage_collect(nftl_blk->nftl_zone) != 0) { nand_dbg_err("nftl_thread garbage_collect error!\n"); } if (do_prio_gc(nftl_blk->nftl_zone) != 0) { nand_dbg_err("nftl_thread do_prio_gc error!\n"); } mutex_unlock(nftl_blk->blk_lock); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(NFTL_SCHEDULE_TIMEOUT); } nftl_blk->nftl_thread = (void *)NULL; return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ void add_nand_dev_list(struct _nand_dev *head, struct _nand_dev *nand_dev) { struct _nand_dev *p = head; nand_dev->nand_dev_next = NULL; while (p->nand_dev_next != NULL) { p = p->nand_dev_next; } p->nand_dev_next = nand_dev; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ struct _nand_dev *del_last_nand_dev(struct _nand_dev *head) { struct _nand_dev *nand_dev = NULL; struct _nand_dev *p = head; while (p->nand_dev_next != NULL) { nand_dev = p->nand_dev_next; if (nand_dev->nand_dev_next == NULL) { p->nand_dev_next = NULL; return nand_dev; } p = p->nand_dev_next; } return NULL; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int add_nand(struct nand_blk_ops *tr, struct _nand_phy_partition *phy_partition) { int i; __u32 cur_offset = 0; struct _nftl_blk *nftl_blk; struct _nand_dev *nand_dev; struct _nand_disk *disk; struct _nand_disk *head_disk; /*struct nand_kobject* nand_kobj;*/ uint16 PartitionNO; PartitionNO = get_partitionNO(phy_partition); nftl_blk = kmalloc(sizeof(struct _nftl_blk), GFP_KERNEL); if (!nftl_blk) { nand_dbg_err("init kmalloc fail 3!\n"); return 1; } nftl_blk->nand = build_nand_partition(phy_partition); if (nftl_initialize(nftl_blk, PartitionNO)) { nand_dbg_err("nftl_initialize failed\n"); return 1; } nftl_blk->blk_lock = kmalloc(sizeof(struct mutex), GFP_KERNEL); if (!nftl_blk->blk_lock) { nand_dbg_err("init kmalloc fail 2!\n"); return 1; } mutex_init(nftl_blk->blk_lock); nftl_blk->nftl_thread = kthread_run(nftl_thread, nftl_blk, "%sd", "nftl"); if (IS_ERR(nftl_blk->nftl_thread)) { nand_dbg_err("init kthread_run fail!\n"); return 1; } add_nftl_blk_list(&tr->nftl_blk_head, nftl_blk); s_nand_kobj = kzalloc(sizeof(struct nand_kobject), GFP_KERNEL); if (!s_nand_kobj) { nand_dbg_err("init kmalloc fail 1!\n"); return 1; } s_nand_kobj->nftl_blk = nftl_blk; if (kobject_init_and_add(&s_nand_kobj->kobj, &ktype, NULL, "nand_driver%d", PartitionNO) != 0) { nand_dbg_err("init nand sysfs fail!\n"); return 1; } disk = get_disk_from_phy_partition(phy_partition); for (i = 0; i < MAX_PART_COUNT_PER_FTL; i++) { /*nand_dbg_err("disk->name %s\n",(char *)(disk->name));*/ /*nand_dbg_err("disk->type %x\n",disk[i].type);*/ /*nand_dbg_err("disk->size %x\n",disk[i].size);*/ } head_disk = get_disk_from_phy_partition(phy_partition); for (i = 0; i < MAX_PART_COUNT_PER_FTL; i++) { disk = head_disk + i; if (disk->type == 0xffffffff) { break; } nand_dev = kmalloc(sizeof(struct _nand_dev), GFP_KERNEL); if (!nand_dev) { nand_dbg_err("init kmalloc fail!\n"); return 1; } add_nand_dev_list(&tr->nand_dev_head, nand_dev); nand_dev->nbd.nandr = &mytr; if (dev_initialize(nand_dev, nftl_blk, cur_offset, disk->size) != 0) { /*nand_dbg_err("dev_initialize failed\n");*/ return 1; } nand_dev->nbd.size = (unsigned int)nand_dev->size; nand_dev->nbd.priv = (void *)nand_dev; /* if(disk->size == 0xffffffff)*/ /* {*/ /* nand_dbg_err("user size1: %d!\n",nand_dev->nbd.size);*/ /* nand_dev->nbd.size += nand_dev->nbd.size >> 2;*/ /* nand_dbg_err("user size2: %d!\n",nand_dev->nbd.size);*/ /* }*/ memcpy(nand_dev->nbd.name, disk->name, strlen(disk->name) + 1); memcpy(nand_dev->name, disk->name, strlen(disk->name) + 1); nand_dbg_err("nand_dev add %s\n", nand_dev->name); if ((PartitionNO == 0) && (i == 0)) { dev_num = -1; } else { dev_num++; nand_dev->nbd.devnum = dev_num; if (add_nand_blktrans_dev(&nand_dev->nbd)) { nand_dbg_err("nftl add blk disk dev failed\n"); return 1; } } cur_offset += disk->size; } return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int remove_nand(struct nand_blk_ops *tr) { /* struct nand_kobject* nand_kobj;*/ struct _nftl_blk *nftl_blk; struct _nand_dev *nand_dev; nand_dbg_err("remove_nand\n"); nftl_blk = &tr->nftl_blk_head; nand_dev = &tr->nand_dev_head; nand_dev = del_last_nand_dev(&tr->nand_dev_head); while (nand_dev != NULL) { nand_flush(&nand_dev->nbd); del_nand_blktrans_dev(&nand_dev->nbd); kfree(nand_dev); nand_dev = del_last_nand_dev(&tr->nand_dev_head); } nftl_blk = del_last_nftl_blk(&tr->nftl_blk_head); while (nftl_blk != NULL) { if (nftl_blk->nftl_thread != NULL) { kthread_stop(nftl_blk->nftl_thread); nftl_blk->nftl_thread = NULL; } /* nand_kobj = container_of(&nftl_blk, struct nand_kobject, nftl_blk);*/ /* kobject_del(&nand_kobj->kobj);*/ /* kobject_put(&nand_kobj->kobj);*/ kfree(nftl_blk->blk_lock); nftl_exit(nftl_blk); kfree(nftl_blk); nftl_blk = del_last_nftl_blk(&tr->nftl_blk_head); } return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int nand_flush(struct nand_blk_dev *dev) { int error = 0; struct _nand_dev *nand_dev = (struct _nand_dev *)(dev->priv); /*error = nand_dev->flush_sector_write_cache(nand_dev,0);*/ error = nand_dev->flush_write_cache(nand_dev, 0xffff); return error; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int dev_initialize(struct _nand_dev *nand_dev, struct _nftl_blk *nftl_blk, __u32 offset, __u32 size) { __u32 offset_t, size_t; offset_t = offset; size_t = size; nand_dev->nftl_blk = nftl_blk; if (offset_t < nftl_blk->nftl_logic_size) { nand_dev->offset = offset_t; } else { /*nand_dbg_err("dev_initialize %x %x \n",offset_t,nftl_blk->nftl_logic_size);*/ return 1; } if ((nand_dev->offset + size_t) <= nftl_blk->nftl_logic_size) { nand_dev->size = size_t; } else { nand_dev->size = nftl_blk->nftl_logic_size - nand_dev->offset; } nand_dev->read_data = _dev_nand_read; nand_dev->write_data = _dev_nand_write; nand_dev->flush_write_cache = _dev_flush_write_cache; nand_dev->flush_sector_write_cache = _dev_flush_sector_write_cache; nand_dev->discard = _dev_nand_discard; /*nand_dbg_err("dev_initialize 0x%x \n",nand_dev->size);*/ return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int _dev_nand_read(struct _nand_dev *nand_dev, __u32 start_sector, __u32 len, unsigned char *buf) { int ret; struct _nftl_blk *nftl_blk = nand_dev->nftl_blk; nand_active_time = jiffies; mutex_lock(nftl_blk->blk_lock); if (start_sector + len > nand_dev->size) { ret = 0xfffff; nand_dbg_err("_dev_nand_read over size 0x%x 0x%x\n", start_sector, nand_dev->size); while (--ret) ; ret = -1; goto _dev_nand_read_end; } ret = nand_dev->nftl_blk->read_data(nand_dev->nftl_blk, start_sector + nand_dev->offset, len, buf); _dev_nand_read_end: nand_active_time = jiffies; mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int _dev_nand_write(struct _nand_dev *nand_dev, __u32 start_sector, __u32 len, unsigned char *buf) { __u32 ret; struct _nftl_blk *nftl_blk = nand_dev->nftl_blk; nand_active_time = jiffies; nand_write_active_time = jiffies; mutex_lock(nftl_blk->blk_lock); if (start_sector + len > nand_dev->size) { ret = 0xffffff; nand_dbg_err("_dev_nand_write over size 0x%x 0x%x\n", start_sector, nand_dev->size); while (--ret) ; ret = -1; goto _dev_nand_write_end; } ret = nand_dev->nftl_blk->write_data(nand_dev->nftl_blk, start_sector + nand_dev->offset, len, buf); _dev_nand_write_end: nand_active_time = jiffies; nand_write_active_time = jiffies; mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int _dev_nand_discard(struct _nand_dev *nand_dev, __u32 start_sector, __u32 len) { __u32 ret = 0; struct _nftl_blk *nftl_blk = nand_dev->nftl_blk; mutex_lock(nftl_blk->blk_lock); /*nand_dbg_err("==========nand_discard========== %d,%d\n",start_sector,len);*/ ret = nand_dev->nftl_blk->discard(nand_dev->nftl_blk, start_sector + nand_dev->offset, len); nand_active_time = jiffies; mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int _dev_flush_write_cache(struct _nand_dev *nand_dev, __u32 num) { __u32 ret; struct _nftl_blk *nftl_blk = nand_dev->nftl_blk; nand_active_time = jiffies; mutex_lock(nftl_blk->blk_lock); ret = nand_dev->nftl_blk->flush_write_cache(nand_dev->nftl_blk, num); nand_active_time = jiffies; mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int _dev_flush_sector_write_cache(struct _nand_dev *nand_dev, __u32 num) { __u32 ret; struct _nftl_blk *nftl_blk = nand_dev->nftl_blk; mutex_lock(nftl_blk->blk_lock); ret = nand_dev->nftl_blk->flush_sector_write_cache(nand_dev->nftl_blk, num); mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ struct _nand_dev * _get_nand_dev_by_name(char * name) { int len; struct _nand_dev * p; //nand_dbg_err("_get_nand_dev_by_name: %s \n",name); len = strlen(name)+1; if(len > 16) { len = 16; } for(p=&mytr.nand_dev_head; p!=NULL; p = p->nand_dev_next) { //nand_dbg_err(" %s %d\n",p->name,len); if( memcmp(p->name,name,len) == 0) { return p; } } return NULL; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ uint32 nand_read_reclaim(struct _nftl_blk *start_blk, unsigned char *buf, uint32 utc) { uint32 ret = 0; struct _nftl_blk *nftl_blk; nftl_blk = get_nftl_need_read_claim(start_blk, utc); if (nftl_blk == NULL) { return 1; } mutex_lock(nftl_blk->blk_lock); ret = read_reclaim(start_blk, nftl_blk, buf, utc); mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int critical_dev_nand_read(uchar *dev_name, __u32 start_sector, __u32 sector_num, uchar *buf) { struct _nand_dev *nand_dev = _get_nand_dev_by_name(dev_name); if(nand_dev == NULL) { nand_dbg_err("partition name not exit!!\n"); return 1; } if (start_sector + sector_num > nand_dev->size) { nand_dbg_err("over size %d + %d > %d\n", start_sector, sector_num, nand_dev->size); return -1; } nand_dbg_err("partition name %s!!\n",dev_name); return panic_read(nand_dev->nftl_blk, start_sector + nand_dev->offset, sector_num, buf); } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int critical_dev_nand_write(uchar *dev_name, __u32 start_sector, __u32 sector_num, uchar *buf) { int ret; struct _nand_dev *nand_dev = _get_nand_dev_by_name(dev_name); if(nand_dev == NULL) { nand_dbg_err("partition name not exit!!\n"); return 1; } if (start_sector + sector_num > nand_dev->size) { nand_dbg_err("over size %d + %d > %d\n", start_sector, sector_num, nand_dev->size); return -1; } nand_dbg_err("partition name %s!!\n",dev_name); return panic_write(nand_dev->nftl_blk, start_sector + nand_dev->offset, sector_num, buf); }