ubi: suspicious calculation in 'ubi_wl_get_peb'
Shmulik Ladkani
shmulik.ladkani at gmail.com
Fri Feb 17 08:38:28 EST 2012
Hi,
Sorry for a long post - this issue requires some detailed background.
It looks like a calculation within 'ubi_wl_get_peb' involved in
picking a candidate PEB is incorrect.
The function 'find_wl_entry(rb_root *root, int max)' finds a
wear-leveling entry closest to a certain erase counter.
The parameter 'max' is documented as "highest possible erase counter",
however it is actually "diff from first entry" - since initially, the
funtion adds first entry's EC to the given 'max':
e = rb_entry(rb_first(root), struct ubi_wl_entry, u.rb);
max += e->ec;
Almost all calls to 'find_wl_entry' follow the (undocumented)
convention, providing "diff from first" argument (where the absolute
'max' value seeked is calculated *internally* by 'find_wl_entry').
E.g. 4 of 5 calls are of the pattern:
e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
However, there is one call where the caller seems to provide an actual
absolute "max" value, instead of a "diff from first".
The call is within 'ubi_wl_get_peb', quote:
case UBI_UNKNOWN:
/*
* For unknown data we pick a physical eraseblock with medium
* erase counter. But we by no means can pick a physical
* eraseblock with erase counter greater or equivalent than the
* lowest erase counter plus %WL_FREE_MAX_DIFF.
*/
first = rb_entry(rb_first(&ubi->free), struct ubi_wl_entry,
u.rb);
last = rb_entry(rb_last(&ubi->free), struct ubi_wl_entry, u.rb);
if (last->ec - first->ec < WL_FREE_MAX_DIFF)
e = rb_entry(ubi->free.rb_node,
struct ubi_wl_entry, u.rb);
else {
medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2;
e = find_wl_entry(&ubi->free, medium_ec);
}
break;
Note calculation of 'medium_ec' - it does no seem as a "diff" value.
The *actual* resulting seeked EC is approximately 1.5*First + Diff/2.
Is it the intended behavior? it does not seem to fit the remark.
Deducting from the "last->ec - first->ec < WL_FREE_MAX_DIFF" condition,
it looks like it should have been:
- e = find_wl_entry(&ubi->free, medium_ec);
+ e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF/2)
Did I get something wrong?
Looking forward for any comments.
Regards,
Shmulik
More information about the linux-mtd
mailing list