mtd: ubi: Fill read disturb statistics
Subsystems (4)
PATCH v2 2/5 mtd: ubi: Fill read disturb statistics
Fill in eraseblock statistics needed for read disturb decision making:
1. read counter: incremented at each read operation
reset at each erase operation
Saved only as part of the meta data in RAM
On attach: if fastmap data is missing a default value is assigned
2. last erase time stamp: updated at each erase operation. Saved as part of PEB OOB info on flash
On attach: if fastmap data is missing retrieved from PEB EC Header on NAND.
If fastmap data is corrupted a default value is assigned.
The above parameters are saved as part of the fastmap data.
Signed-off-by Dolev Raviv <[email protected]>
Signed-off-by Tanya Brokhman <[email protected]>
--- drivers/mtd/ubi/attach.c | 137 +++++++++++++++++++++++++++++++++++----------- drivers/mtd/ubi/debug.c | 11 ++++ drivers/mtd/ubi/fastmap.c | 118 ++++++++++++++++++++++++++++++++++----- drivers/mtd/ubi/io.c | 28 ++++++++++ drivers/mtd/ubi/ubi.h | 24 +++++++- drivers/mtd/ubi/vtbl.c | 6 +- drivers/mtd/ubi/wl.c | 47 ++++++++++++++++ 7 files changed, 322 insertions(+), 49 deletions(-)
drivers/mtd/ubi/attach.c +105 -32
@@ -1,5 +1,8 @@/* * Copyright (c) International Business Machines Corp., 2006 +* Copyright (c) 2014, Linux Foundation. All rights reserved. +* Linux Foundation chooses to take subject only to the GPLv2 +* license terms, and distributes only under these terms. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by
@@ -87,8 +90,21 @@#include <linux/crc32.h> #include <linux/math64.h> #include <linux/random.h> +#include <linux/time.h> #include "ubi.h" +#define set_aeb_default_values (aeb, ai) \ + do { \ + if (aeb->ec == UBI_UNKNOWN) { \ + aeb->ec = ai->mean_ec; \ + if (ai->mean_last_erase_time) \ + aeb->last_erase_time = \ + ai->mean_last_erase_time; \ + else \ + aeb->last_erase_time = UBI_DT_THRESHOLD / 2; \ + } \ + } while (0) +static int self_check_ai (struct ubi_device *ubi,struct ubi_attach_info *ai);/* Temporary variables used during scanning */
@@ -102,6 +118,9 @@ static struct ubi_vid_hdr *vidh;* @vol_id: the last used volume id for the PEB * @lnum: the last used LEB number for the PEB * @ec: erase counter of the physical eraseblock +* @last_erase_time: last erase time stamp (%UBI_UNKNOWN if it +* is unknown) +* @rc: read counter (%UBI_UNKNOWN if it is unknown) * @to_head: if not zero, add to the head of the list * @list: the list to add to *
@@ -117,7 +136,8 @@ static struct ubi_vid_hdr *vidh;* failure. */ static int add_to_list (struct ubi_attach_info *ai,int pnum,int vol_id, -int lnum,int ec,int to_head,struct list_head *list) +int lnum,int ec,long last_erase_time,long rc, +int to_head,struct list_head *list) {struct ubi_ainf_peb *aeb;
@@ -139,6 +159,9 @@ static int add_to_list(struct ubi_attach_info *ai, int pnum, int vol_id, aeb->vol_id = vol_id; aeb->lnum = lnum; aeb->ec = ec; + aeb->rc = rc; + aeb->last_erase_time = last_erase_time; +if (to_head)list_add (&aeb->u.list, list);else
@@ -151,13 +174,17 @@ static int add_to_list(struct ubi_attach_info *ai, int pnum, int vol_id,* @ai: attaching information * @pnum: physical eraseblock number to add * @ec: erase counter of the physical eraseblock +* @last_erase_time: last erase time stamp (%UBI_UNKNOWN if it +* is unknown) +* @rc: read counter (%UBI_UNKNOWN if it is unknown) * * This function allocates a 'struct ubi_ainf_peb' object for a corrupted * physical eraseblock @pnum and adds it to the 'corr' list. The corruption * was presumably not caused by a power cut. Returns zero in case of success * and a negative error code in case of failure. */ -static int add_corrupted (struct ubi_attach_info *ai,int pnum,int ec) +static int add_corrupted (struct ubi_attach_info *ai,int pnum, +int ec,long rc,long last_erase_time) {struct ubi_ainf_peb *aeb;
@@ -170,6 +197,8 @@ static int add_corrupted(struct ubi_attach_info *ai, int pnum, int ec) ai->corr_peb_count +=1 ; aeb->pnum = pnum; aeb->ec = ec; + aeb->rc = rc; + aeb->last_erase_time = last_erase_time;list_add (&aeb->u.list, &ai->corr);return 0 ; }
@@ -434,6 +463,9 @@ out_free_vidh:* @ai: attaching information * @pnum: the physical eraseblock number * @ec: erase counter +* @last_erase_time: last erase time stamp (%UBI_UNKNOWN if it +* is unknown) +* @rc: read counter (%UBI_UNKNOWN if it is unknown) * @vid_hdr: the volume identifier header * @bitflips: if bit-flips were detected when this physical eraseblock was read *
@@ -445,7 +477,8 @@ out_free_vidh:* zero in case of success and a negative error code in case of failure. */ int ubi_add_to_av (struct ubi_device *ubi,struct ubi_attach_info *ai,int pnum, -int ec,const struct ubi_vid_hdr *vid_hdr,int bitflips) +int ec,long last_erase_time,long rc, +const struct ubi_vid_hdr *vid_hdr,int bitflips) {int err, vol_id, lnum;unsigned long long sqnum;
@@ -532,12 +565,16 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,return err; err =add_to_list (ai, aeb->pnum, aeb->vol_id, - aeb->lnum, aeb->ec, cmp_res &4 , + aeb->lnum, aeb->ec, + aeb->last_erase_time, + aeb->rc, cmp_res &4 , &ai->erase);if (err)return err; aeb->ec = ec; + aeb->last_erase_time = last_erase_time; + aeb->rc = rc; aeb->pnum = pnum; aeb->vol_id = vol_id; aeb->lnum = lnum;
@@ -556,7 +593,8 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,* previously. */ return add_to_list (ai, pnum, vol_id, lnum, ec, - cmp_res &4 , &ai->erase); + last_erase_time, rc, cmp_res &4 , + &ai->erase); } }
@@ -574,6 +612,8 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,return -ENOMEM ; aeb->ec = ec; + aeb->last_erase_time = last_erase_time; + aeb->rc = rc; aeb->pnum = pnum; aeb->vol_id = vol_id; aeb->lnum = lnum;
@@ -650,6 +690,8 @@ void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av)* @ai: attaching information * @pnum: physical eraseblock number to erase; * @ec: erase counter value to write (%UBI_UNKNOWN if it is unknown) +* @last_erase_time: last erase time stamp (%UBI_UNKNOWN if it +* is unknown) * * This function erases physical eraseblock 'pnum', and writes the erase * counter header to it. This function should only be used on UBI device
@@ -658,7 +700,8 @@ void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av)* case of failure. */ static int early_erase_peb (struct ubi_device *ubi, -const struct ubi_attach_info *ai,int pnum,int ec) +const struct ubi_attach_info *ai, +int pnum,int ec,long last_erase_time) {int err;struct ubi_ec_hdr *ec_hdr;
@@ -677,7 +720,7 @@ static int early_erase_peb(struct ubi_device *ubi,return -ENOMEM ; ec_hdr->ec =cpu_to_be64 (ec); - + ec_hdr->last_erase_time =cpu_to_be64 (last_erase_time); err =ubi_io_sync_erase (ubi, pnum,0 );if (err <0 ) goto out_free;
@@ -708,6 +751,7 @@ struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, {int err =0 ;struct ubi_ainf_peb *aeb, *tmp_aeb; +struct timeval tv;if (!list_empty (&ai->free)) { aeb = list_entry(ai->free.next, struct ubi_ainf_peb, u.list);
@@ -722,15 +766,20 @@ struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi,* so forth. We don't want to take care about bad eraseblocks here - * they'll be handled later. */ +do_gettimeofday (&tv);list_for_each_entry_safe (aeb, tmp_aeb, &ai->erase, u.list) {if (aeb->ec ==UBI_UNKNOWN ) aeb->ec = ai->mean_ec; - err =early_erase_peb (ubi, ai, aeb->pnum, aeb->ec+1 ); +/* The last erase time resolution is in days */ + err =early_erase_peb (ubi, ai, aeb->pnum, + aeb->ec+1 , tv.tv_sec /NUM_SEC_IN_DAY );if (err)continue ; aeb->ec +=1 ; + aeb->last_erase_time = tv.tv_sec /NUM_SEC_IN_DAY ; + aeb->rc =0 ;list_del (&aeb->u.list);dbg_bld ("return PEB %d, EC %d" , aeb->pnum, aeb->ec);return aeb;
@@ -817,6 +866,8 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, int *vid, unsigned long long *sqnum) { long long uninitialized_var(ec); +long long uninitialized_var (rc ); +long long uninitialized_var (last_erase_time );int err, bitflips =0 , vol_id =-1 , ec_err =0 ;dbg_bld ("scan PEB %d" , pnum);
@@ -842,11 +893,13 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,case UBI_IO_FF : ai->empty_peb_count +=1 ;return add_to_list (ai, pnum,UBI_UNKNOWN ,UBI_UNKNOWN , -UBI_UNKNOWN ,0 , &ai->erase); +UBI_UNKNOWN ,UBI_UNKNOWN ,UBI_UNKNOWN , +0 , &ai->erase);case UBI_IO_FF_BITFLIPS : ai->empty_peb_count +=1 ;return add_to_list (ai, pnum,UBI_UNKNOWN ,UBI_UNKNOWN , -UBI_UNKNOWN ,1 , &ai->erase); +UBI_UNKNOWN ,UBI_UNKNOWN ,UBI_UNKNOWN , +1 , &ai->erase);case UBI_IO_BAD_HDR_EBADMSG :case UBI_IO_BAD_HDR :/*
@@ -856,6 +909,8 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,*/ ec_err = err; ec =UBI_UNKNOWN ; + last_erase_time =UBI_UNKNOWN ; + rc =UBI_UNKNOWN ; bitflips =1 ;break ;default :
@@ -874,6 +929,16 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, } ec =be64_to_cpu (ech->ec); + last_erase_time =be64_to_cpu (ech->last_erase_time); +/* +* Default value for read counter should be 0. If this is a +* free or erased peb, the counter has no meaning. +* If this peb is used, later code will schedule the peb for +* scrubbing. We can afford erasing all used blocks in this +* case as this is a rear case, and not doing so might have +* destructive implication on the system. +*/ + rc =0 ;if (ec >UBI_MAX_ERASECOUNTER ) {/* * Erase counter overflow. The EC headers have 64 bits
@@ -957,29 +1022,32 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,else if (!err)/* This corruption is caused by a power cut */ err =add_to_list (ai, pnum,UBI_UNKNOWN , -UBI_UNKNOWN , ec,1 , &ai->erase); +UBI_UNKNOWN , ec, last_erase_time, rc, +1 , &ai->erase);else /* This is an unexpected corruption */ - err =add_corrupted (ai, pnum, ec); + err =add_corrupted (ai, pnum, ec, rc, last_erase_time);if (err)return err; - goto adjust_mean_ec; + goto adjust_mean_av_stat;case UBI_IO_FF_BITFLIPS : err =add_to_list (ai, pnum,UBI_UNKNOWN ,UBI_UNKNOWN , - ec,1 , &ai->erase); + ec, last_erase_time, rc,1 , &ai->erase);if (err)return err; - goto adjust_mean_ec; + goto adjust_mean_av_stat;case UBI_IO_FF :if (ec_err || bitflips) err =add_to_list (ai, pnum,UBI_UNKNOWN , -UBI_UNKNOWN , ec,1 , &ai->erase); +UBI_UNKNOWN , ec, last_erase_time, rc, +1 , &ai->erase);else err =add_to_list (ai, pnum,UBI_UNKNOWN , -UBI_UNKNOWN , ec,0 , &ai->free); +UBI_UNKNOWN , ec, last_erase_time,0 , +0 , &ai->free);if (err)return err; - goto adjust_mean_ec; + goto adjust_mean_av_stat;default :ubi_err ("'ubi_io_read_vid_hdr()' returned unknown code %d" , err);
@@ -1003,7 +1071,8 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, vol_id, lnum); } err =add_to_list (ai, pnum, vol_id, lnum, - ec,1 , &ai->erase); + ec, last_erase_time, + rc,1 , &ai->erase);if (err)return err;return 0 ;
@@ -1018,7 +1087,8 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,ubi_msg ("\"preserve\" compatible internal volume %d:%d found" , vol_id, lnum); err =add_to_list (ai, pnum, vol_id, lnum, - ec,0 , &ai->alien); + ec, last_erase_time, + rc,0 , &ai->alien);if (err)return err;return 0 ;
@@ -1033,11 +1103,13 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,if (ec_err)ubi_warn ("valid VID header but corrupted EC header at PEB %d" , pnum); - err =ubi_add_to_av (ubi, ai, pnum, ec, vidh, bitflips); + + err =ubi_add_to_av (ubi, ai, pnum, ec, last_erase_time, +UBI_DEF_RD_THRESHOLD , vidh, bitflips);if (err)return err; -adjust_mean_ec: +adjust_mean_av_stat:if (!ec_err) { ai->ec_sum += ec; ai->ec_count +=1 ;
@@ -1045,6 +1117,8 @@ adjust_mean_ec: ai->max_ec = ec;if (ec < ai->min_ec) ai->min_ec = ec; + ai->last_erase_time_sum += last_erase_time; + ai->last_erase_time_count++; }return 0 ;
@@ -1254,6 +1328,10 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai,if (ai->ec_count) ai->mean_ec =div_u64 (ai->ec_sum, ai->ec_count); +if (ai->last_erase_time_count) + ai->mean_last_erase_time =div_u64 (ai->last_erase_time_sum, + ai->last_erase_time_count); + err =late_analysis (ubi, ai);if (err) goto out_vidh;
@@ -1264,22 +1342,17 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai,*/ ubi_rb_for_each_entry (rb1, av, &ai->volumes, rb) {ubi_rb_for_each_entry (rb2, aeb, &av->root, u.rb) -if (aeb->ec ==UBI_UNKNOWN ) - aeb->ec = ai->mean_ec; +set_aeb_default_values (aeb, ai); } -list_for_each_entry (aeb, &ai->free, u.list) { -if (aeb->ec ==UBI_UNKNOWN ) - aeb->ec = ai->mean_ec; - } +list_for_each_entry (aeb, &ai->free, u.list) +set_aeb_default_values (aeb, ai);list_for_each_entry (aeb, &ai->corr, u.list) -if (aeb->ec ==UBI_UNKNOWN ) - aeb->ec = ai->mean_ec; +set_aeb_default_values (aeb, ai);list_for_each_entry (aeb, &ai->erase, u.list) -if (aeb->ec ==UBI_UNKNOWN ) - aeb->ec = ai->mean_ec; +set_aeb_default_values (aeb, ai); err =self_check_ai (ubi, ai);if (err)
drivers/mtd/ubi/debug.c +11 -0
@@ -1,5 +1,8 @@/* * Copyright (c) International Business Machines Corp., 2006 +* Copyright (c) 2014, Linux Foundation. All rights reserved. +* Linux Foundation chooses to take subject only to the GPLv2 +* license terms, and distributes only under these terms. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by
@@ -47,6 +50,14 @@ void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len) err, len, pnum, offset, read); goto out; } +if (ubi->lookuptbl) { +if (ubi->lookuptbl[pnum]->rc <UBI_MAX_READCOUNTER ) + ubi->lookuptbl[pnum]->rc++; +else +ubi_err ("read counter overflow at PEB %d, RC %d" , + pnum, ubi->lookuptbl[pnum]->rc); + }else +ubi_err ("Can't update RC. No lookuptbl" );ubi_msg ("dumping %d bytes of data from PEB %d, offset %d" , len, pnum, offset);
drivers/mtd/ubi/fastmap.c +103 -15
324 lines not on this page · raw message
drivers/mtd/ubi/io.c +28 -0
@@ -1,6 +1,9 @@/* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2006, 2007 +* Copyright (c) 2014, Linux Foundation. All rights reserved. +* Linux Foundation chooses to take subject only to the GPLv2 +* license terms, and distributes only under these terms. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by
@@ -204,6 +207,14 @@ retry: } }else {ubi_assert (len == read); +if (ubi->lookuptbl && ubi->lookuptbl[pnum]) { +if (ubi->lookuptbl[pnum]->rc < +UBI_MAX_READCOUNTER ) + ubi->lookuptbl[pnum]->rc++; +else +ubi_err ("read counter overflow at PEB %d, RC %d" , + pnum, ubi->lookuptbl[pnum]->rc); + }if (ubi_dbg_is_bitflip (ubi)) {dbg_gen ("bit-flip (emulated)" );
@@ -1337,6 +1348,15 @@ static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum,if (err && !mtd_is_bitflip (err)) goto out_free; +if (ubi->lookuptbl && ubi->lookuptbl[pnum]) { +if (ubi->lookuptbl[pnum]->rc <UBI_MAX_READCOUNTER ) + ubi->lookuptbl[pnum]->rc++; +else +ubi_err ("read counter overflow at PEB %d, RC %d" , + pnum, ubi->lookuptbl[pnum]->rc); + }else +ubi_err ("Can't update RC. No lookuptbl" ); +for (i =0 ; i < len; i++) {uint8_t c = ((uint8_t *)buf)[i];uint8_t c1 = ((uint8_t *)buf1)[i];
@@ -1403,6 +1423,14 @@ int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len) err, len, pnum, offset, read); goto error; } +if (ubi->lookuptbl && ubi->lookuptbl[pnum]) { +if (ubi->lookuptbl[pnum]->rc <UBI_MAX_READCOUNTER ) + ubi->lookuptbl[pnum]->rc++; +else +ubi_err ("read counter overflow at PEB %d, RC %d" , + pnum, ubi->lookuptbl[pnum]->rc); + }else +ubi_err ("Can't update RC. No lookuptbl" ); err =ubi_check_pattern (buf,0xFF , len);if (err ==0 ) {
drivers/mtd/ubi/ubi.h +23 -1
@@ -95,6 +95,16 @@#define UBI_RD_THRESHOLD 100000/* +* This is the default read counter to be assigned to blocks lacking +* read counter value on attach. The value was choosen as mean between +* just_erased_block (rc = 0) and needs_scrubbibg_block +* (rc = UBI_RD_THRESHOLD). On the one hand we don't want to miss +* blocks that needs scrubbing but on the other, we dont want to +* abuse scrubbing. +*/ +#define UBI_DEF_RD_THRESHOLD (UBI_RD_THRESHOLD / 2) + +/* * This parameter defines the maximun interval (in days) between two * erasures of an eraseblock. When this interval is reached, UBI starts * performing wear leveling by means of moving data from eraseblock with
@@ -102,6 +112,9 @@*/ #define UBI_DT_THRESHOLD 120 +/* Used when calculaing the lats erase timestamp of a PEB */ +#define NUM_SEC_IN_DAY (60*60*24) +/* * The UBI debugfs directory name pattern and maximum name length (3 for "ubi" * + 2 for the number plus 1 for the trailing zero byte.
@@ -709,6 +722,11 @@ struct ubi_ainf_volume {* @ec_sum: a temporary variable used when calculating @mean_ec * @ec_count: a temporary variable used when calculating @mean_ec * @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects +* @mean_last_erase_time: mean late erase timestamp value +* @last_erase_time_sum: temporary variable, used to calculate +* @mean_last_erase_time +* @last_erase_time_count: temporary variable, used to calculate +* @mean_last_erase_time * * This data structure contains the result of attaching an MTD device and may * be used by other UBI sub-systems to build final UBI data structures, further
@@ -735,6 +753,9 @@ struct ubi_attach_info {uint64_t ec_sum;int ec_count;struct kmem_cache *aeb_slab_cache; +long long mean_last_erase_time; +long long last_erase_time_sum; +int last_erase_time_count; };/**
@@ -775,7 +796,8 @@ extern struct blocking_notifier_head ubi_notifiers;/* attach.c */ int ubi_add_to_av (struct ubi_device *ubi,struct ubi_attach_info *ai,int pnum, -int ec,const struct ubi_vid_hdr *vid_hdr,int bitflips); +int ec,long last_erase_time,long rc, +const struct ubi_vid_hdr *vid_hdr,int bitflips);struct ubi_ainf_volume *ubi_find_av (const struct ubi_attach_info *ai,int vol_id);void ubi_remove_av (struct ubi_attach_info *ai,struct ubi_ainf_volume *av);
drivers/mtd/ubi/vtbl.c +5 -1
@@ -1,6 +1,9 @@/* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2006, 2007 +* Copyright (c) 2014, Linux Foundation. All rights reserved. +* Linux Foundation chooses to take subject only to the GPLv2 +* license terms, and distributes only under these terms. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by
@@ -339,7 +342,8 @@ retry:* And add it to the attaching information. Don't delete the old version * of this LEB as it will be deleted and freed in 'ubi_add_to_av()'. */ - err =ubi_add_to_av (ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr,0 ); + err =ubi_add_to_av (ubi, ai, new_aeb->pnum, new_aeb->ec, + new_aeb->last_erase_time, new_aeb->rc, vid_hdr,0 );kmem_cache_free (ai->aeb_slab_cache, new_aeb);ubi_free_vid_hdr (ubi, vid_hdr);return err;
drivers/mtd/ubi/wl.c +47 -0
94 lines not on this page · raw message
Signature · 3 lines
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project