Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

UBI: modify ubi_wl_flush function to clear work queue for a lnum

This patch modifies ubi_wl_flush to force the erasure of
particular volume id / logical eraseblock number pairs. Previous functionality
is preserved when passing UBI_ALL for both values. The locations where ubi_wl_flush
were called are appropriately changed: ubi_leb_erase only flushes for the
erased LEB, and ubi_create_volume forces only flushing for its volume id.
External code can call this new feature via the new function ubi_flush() added
to kapi.c, which simply passes through to ubi_wl_flush().

This was tested by disabling the call to do_work in ubi thread, which results
in the work queue remaining unless explicitly called to remove. UBIFS was
changed to call ubifs_leb_change 50 times for four different LEBs. Then the
new function was called to clear the queue: passing wrong volume ids / lnum,
correct ones, and finally UBI_ALL for both to ensure it was finally all
cleard. The work queue was dumped each time and the selective removal
of the particular LEB numbers was observed. Extra checks were enabled and
ubifs's integck was also run. Finally, the drive was repeatedly filled and
emptied to ensure that the queue was cleared normally.

Artem: amended the patch.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>

authored by

Joel Reardon and committed by
Artem Bityutskiy
62f38455 05a3cb7d

+70 -31
+1 -1
drivers/mtd/ubi/cdev.c
··· 514 514 if (err) 515 515 break; 516 516 517 - err = ubi_wl_flush(ubi); 517 + err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); 518 518 break; 519 519 } 520 520
+28 -1
drivers/mtd/ubi/kapi.c
··· 551 551 if (err) 552 552 return err; 553 553 554 - return ubi_wl_flush(ubi); 554 + return ubi_wl_flush(ubi, vol->vol_id, lnum); 555 555 } 556 556 EXPORT_SYMBOL_GPL(ubi_leb_erase); 557 557 ··· 703 703 return 0; 704 704 } 705 705 EXPORT_SYMBOL_GPL(ubi_sync); 706 + 707 + /** 708 + * ubi_flush - flush UBI work queue. 709 + * @ubi_num: UBI device to flush work queue 710 + * @vol_id: volume id to flush for 711 + * @lnum: logical eraseblock number to flush for 712 + * 713 + * This function executes all pending works for a particular volume id / logical 714 + * eraseblock number pair. If either value is set to %UBI_ALL, then it acts as 715 + * a wildcard for all of the corresponding volume numbers or logical 716 + * eraseblock numbers. It returns zero in case of success and a negative error 717 + * code in case of failure. 718 + */ 719 + int ubi_flush(int ubi_num, int vol_id, int lnum) 720 + { 721 + struct ubi_device *ubi; 722 + int err = 0; 723 + 724 + ubi = ubi_get_device(ubi_num); 725 + if (!ubi) 726 + return -ENODEV; 727 + 728 + err = ubi_wl_flush(ubi, vol_id, lnum); 729 + ubi_put_device(ubi); 730 + return err; 731 + } 732 + EXPORT_SYMBOL_GPL(ubi_flush); 706 733 707 734 BLOCKING_NOTIFIER_HEAD(ubi_notifiers); 708 735
+1 -1
drivers/mtd/ubi/ubi.h
··· 669 669 int ubi_wl_get_peb(struct ubi_device *ubi); 670 670 int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, 671 671 int pnum, int torture); 672 - int ubi_wl_flush(struct ubi_device *ubi); 672 + int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum); 673 673 int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); 674 674 int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai); 675 675 void ubi_wl_close(struct ubi_device *ubi);
+2 -2
drivers/mtd/ubi/upd.c
··· 147 147 } 148 148 149 149 if (bytes == 0) { 150 - err = ubi_wl_flush(ubi); 150 + err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); 151 151 if (err) 152 152 return err; 153 153 ··· 361 361 362 362 ubi_assert(vol->upd_received <= vol->upd_bytes); 363 363 if (vol->upd_received == vol->upd_bytes) { 364 - err = ubi_wl_flush(ubi); 364 + err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); 365 365 if (err) 366 366 return err; 367 367 /* The update is finished, clear the update marker */
+1 -1
drivers/mtd/ubi/vmt.c
··· 280 280 * Finish all pending erases because there may be some LEBs belonging 281 281 * to the same volume ID. 282 282 */ 283 - err = ubi_wl_flush(ubi); 283 + err = ubi_wl_flush(ubi, vol_id, UBI_ALL); 284 284 if (err) 285 285 goto out_acc; 286 286
+36 -25
drivers/mtd/ubi/wl.c
··· 1241 1241 /** 1242 1242 * ubi_wl_flush - flush all pending works. 1243 1243 * @ubi: UBI device description object 1244 + * @vol_id: the volume id to flush for 1245 + * @lnum: the logical eraseblock number to flush for 1244 1246 * 1245 - * This function returns zero in case of success and a negative error code in 1246 - * case of failure. 1247 + * This function executes all pending works for a particular volume id / 1248 + * logical eraseblock number pair. If either value is set to %UBI_ALL, then it 1249 + * acts as a wildcard for all of the corresponding volume numbers or logical 1250 + * eraseblock numbers. It returns zero in case of success and a negative error 1251 + * code in case of failure. 1247 1252 */ 1248 - int ubi_wl_flush(struct ubi_device *ubi) 1253 + int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum) 1249 1254 { 1250 - int err; 1255 + int err = 0; 1256 + int found = 1; 1251 1257 1252 1258 /* 1253 1259 * Erase while the pending works queue is not empty, but not more than 1254 1260 * the number of currently pending works. 1255 1261 */ 1256 - dbg_wl("flush (%d pending works)", ubi->works_count); 1257 - while (ubi->works_count) { 1258 - err = do_work(ubi); 1259 - if (err) 1260 - return err; 1261 - } 1262 + dbg_wl("flush pending work for LEB %d:%d (%d pending works)", 1263 + vol_id, lnum, ubi->works_count); 1262 1264 1263 - /* 1264 - * Make sure all the works which have been done in parallel are 1265 - * finished. 1266 - */ 1267 1265 down_write(&ubi->work_sem); 1268 - up_write(&ubi->work_sem); 1266 + while (found) { 1267 + struct ubi_work *wrk; 1268 + found = 0; 1269 1269 1270 - /* 1271 - * And in case last was the WL worker and it canceled the LEB 1272 - * movement, flush again. 1273 - */ 1274 - while (ubi->works_count) { 1275 - dbg_wl("flush more (%d pending works)", ubi->works_count); 1276 - err = do_work(ubi); 1277 - if (err) 1278 - return err; 1270 + spin_lock(&ubi->wl_lock); 1271 + list_for_each_entry(wrk, &ubi->works, list) { 1272 + if ((vol_id == UBI_ALL || wrk->vol_id == vol_id) && 1273 + (lnum == UBI_ALL || wrk->lnum == lnum)) { 1274 + list_del(&wrk->list); 1275 + ubi->works_count -= 1; 1276 + ubi_assert(ubi->works_count >= 0); 1277 + spin_unlock(&ubi->wl_lock); 1278 + 1279 + err = wrk->func(ubi, wrk, 0); 1280 + if (err) 1281 + goto out; 1282 + spin_lock(&ubi->wl_lock); 1283 + found = 1; 1284 + break; 1285 + } 1286 + } 1287 + spin_unlock(&ubi->wl_lock); 1279 1288 } 1280 1289 1281 - return 0; 1290 + out: 1291 + up_write(&ubi->work_sem); 1292 + return err; 1282 1293 } 1283 1294 1284 1295 /**
+1
include/linux/mtd/ubi.h
··· 219 219 int ubi_leb_map(struct ubi_volume_desc *desc, int lnum); 220 220 int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); 221 221 int ubi_sync(int ubi_num); 222 + int ubi_flush(int ubi_num, int vol_id, int lnum); 222 223 223 224 /* 224 225 * This function is the same as the 'ubi_leb_read()' function, but it does not