Loading drivers/mtd/ubi/build.c +11 −9 Original line number Diff line number Diff line /* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2007 * Copyright (c) 2014, Linux Foundation. All rights reserved. * Copyright (c) 2014 - 2015, Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 * license terms, and distributes only under these terms. * Loading Loading @@ -152,8 +152,8 @@ static struct device_attribute dev_dt_threshold = static struct device_attribute dev_rd_threshold = __ATTR(rd_threshold, S_IRUGO | S_IWUGO, dev_attribute_show, dev_attribute_store); static struct device_attribute dev_mtd_trigger_scan = __ATTR(peb_scan, S_IRUGO | S_IWUSR, static struct device_attribute dev_mtd_trigger_scrub = __ATTR(peb_scrub, S_IRUGO | S_IWUSR, dev_attribute_show, dev_attribute_store); /** Loading Loading @@ -396,8 +396,8 @@ static ssize_t dev_attribute_show(struct device *dev, ret = sprintf(buf, "%d\n", ubi->dt_threshold); else if (attr == &dev_rd_threshold) ret = sprintf(buf, "%d\n", ubi->rd_threshold); else if (attr == &dev_mtd_trigger_scan) ret = sprintf(buf, "%d\n", ubi->scan_in_progress); else if (attr == &dev_mtd_trigger_scrub) ret = snprintf(buf, 3, "%d\n", ubi->scrub_in_progress); else ret = -EINVAL; Loading Loading @@ -435,7 +435,7 @@ static ssize_t dev_attribute_store(struct device *dev, else pr_err("Max supported threshold value is %d", UBI_MAX_READCOUNTER); } else if (attr == &dev_mtd_trigger_scan) { } else if (attr == &dev_mtd_trigger_scrub) { if (value != 1) { pr_err("Invalid input. Echo 1 to start trigger"); goto out; Loading @@ -444,10 +444,12 @@ static ssize_t dev_attribute_store(struct device *dev, pr_err("lookuptbl is null"); goto out; } ret = ubi_wl_scan_all(ubi); ret = ubi_wl_scrub_all(ubi); } out: if (ret == 0) ret = count; ubi_put_device(ubi); return ret; } Loading Loading @@ -520,7 +522,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref) err = device_create_file(&ubi->dev, &dev_rd_threshold); if (err) return err; err = device_create_file(&ubi->dev, &dev_mtd_trigger_scan); err = device_create_file(&ubi->dev, &dev_mtd_trigger_scrub); return err; } Loading @@ -530,7 +532,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref) */ static void ubi_sysfs_close(struct ubi_device *ubi) { device_remove_file(&ubi->dev, &dev_mtd_trigger_scan); device_remove_file(&ubi->dev, &dev_mtd_trigger_scrub); device_remove_file(&ubi->dev, &dev_mtd_num); device_remove_file(&ubi->dev, &dev_dt_threshold); device_remove_file(&ubi->dev, &dev_rd_threshold); Loading drivers/mtd/ubi/ubi.h +13 −4 Original line number Diff line number Diff line /* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2006, 2007 * Copyright (c) 2014, Linux Foundation. All rights reserved. * Copyright (c) 2014 - 2015, Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 * license terms, and distributes only under these terms. * Loading Loading @@ -124,6 +124,14 @@ #define UBI_DFS_DIR_NAME "ubi%d" #define UBI_DFS_DIR_LEN (3 + 2 + 1) /* * When scrub_all is triggered, all free PEBs will be scheduled for erasure. * Until the bg thread performs the work, we are left with now free PEBs. * To make sure we can flush the fm, UBI_FM_MAX_BLOCKS are erased synchroniusly * before scrub_all is returning. */ #define NUM_PEBS_TO_SYNC_ERASE UBI_FM_MAX_BLOCKS /* * Error codes returned by the I/O sub-system. * Loading Loading @@ -493,7 +501,8 @@ struct ubi_debug_info { * for more info * @dt_threshold: data retention threshold. See UBI_DT_THRESHOLD * for more info * @scan_in_progress: true if scanning of device PEBs is in progress * @scrub_in_progress: true while scheduling all device PEBs for scrub/erase * is in progress * * @flash_size: underlying MTD device size (in bytes) * @peb_count: count of physical eraseblocks on the MTD device Loading Loading @@ -598,7 +607,7 @@ struct ubi_device { char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; int rd_threshold; int dt_threshold; bool scan_in_progress; bool scrub_in_progress; /* I/O sub-system's stuff */ Loading Loading @@ -879,7 +888,7 @@ int ubi_is_erase_work(struct ubi_work *wrk); void ubi_refill_pools(struct ubi_device *ubi); int ubi_ensure_anchor_pebs(struct ubi_device *ubi); int ubi_in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root); int ubi_wl_scan_all(struct ubi_device *ubi); int ubi_wl_scrub_all(struct ubi_device *ubi); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, Loading drivers/mtd/ubi/wl.c +109 −93 Original line number Diff line number Diff line /* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) 2014, Linux Foundation. All rights reserved. * Copyright (c) 2014 - 2015, Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 * license terms, and distributes only under these terms. * Loading Loading @@ -145,6 +145,8 @@ static int self_check_in_pq(const struct ubi_device *ubi, struct ubi_wl_entry *e); static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int vol_id, int lnum, int torture); static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture); #ifdef CONFIG_MTD_UBI_FASTMAP /** Loading Loading @@ -561,11 +563,8 @@ retry: static void return_unused_pool_pebs(struct ubi_device *ubi, struct ubi_fm_pool *pool) { int i, err; int i; struct ubi_wl_entry *e; struct timeval tv; do_gettimeofday(&tv); for (i = pool->used; i < pool->size; i++) { e = ubi->lookuptbl[pool->pebs[i]]; Loading @@ -576,24 +575,10 @@ static void return_unused_pool_pebs(struct ubi_device *ubi, self_check_in_wl_tree(ubi, e, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub); } if (e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY)) { spin_unlock(&ubi->wl_lock); err = schedule_erase(ubi, e, UBI_UNKNOWN, UBI_UNKNOWN, 0); spin_lock(&ubi->wl_lock); if (err) { ubi_err(ubi->ubi_num, "Failed to schedule erase for PEB %d (err=%d)", e->pnum, err); ubi_ro_mode(ubi); } } else { wl_tree_add(e, &ubi->free); ubi->free_count++; } } } /** * refill_wl_pool - refills all the fastmap pool used by the Loading Loading @@ -757,120 +742,151 @@ int ubi_wl_get_peb(struct ubi_device *ubi) * ubi_wl_scan_all - Scan all PEB's * @ubi: UBI device description object * * This function scans all device PEBs in order to locate once * need scrubbing; due to read disturb threashold or last erase * timestamp. * This function schedules all device PEBs for erasure if free, or for * scrubbing otherwise. This trigger is used to prevent data loss due to read * disturb, data retention. * * Return 0 in case of sucsess, (negative) error code otherwise * Return 0 in case of success, (negative) error code otherwise * */ int ubi_wl_scan_all(struct ubi_device *ubi) int ubi_wl_scrub_all(struct ubi_device *ubi) { struct timeval tv; struct rb_node *node; struct ubi_wl_entry *wl_e, *tmp; int used_cnt, free_cnt; int err; int i, err = 0; struct ubi_wl_entry *sync_erase_q[NUM_PEBS_TO_SYNC_ERASE] = {0}; int sync_erase_pos = 0; do_gettimeofday(&tv); if (!ubi->lookuptbl) { ubi_err(ubi->ubi_num, "lookuptbl is null"); return -ENOENT; } spin_lock(&ubi->wl_lock); if (ubi->scan_in_progress) { if (ubi->scrub_in_progress) { ubi_err(ubi->ubi_num, "Scan already in progress, ignoring the trigger"); err = -EPERM; goto out; spin_unlock(&ubi->wl_lock); return err; } ubi->scan_in_progress = true; ubi->scrub_in_progress = true; /* stop all works in order to freeze system state */ ubi->thread_enabled = 0; spin_unlock(&ubi->wl_lock); down_write(&ubi->work_sem); up_write(&ubi->work_sem); ubi_msg(ubi->ubi_num, "Scanning all PEBs for read-disturb/erasures"); /* For PEBs in free list rc=0 */ free_cnt = 0; node = rb_first(&ubi->free); while (node) { /* * fm_mutex prevents fastmap flush. * Without FM flush there is no pools refill. * When the pools are empty, there are no available PEBSs for write. * Thus prevent PEBS's from moving under our feet. * * Keep the wl_lock, while iterating the wl data structures. */ mutex_lock(&ubi->fm_mutex); spin_lock(&ubi->wl_lock); ubi_msg(ubi->ubi_num, "Scheduling all PEBs for scrub/erasure"); /* * Flush the pools into the free list before erasing all the * PEBS in the free list. */ return_unused_pool_pebs(ubi, &ubi->fm_wl_pool); ubi->fm_wl_pool.used = ubi->fm_wl_pool.size = 0; return_unused_pool_pebs(ubi, &ubi->fm_pool); ubi->fm_pool.used = ubi->fm_pool.size = 0; /* PEBs in free list */ while ((node = rb_first(&ubi->free)) != NULL) { wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); node = rb_next(node); if (wl_e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY)) { /* Sanity check to verify consistency */ if (self_check_in_wl_tree(ubi, wl_e, &ubi->free)) { ubi_err(ubi->ubi_num, "PEB %d moved from free tree", ubi_err(ubi->ubi_num, "PEB %d moved from free tree", wl_e->pnum); err = -EAGAIN; spin_unlock(&ubi->wl_lock); goto out; } rb_erase(&wl_e->u.rb, &ubi->free); ubi->free_count--; if (sync_erase_pos < NUM_PEBS_TO_SYNC_ERASE) { sync_erase_q[sync_erase_pos++] = wl_e; } else { spin_unlock(&ubi->wl_lock); err = schedule_erase(ubi, wl_e, UBI_UNKNOWN, UBI_UNKNOWN, 0); spin_lock(&ubi->wl_lock); } if (err) { ubi_err(ubi->ubi_num, "Failed to schedule erase for PEB %d (err=%d)", wl_e->pnum, err); ubi_ro_mode(ubi); spin_unlock(&ubi->wl_lock); goto out; } free_cnt++; } } used_cnt = 0; node = rb_first(&ubi->used); while (node) { /* Move all used pebs to scrub tree */ while ((node = rb_first(&ubi->used)) != NULL) { wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); node = rb_next(node); if ((wl_e->rc >= UBI_RD_THRESHOLD) || (wl_e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY))) { rb_erase(&wl_e->u.rb, &ubi->used); wl_tree_add(wl_e, &ubi->scrub); } /* Go over protection queue */ for (i = 0; i < UBI_PROT_QUEUE_LEN; i++) { list_for_each_entry_safe(wl_e, tmp, &ubi->pq[i], u.list) { spin_unlock(&ubi->wl_lock); err = ubi_wl_scrub_peb(ubi, wl_e->pnum); spin_lock(&ubi->wl_lock); if (err) ubi_err(ubi->ubi_num, "Failed to schedule scrub for PEB %d (err=%d)", wl_e->pnum, err); else used_cnt++; spin_lock(&ubi->wl_lock); } } /* Go over protection queue */ list_for_each_entry_safe(wl_e, tmp, &ubi->pq[ubi->pq_head], u.list) { if ((wl_e->rc >= UBI_RD_THRESHOLD) || (wl_e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY))) { spin_unlock(&ubi->wl_lock); err = ubi_wl_scrub_peb(ubi, wl_e->pnum); for (i = 0; i < sync_erase_pos; i++) { wl_e = sync_erase_q[i]; err = sync_erase(ubi, wl_e, 0); if (err) { ubi_err(ubi->ubi_num, "Failed to erase PEB %d (err=%d)", wl_e->pnum, err); err = schedule_erase(ubi, wl_e, UBI_UNKNOWN, UBI_UNKNOWN, 0); if (err) ubi_err(ubi->ubi_num, "Failed to schedule scrub for PEB %d (err=%d)", ubi_err(ubi->ubi_num, "Failed to schedule scrub for PEB %d (err=%d)", wl_e->pnum, err); else used_cnt++; spin_lock(&ubi->wl_lock); } /* even if have errors we still have to return those PEB's */ spin_lock(&ubi->wl_lock); wl_tree_add(wl_e, &ubi->free); ubi->free_count++; spin_unlock(&ubi->wl_lock); } out: mutex_unlock(&ubi->fm_mutex); /* Resume the worker thread */ spin_lock(&ubi->wl_lock); ubi->thread_enabled = 1; spin_unlock(&ubi->wl_lock); ubi_msg(ubi->ubi_num, "Scheduled %d for erasure", free_cnt); ubi_msg(ubi->ubi_num, "Scehduled %d for scrubbing", used_cnt); err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); if (err) ubi_err(ubi->ubi_num, "Failed to flush ubi wq. err = %d", err); else ubi_msg(ubi->ubi_num, "Flashed ubi wq"); if (!ubi_dbg_is_bgt_disabled(ubi)) wake_up_process(ubi->bgt_thread); /* Make sure all PEBs are scrubed after reset */ err = ubi_update_fastmap(ubi); spin_lock(&ubi->wl_lock); out: ubi->scan_in_progress = false; ubi->scrub_in_progress = false; spin_unlock(&ubi->wl_lock); ubi_msg(ubi->ubi_num, "Scanning all PEBs completed. err = %d", err); ubi_msg(ubi->ubi_num, "Scrubbing all PEBs completed. err = %d", err); return err; } Loading Loading
drivers/mtd/ubi/build.c +11 −9 Original line number Diff line number Diff line /* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2007 * Copyright (c) 2014, Linux Foundation. All rights reserved. * Copyright (c) 2014 - 2015, Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 * license terms, and distributes only under these terms. * Loading Loading @@ -152,8 +152,8 @@ static struct device_attribute dev_dt_threshold = static struct device_attribute dev_rd_threshold = __ATTR(rd_threshold, S_IRUGO | S_IWUGO, dev_attribute_show, dev_attribute_store); static struct device_attribute dev_mtd_trigger_scan = __ATTR(peb_scan, S_IRUGO | S_IWUSR, static struct device_attribute dev_mtd_trigger_scrub = __ATTR(peb_scrub, S_IRUGO | S_IWUSR, dev_attribute_show, dev_attribute_store); /** Loading Loading @@ -396,8 +396,8 @@ static ssize_t dev_attribute_show(struct device *dev, ret = sprintf(buf, "%d\n", ubi->dt_threshold); else if (attr == &dev_rd_threshold) ret = sprintf(buf, "%d\n", ubi->rd_threshold); else if (attr == &dev_mtd_trigger_scan) ret = sprintf(buf, "%d\n", ubi->scan_in_progress); else if (attr == &dev_mtd_trigger_scrub) ret = snprintf(buf, 3, "%d\n", ubi->scrub_in_progress); else ret = -EINVAL; Loading Loading @@ -435,7 +435,7 @@ static ssize_t dev_attribute_store(struct device *dev, else pr_err("Max supported threshold value is %d", UBI_MAX_READCOUNTER); } else if (attr == &dev_mtd_trigger_scan) { } else if (attr == &dev_mtd_trigger_scrub) { if (value != 1) { pr_err("Invalid input. Echo 1 to start trigger"); goto out; Loading @@ -444,10 +444,12 @@ static ssize_t dev_attribute_store(struct device *dev, pr_err("lookuptbl is null"); goto out; } ret = ubi_wl_scan_all(ubi); ret = ubi_wl_scrub_all(ubi); } out: if (ret == 0) ret = count; ubi_put_device(ubi); return ret; } Loading Loading @@ -520,7 +522,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref) err = device_create_file(&ubi->dev, &dev_rd_threshold); if (err) return err; err = device_create_file(&ubi->dev, &dev_mtd_trigger_scan); err = device_create_file(&ubi->dev, &dev_mtd_trigger_scrub); return err; } Loading @@ -530,7 +532,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref) */ static void ubi_sysfs_close(struct ubi_device *ubi) { device_remove_file(&ubi->dev, &dev_mtd_trigger_scan); device_remove_file(&ubi->dev, &dev_mtd_trigger_scrub); device_remove_file(&ubi->dev, &dev_mtd_num); device_remove_file(&ubi->dev, &dev_dt_threshold); device_remove_file(&ubi->dev, &dev_rd_threshold); Loading
drivers/mtd/ubi/ubi.h +13 −4 Original line number Diff line number Diff line /* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2006, 2007 * Copyright (c) 2014, Linux Foundation. All rights reserved. * Copyright (c) 2014 - 2015, Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 * license terms, and distributes only under these terms. * Loading Loading @@ -124,6 +124,14 @@ #define UBI_DFS_DIR_NAME "ubi%d" #define UBI_DFS_DIR_LEN (3 + 2 + 1) /* * When scrub_all is triggered, all free PEBs will be scheduled for erasure. * Until the bg thread performs the work, we are left with now free PEBs. * To make sure we can flush the fm, UBI_FM_MAX_BLOCKS are erased synchroniusly * before scrub_all is returning. */ #define NUM_PEBS_TO_SYNC_ERASE UBI_FM_MAX_BLOCKS /* * Error codes returned by the I/O sub-system. * Loading Loading @@ -493,7 +501,8 @@ struct ubi_debug_info { * for more info * @dt_threshold: data retention threshold. See UBI_DT_THRESHOLD * for more info * @scan_in_progress: true if scanning of device PEBs is in progress * @scrub_in_progress: true while scheduling all device PEBs for scrub/erase * is in progress * * @flash_size: underlying MTD device size (in bytes) * @peb_count: count of physical eraseblocks on the MTD device Loading Loading @@ -598,7 +607,7 @@ struct ubi_device { char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; int rd_threshold; int dt_threshold; bool scan_in_progress; bool scrub_in_progress; /* I/O sub-system's stuff */ Loading Loading @@ -879,7 +888,7 @@ int ubi_is_erase_work(struct ubi_work *wrk); void ubi_refill_pools(struct ubi_device *ubi); int ubi_ensure_anchor_pebs(struct ubi_device *ubi); int ubi_in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root); int ubi_wl_scan_all(struct ubi_device *ubi); int ubi_wl_scrub_all(struct ubi_device *ubi); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, Loading
drivers/mtd/ubi/wl.c +109 −93 Original line number Diff line number Diff line /* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) 2014, Linux Foundation. All rights reserved. * Copyright (c) 2014 - 2015, Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 * license terms, and distributes only under these terms. * Loading Loading @@ -145,6 +145,8 @@ static int self_check_in_pq(const struct ubi_device *ubi, struct ubi_wl_entry *e); static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int vol_id, int lnum, int torture); static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture); #ifdef CONFIG_MTD_UBI_FASTMAP /** Loading Loading @@ -561,11 +563,8 @@ retry: static void return_unused_pool_pebs(struct ubi_device *ubi, struct ubi_fm_pool *pool) { int i, err; int i; struct ubi_wl_entry *e; struct timeval tv; do_gettimeofday(&tv); for (i = pool->used; i < pool->size; i++) { e = ubi->lookuptbl[pool->pebs[i]]; Loading @@ -576,24 +575,10 @@ static void return_unused_pool_pebs(struct ubi_device *ubi, self_check_in_wl_tree(ubi, e, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub); } if (e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY)) { spin_unlock(&ubi->wl_lock); err = schedule_erase(ubi, e, UBI_UNKNOWN, UBI_UNKNOWN, 0); spin_lock(&ubi->wl_lock); if (err) { ubi_err(ubi->ubi_num, "Failed to schedule erase for PEB %d (err=%d)", e->pnum, err); ubi_ro_mode(ubi); } } else { wl_tree_add(e, &ubi->free); ubi->free_count++; } } } /** * refill_wl_pool - refills all the fastmap pool used by the Loading Loading @@ -757,120 +742,151 @@ int ubi_wl_get_peb(struct ubi_device *ubi) * ubi_wl_scan_all - Scan all PEB's * @ubi: UBI device description object * * This function scans all device PEBs in order to locate once * need scrubbing; due to read disturb threashold or last erase * timestamp. * This function schedules all device PEBs for erasure if free, or for * scrubbing otherwise. This trigger is used to prevent data loss due to read * disturb, data retention. * * Return 0 in case of sucsess, (negative) error code otherwise * Return 0 in case of success, (negative) error code otherwise * */ int ubi_wl_scan_all(struct ubi_device *ubi) int ubi_wl_scrub_all(struct ubi_device *ubi) { struct timeval tv; struct rb_node *node; struct ubi_wl_entry *wl_e, *tmp; int used_cnt, free_cnt; int err; int i, err = 0; struct ubi_wl_entry *sync_erase_q[NUM_PEBS_TO_SYNC_ERASE] = {0}; int sync_erase_pos = 0; do_gettimeofday(&tv); if (!ubi->lookuptbl) { ubi_err(ubi->ubi_num, "lookuptbl is null"); return -ENOENT; } spin_lock(&ubi->wl_lock); if (ubi->scan_in_progress) { if (ubi->scrub_in_progress) { ubi_err(ubi->ubi_num, "Scan already in progress, ignoring the trigger"); err = -EPERM; goto out; spin_unlock(&ubi->wl_lock); return err; } ubi->scan_in_progress = true; ubi->scrub_in_progress = true; /* stop all works in order to freeze system state */ ubi->thread_enabled = 0; spin_unlock(&ubi->wl_lock); down_write(&ubi->work_sem); up_write(&ubi->work_sem); ubi_msg(ubi->ubi_num, "Scanning all PEBs for read-disturb/erasures"); /* For PEBs in free list rc=0 */ free_cnt = 0; node = rb_first(&ubi->free); while (node) { /* * fm_mutex prevents fastmap flush. * Without FM flush there is no pools refill. * When the pools are empty, there are no available PEBSs for write. * Thus prevent PEBS's from moving under our feet. * * Keep the wl_lock, while iterating the wl data structures. */ mutex_lock(&ubi->fm_mutex); spin_lock(&ubi->wl_lock); ubi_msg(ubi->ubi_num, "Scheduling all PEBs for scrub/erasure"); /* * Flush the pools into the free list before erasing all the * PEBS in the free list. */ return_unused_pool_pebs(ubi, &ubi->fm_wl_pool); ubi->fm_wl_pool.used = ubi->fm_wl_pool.size = 0; return_unused_pool_pebs(ubi, &ubi->fm_pool); ubi->fm_pool.used = ubi->fm_pool.size = 0; /* PEBs in free list */ while ((node = rb_first(&ubi->free)) != NULL) { wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); node = rb_next(node); if (wl_e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY)) { /* Sanity check to verify consistency */ if (self_check_in_wl_tree(ubi, wl_e, &ubi->free)) { ubi_err(ubi->ubi_num, "PEB %d moved from free tree", ubi_err(ubi->ubi_num, "PEB %d moved from free tree", wl_e->pnum); err = -EAGAIN; spin_unlock(&ubi->wl_lock); goto out; } rb_erase(&wl_e->u.rb, &ubi->free); ubi->free_count--; if (sync_erase_pos < NUM_PEBS_TO_SYNC_ERASE) { sync_erase_q[sync_erase_pos++] = wl_e; } else { spin_unlock(&ubi->wl_lock); err = schedule_erase(ubi, wl_e, UBI_UNKNOWN, UBI_UNKNOWN, 0); spin_lock(&ubi->wl_lock); } if (err) { ubi_err(ubi->ubi_num, "Failed to schedule erase for PEB %d (err=%d)", wl_e->pnum, err); ubi_ro_mode(ubi); spin_unlock(&ubi->wl_lock); goto out; } free_cnt++; } } used_cnt = 0; node = rb_first(&ubi->used); while (node) { /* Move all used pebs to scrub tree */ while ((node = rb_first(&ubi->used)) != NULL) { wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); node = rb_next(node); if ((wl_e->rc >= UBI_RD_THRESHOLD) || (wl_e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY))) { rb_erase(&wl_e->u.rb, &ubi->used); wl_tree_add(wl_e, &ubi->scrub); } /* Go over protection queue */ for (i = 0; i < UBI_PROT_QUEUE_LEN; i++) { list_for_each_entry_safe(wl_e, tmp, &ubi->pq[i], u.list) { spin_unlock(&ubi->wl_lock); err = ubi_wl_scrub_peb(ubi, wl_e->pnum); spin_lock(&ubi->wl_lock); if (err) ubi_err(ubi->ubi_num, "Failed to schedule scrub for PEB %d (err=%d)", wl_e->pnum, err); else used_cnt++; spin_lock(&ubi->wl_lock); } } /* Go over protection queue */ list_for_each_entry_safe(wl_e, tmp, &ubi->pq[ubi->pq_head], u.list) { if ((wl_e->rc >= UBI_RD_THRESHOLD) || (wl_e->last_erase_time + UBI_DT_THRESHOLD < (tv.tv_sec / NUM_SEC_IN_DAY))) { spin_unlock(&ubi->wl_lock); err = ubi_wl_scrub_peb(ubi, wl_e->pnum); for (i = 0; i < sync_erase_pos; i++) { wl_e = sync_erase_q[i]; err = sync_erase(ubi, wl_e, 0); if (err) { ubi_err(ubi->ubi_num, "Failed to erase PEB %d (err=%d)", wl_e->pnum, err); err = schedule_erase(ubi, wl_e, UBI_UNKNOWN, UBI_UNKNOWN, 0); if (err) ubi_err(ubi->ubi_num, "Failed to schedule scrub for PEB %d (err=%d)", ubi_err(ubi->ubi_num, "Failed to schedule scrub for PEB %d (err=%d)", wl_e->pnum, err); else used_cnt++; spin_lock(&ubi->wl_lock); } /* even if have errors we still have to return those PEB's */ spin_lock(&ubi->wl_lock); wl_tree_add(wl_e, &ubi->free); ubi->free_count++; spin_unlock(&ubi->wl_lock); } out: mutex_unlock(&ubi->fm_mutex); /* Resume the worker thread */ spin_lock(&ubi->wl_lock); ubi->thread_enabled = 1; spin_unlock(&ubi->wl_lock); ubi_msg(ubi->ubi_num, "Scheduled %d for erasure", free_cnt); ubi_msg(ubi->ubi_num, "Scehduled %d for scrubbing", used_cnt); err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); if (err) ubi_err(ubi->ubi_num, "Failed to flush ubi wq. err = %d", err); else ubi_msg(ubi->ubi_num, "Flashed ubi wq"); if (!ubi_dbg_is_bgt_disabled(ubi)) wake_up_process(ubi->bgt_thread); /* Make sure all PEBs are scrubed after reset */ err = ubi_update_fastmap(ubi); spin_lock(&ubi->wl_lock); out: ubi->scan_in_progress = false; ubi->scrub_in_progress = false; spin_unlock(&ubi->wl_lock); ubi_msg(ubi->ubi_num, "Scanning all PEBs completed. err = %d", err); ubi_msg(ubi->ubi_num, "Scrubbing all PEBs completed. err = %d", err); return err; } Loading