]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - drivers/mtd/ubi/wl.c
mtd: ubi: Fix worker handling
[people/ms/u-boot.git] / drivers / mtd / ubi / wl.c
index 102309016ac43d7a83de295218c4987b19da6c40..0de2a4a5f8d8e6ade8fc2f286d52e3f7e601848e 100644 (file)
@@ -86,7 +86,6 @@
  * room for future re-works of the WL sub-system.
  */
 
-#define __UBOOT__
 #ifndef __UBOOT__
 #include <linux/slab.h>
 #include <linux/crc32.h>
@@ -97,6 +96,7 @@
 #endif
 
 #include "ubi.h"
+#include "wl.h"
 
 /* Number of physical eraseblocks reserved for wear-leveling purposes */
 #define WL_RESERVED_PEBS 1
@@ -134,44 +134,6 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi,
 static int self_check_in_pq(const struct ubi_device *ubi,
                            struct ubi_wl_entry *e);
 
-#ifdef CONFIG_MTD_UBI_FASTMAP
-#ifndef __UBOOT__
-/**
- * update_fastmap_work_fn - calls ubi_update_fastmap from a work queue
- * @wrk: the work description object
- */
-static void update_fastmap_work_fn(struct work_struct *wrk)
-{
-       struct ubi_device *ubi = container_of(wrk, struct ubi_device, fm_work);
-       ubi_update_fastmap(ubi);
-}
-#endif
-
-/**
- *  ubi_ubi_is_fm_block - returns 1 if a PEB is currently used in a fastmap.
- *  @ubi: UBI device description object
- *  @pnum: the to be checked PEB
- */
-static int ubi_is_fm_block(struct ubi_device *ubi, int pnum)
-{
-       int i;
-
-       if (!ubi->fm)
-               return 0;
-
-       for (i = 0; i < ubi->fm->used_blocks; i++)
-               if (ubi->fm->e[i]->pnum == pnum)
-                       return 1;
-
-       return 0;
-}
-#else
-static int ubi_is_fm_block(struct ubi_device *ubi, int pnum)
-{
-       return 0;
-}
-#endif
-
 /**
  * wl_tree_add - add a wear-leveling entry to a WL RB-tree.
  * @e: the wear-leveling entry to add
@@ -208,6 +170,20 @@ static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root)
        rb_insert_color(&e->u.rb, root);
 }
 
+/**
+ * wl_tree_destroy - destroy a wear-leveling entry.
+ * @ubi: UBI device description object
+ * @e: the wear-leveling entry to add
+ *
+ * This function destroys a wear leveling entry and removes
+ * the reference from the lookup table.
+ */
+static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e)
+{
+       ubi->lookuptbl[e->pnum] = NULL;
+       kmem_cache_free(ubi_wl_entry_slab, e);
+}
+
 /**
  * do_work - do one pending work.
  * @ubi: UBI device description object
@@ -249,39 +225,12 @@ static int do_work(struct ubi_device *ubi)
         */
        err = wrk->func(ubi, wrk, 0);
        if (err)
-               ubi_err("work failed with error code %d", err);
+               ubi_err(ubi, "work failed with error code %d", err);
        up_read(&ubi->work_sem);
 
        return err;
 }
 
-/**
- * produce_free_peb - produce a free physical eraseblock.
- * @ubi: UBI device description object
- *
- * This function tries to make a free PEB by means of synchronous execution of
- * pending works. This may be needed if, for example the background thread is
- * disabled. Returns zero in case of success and a negative error code in case
- * of failure.
- */
-static int produce_free_peb(struct ubi_device *ubi)
-{
-       int err;
-
-       while (!ubi->free.rb_node) {
-               spin_unlock(&ubi->wl_lock);
-
-               dbg_wl("do one work synchronously");
-               err = do_work(ubi);
-
-               spin_lock(&ubi->wl_lock);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
 /**
  * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree.
  * @e: the wear-leveling entry to check
@@ -405,119 +354,32 @@ static struct ubi_wl_entry *find_mean_wl_entry(struct ubi_device *ubi,
        if (last->ec - first->ec < WL_FREE_MAX_DIFF) {
                e = rb_entry(root->rb_node, struct ubi_wl_entry, u.rb);
 
-#ifdef CONFIG_MTD_UBI_FASTMAP
                /* If no fastmap has been written and this WL entry can be used
                 * as anchor PEB, hold it back and return the second best
                 * WL entry such that fastmap can use the anchor PEB later. */
-               if (e && !ubi->fm_disabled && !ubi->fm &&
-                   e->pnum < UBI_FM_MAX_START)
-                       e = rb_entry(rb_next(root->rb_node),
-                                    struct ubi_wl_entry, u.rb);
-#endif
+               e = may_reserve_for_fm(ubi, e, root);
        } else
                e = find_wl_entry(ubi, root, WL_FREE_MAX_DIFF/2);
 
        return e;
 }
 
-#ifdef CONFIG_MTD_UBI_FASTMAP
-/**
- * find_anchor_wl_entry - find wear-leveling entry to used as anchor PEB.
- * @root: the RB-tree where to look for
- */
-static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root)
-{
-       struct rb_node *p;
-       struct ubi_wl_entry *e, *victim = NULL;
-       int max_ec = UBI_MAX_ERASECOUNTER;
-
-       ubi_rb_for_each_entry(p, e, root, u.rb) {
-               if (e->pnum < UBI_FM_MAX_START && e->ec < max_ec) {
-                       victim = e;
-                       max_ec = e->ec;
-               }
-       }
-
-       return victim;
-}
-
-static int anchor_pebs_avalible(struct rb_root *root)
-{
-       struct rb_node *p;
-       struct ubi_wl_entry *e;
-
-       ubi_rb_for_each_entry(p, e, root, u.rb)
-               if (e->pnum < UBI_FM_MAX_START)
-                       return 1;
-
-       return 0;
-}
-
-/**
- * ubi_wl_get_fm_peb - find a physical erase block with a given maximal number.
- * @ubi: UBI device description object
- * @anchor: This PEB will be used as anchor PEB by fastmap
- *
- * The function returns a physical erase block with a given maximal number
- * and removes it from the wl subsystem.
- * Must be called with wl_lock held!
- */
-struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor)
-{
-       struct ubi_wl_entry *e = NULL;
-
-       if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1))
-               goto out;
-
-       if (anchor)
-               e = find_anchor_wl_entry(&ubi->free);
-       else
-               e = find_mean_wl_entry(ubi, &ubi->free);
-
-       if (!e)
-               goto out;
-
-       self_check_in_wl_tree(ubi, e, &ubi->free);
-
-       /* remove it from the free list,
-        * the wl subsystem does no longer know this erase block */
-       rb_erase(&e->u.rb, &ubi->free);
-       ubi->free_count--;
-out:
-       return e;
-}
-#endif
-
 /**
- * __wl_get_peb - get a physical eraseblock.
+ * wl_get_wle - get a mean wl entry to be used by ubi_wl_get_peb() or
+ * refill_wl_user_pool().
  * @ubi: UBI device description object
  *
- * This function returns a physical eraseblock in case of success and a
- * negative error code in case of failure.
+ * This function returns a a wear leveling entry in case of success and
+ * NULL in case of failure.
  */
-static int __wl_get_peb(struct ubi_device *ubi)
+static struct ubi_wl_entry *wl_get_wle(struct ubi_device *ubi)
 {
-       int err;
        struct ubi_wl_entry *e;
 
-retry:
-       if (!ubi->free.rb_node) {
-               if (ubi->works_count == 0) {
-                       ubi_err("no free eraseblocks");
-                       ubi_assert(list_empty(&ubi->works));
-                       return -ENOSPC;
-               }
-
-               err = produce_free_peb(ubi);
-               if (err < 0)
-                       return err;
-               goto retry;
-       }
-
        e = find_mean_wl_entry(ubi, &ubi->free);
        if (!e) {
-               ubi_err("no free eraseblocks");
-               return -ENOSPC;
+               ubi_err(ubi, "no free eraseblocks");
+               return NULL;
        }
 
        self_check_in_wl_tree(ubi, e, &ubi->free);
@@ -529,178 +391,10 @@ retry:
        rb_erase(&e->u.rb, &ubi->free);
        ubi->free_count--;
        dbg_wl("PEB %d EC %d", e->pnum, e->ec);
-#ifndef CONFIG_MTD_UBI_FASTMAP
-       /* We have to enqueue e only if fastmap is disabled,
-        * is fastmap enabled prot_queue_add() will be called by
-        * ubi_wl_get_peb() after removing e from the pool. */
-       prot_queue_add(ubi, e);
-#endif
-       return e->pnum;
-}
-
-#ifdef CONFIG_MTD_UBI_FASTMAP
-/**
- * return_unused_pool_pebs - returns unused PEB to the free tree.
- * @ubi: UBI device description object
- * @pool: fastmap pool description object
- */
-static void return_unused_pool_pebs(struct ubi_device *ubi,
-                                   struct ubi_fm_pool *pool)
-{
-       int i;
-       struct ubi_wl_entry *e;
-
-       for (i = pool->used; i < pool->size; i++) {
-               e = ubi->lookuptbl[pool->pebs[i]];
-               wl_tree_add(e, &ubi->free);
-               ubi->free_count++;
-       }
-}
-
-/**
- * refill_wl_pool - refills all the fastmap pool used by the
- * WL sub-system.
- * @ubi: UBI device description object
- */
-static void refill_wl_pool(struct ubi_device *ubi)
-{
-       struct ubi_wl_entry *e;
-       struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
-
-       return_unused_pool_pebs(ubi, pool);
-
-       for (pool->size = 0; pool->size < pool->max_size; pool->size++) {
-               if (!ubi->free.rb_node ||
-                  (ubi->free_count - ubi->beb_rsvd_pebs < 5))
-                       break;
-
-               e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
-               self_check_in_wl_tree(ubi, e, &ubi->free);
-               rb_erase(&e->u.rb, &ubi->free);
-               ubi->free_count--;
-
-               pool->pebs[pool->size] = e->pnum;
-       }
-       pool->used = 0;
-}
-
-/**
- * refill_wl_user_pool - refills all the fastmap pool used by ubi_wl_get_peb.
- * @ubi: UBI device description object
- */
-static void refill_wl_user_pool(struct ubi_device *ubi)
-{
-       struct ubi_fm_pool *pool = &ubi->fm_pool;
-
-       return_unused_pool_pebs(ubi, pool);
-
-       for (pool->size = 0; pool->size < pool->max_size; pool->size++) {
-               pool->pebs[pool->size] = __wl_get_peb(ubi);
-               if (pool->pebs[pool->size] < 0)
-                       break;
-       }
-       pool->used = 0;
-}
-
-/**
- * ubi_refill_pools - refills all fastmap PEB pools.
- * @ubi: UBI device description object
- */
-void ubi_refill_pools(struct ubi_device *ubi)
-{
-       spin_lock(&ubi->wl_lock);
-       refill_wl_pool(ubi);
-       refill_wl_user_pool(ubi);
-       spin_unlock(&ubi->wl_lock);
-}
-
-/* ubi_wl_get_peb - works exaclty like __wl_get_peb but keeps track of
- * the fastmap pool.
- */
-int ubi_wl_get_peb(struct ubi_device *ubi)
-{
-       int ret;
-       struct ubi_fm_pool *pool = &ubi->fm_pool;
-       struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
-
-       if (!pool->size || !wl_pool->size || pool->used == pool->size ||
-           wl_pool->used == wl_pool->size)
-               ubi_update_fastmap(ubi);
-
-       /* we got not a single free PEB */
-       if (!pool->size)
-               ret = -ENOSPC;
-       else {
-               spin_lock(&ubi->wl_lock);
-               ret = pool->pebs[pool->used++];
-               prot_queue_add(ubi, ubi->lookuptbl[ret]);
-               spin_unlock(&ubi->wl_lock);
-       }
-
-       return ret;
-}
-
-/* get_peb_for_wl - returns a PEB to be used internally by the WL sub-system.
- *
- * @ubi: UBI device description object
- */
-static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
-{
-       struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
-       int pnum;
-
-       if (pool->used == pool->size || !pool->size) {
-               /* We cannot update the fastmap here because this
-                * function is called in atomic context.
-                * Let's fail here and refill/update it as soon as possible. */
-#ifndef __UBOOT__
-               schedule_work(&ubi->fm_work);
-#else
-               /* In U-Boot we must call this directly */
-               ubi_update_fastmap(ubi);
-#endif
-               return NULL;
-       } else {
-               pnum = pool->pebs[pool->used++];
-               return ubi->lookuptbl[pnum];
-       }
-}
-#else
-static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
-{
-       struct ubi_wl_entry *e;
-
-       e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
-       self_check_in_wl_tree(ubi, e, &ubi->free);
-       ubi->free_count--;
-       ubi_assert(ubi->free_count >= 0);
-       rb_erase(&e->u.rb, &ubi->free);
 
        return e;
 }
 
-int ubi_wl_get_peb(struct ubi_device *ubi)
-{
-       int peb, err;
-
-       spin_lock(&ubi->wl_lock);
-       peb = __wl_get_peb(ubi);
-       spin_unlock(&ubi->wl_lock);
-
-       if (peb < 0)
-               return peb;
-
-       err = ubi_self_check_all_ff(ubi, peb, ubi->vid_hdr_aloffset,
-                                   ubi->peb_size - ubi->vid_hdr_aloffset);
-       if (err) {
-               ubi_err("new PEB %d does not contain all 0xFF bytes", peb);
-               return err;
-       }
-
-       return peb;
-}
-#endif
-
 /**
  * prot_queue_del - remove a physical eraseblock from the protection queue.
  * @ubi: UBI device description object
@@ -761,7 +455,7 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
                 * Erase counter overflow. Upgrade UBI and use 64-bit
                 * erase counters internally.
                 */
-               ubi_err("erase counter overflow at PEB %d, EC %llu",
+               ubi_err(ubi, "erase counter overflow at PEB %d, EC %llu",
                        e->pnum, ec);
                err = -EINVAL;
                goto out_free;
@@ -830,13 +524,40 @@ repeat:
        spin_unlock(&ubi->wl_lock);
 }
 
+#ifdef __UBOOT__
+void ubi_do_worker(struct ubi_device *ubi)
+{
+       int err;
+
+       if (list_empty(&ubi->works) || ubi->ro_mode ||
+           !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi))
+               return;
+
+       spin_lock(&ubi->wl_lock);
+       while (!list_empty(&ubi->works)) {
+               /*
+                * call do_work, which executes exactly one work form the queue,
+                * including removeing it from the work queue.
+                */
+               spin_unlock(&ubi->wl_lock);
+               err = do_work(ubi);
+               spin_lock(&ubi->wl_lock);
+               if (err) {
+                       ubi_err(ubi, "%s: work failed with error code %d",
+                               ubi->bgt_name, err);
+               }
+       }
+       spin_unlock(&ubi->wl_lock);
+}
+#endif
+
 /**
  * __schedule_ubi_work - schedule a work.
  * @ubi: UBI device description object
  * @wrk: the work to schedule
  *
  * This function adds a work defined by @wrk to the tail of the pending works
- * list. Can only be used of ubi->work_sem is already held in read mode!
+ * list. Can only be used if ubi->work_sem is already held in read mode!
  */
 static void __schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
 {
@@ -847,12 +568,6 @@ static void __schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
 #ifndef __UBOOT__
        if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled(ubi))
                wake_up_process(ubi->bgt_thread);
-#else
-       /*
-        * U-Boot special: We have no bgt_thread in U-Boot!
-        * So just call do_work() here directly.
-        */
-       do_work(ubi);
 #endif
        spin_unlock(&ubi->wl_lock);
 }
@@ -873,18 +588,7 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
 }
 
 static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
-                       int cancel);
-
-#ifdef CONFIG_MTD_UBI_FASTMAP
-/**
- * ubi_is_erase_work - checks whether a work is erase work.
- * @wrk: The work object to be checked
- */
-int ubi_is_erase_work(struct ubi_work *wrk)
-{
-       return wrk->func == erase_worker;
-}
-#endif
+                       int shutdown);
 
 /**
  * schedule_erase - schedule an erase work.
@@ -903,7 +607,6 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
        struct ubi_work *wl_wrk;
 
        ubi_assert(e);
-       ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
 
        dbg_wl("schedule erasure of PEB %d, EC %d, torture %d",
               e->pnum, e->ec, torture);
@@ -919,6 +622,10 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
        wl_wrk->torture = torture;
 
        schedule_ubi_work(ubi, wl_wrk);
+
+#ifdef __UBOOT__
+       ubi_do_worker(ubi);
+#endif
        return 0;
 }
 
@@ -950,66 +657,22 @@ static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
        return erase_worker(ubi, wl_wrk, 0);
 }
 
-#ifdef CONFIG_MTD_UBI_FASTMAP
-/**
- * ubi_wl_put_fm_peb - returns a PEB used in a fastmap to the wear-leveling
- * sub-system.
- * see: ubi_wl_put_peb()
- *
- * @ubi: UBI device description object
- * @fm_e: physical eraseblock to return
- * @lnum: the last used logical eraseblock number for the PEB
- * @torture: if this physical eraseblock has to be tortured
- */
-int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e,
-                     int lnum, int torture)
-{
-       struct ubi_wl_entry *e;
-       int vol_id, pnum = fm_e->pnum;
-
-       dbg_wl("PEB %d", pnum);
-
-       ubi_assert(pnum >= 0);
-       ubi_assert(pnum < ubi->peb_count);
-
-       spin_lock(&ubi->wl_lock);
-       e = ubi->lookuptbl[pnum];
-
-       /* This can happen if we recovered from a fastmap the very
-        * first time and writing now a new one. In this case the wl system
-        * has never seen any PEB used by the original fastmap.
-        */
-       if (!e) {
-               e = fm_e;
-               ubi_assert(e->ec >= 0);
-               ubi->lookuptbl[pnum] = e;
-       } else {
-               e->ec = fm_e->ec;
-               kfree(fm_e);
-       }
-
-       spin_unlock(&ubi->wl_lock);
-
-       vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID;
-       return schedule_erase(ubi, e, vol_id, lnum, torture);
-}
-#endif
-
 /**
  * wear_leveling_worker - wear-leveling worker function.
  * @ubi: UBI device description object
  * @wrk: the work object
- * @cancel: non-zero if the worker has to free memory and exit
+ * @shutdown: non-zero if the worker has to free memory and exit
+ * because the WL-subsystem is shutting down
  *
  * This function copies a more worn out physical eraseblock to a less worn out
  * one. Returns zero in case of success and a negative error code in case of
  * failure.
  */
 static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
-                               int cancel)
+                               int shutdown)
 {
        int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
-       int vol_id = -1, uninitialized_var(lnum);
+       int vol_id = -1, lnum = -1;
 #ifdef CONFIG_MTD_UBI_FASTMAP
        int anchor = wrk->anchor;
 #endif
@@ -1017,7 +680,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
        struct ubi_vid_hdr *vid_hdr;
 
        kfree(wrk);
-       if (cancel)
+       if (shutdown)
                return 0;
 
        vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
@@ -1145,7 +808,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
                        goto out_not_moved;
                }
 
-               ubi_err("error %d while reading VID header from PEB %d",
+               ubi_err(ubi, "error %d while reading VID header from PEB %d",
                        err, e1->pnum);
                goto out_error;
        }
@@ -1189,7 +852,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
                         * UBI from trying to move it over and over again.
                         */
                        if (ubi->erroneous_peb_count > ubi->max_erroneous) {
-                               ubi_err("too many erroneous eraseblocks (%d)",
+                               ubi_err(ubi, "too many erroneous eraseblocks (%d)",
                                        ubi->erroneous_peb_count);
                                goto out_error;
                        }
@@ -1205,7 +868,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
 
        /* The PEB has been successfully moved */
        if (scrubbing)
-               ubi_msg("scrubbed PEB %d (LEB %d:%d), data moved to PEB %d",
+               ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d",
                        e1->pnum, vol_id, lnum, e2->pnum);
        ubi_free_vid_hdr(ubi, vid_hdr);
 
@@ -1220,9 +883,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
 
        err = do_sync_erase(ubi, e1, vol_id, lnum, 0);
        if (err) {
-               kmem_cache_free(ubi_wl_entry_slab, e1);
                if (e2)
-                       kmem_cache_free(ubi_wl_entry_slab, e2);
+                       wl_entry_destroy(ubi, e2);
                goto out_ro;
        }
 
@@ -1234,10 +896,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
                dbg_wl("PEB %d (LEB %d:%d) was put meanwhile, erase",
                       e2->pnum, vol_id, lnum);
                err = do_sync_erase(ubi, e2, vol_id, lnum, 0);
-               if (err) {
-                       kmem_cache_free(ubi_wl_entry_slab, e2);
+               if (err)
                        goto out_ro;
-               }
        }
 
        dbg_wl("done");
@@ -1273,19 +933,18 @@ out_not_moved:
 
        ubi_free_vid_hdr(ubi, vid_hdr);
        err = do_sync_erase(ubi, e2, vol_id, lnum, torture);
-       if (err) {
-               kmem_cache_free(ubi_wl_entry_slab, e2);
+       if (err)
                goto out_ro;
-       }
+
        mutex_unlock(&ubi->move_mutex);
        return 0;
 
 out_error:
        if (vol_id != -1)
-               ubi_err("error %d while moving PEB %d to PEB %d",
+               ubi_err(ubi, "error %d while moving PEB %d to PEB %d",
                        err, e1->pnum, e2->pnum);
        else
-               ubi_err("error %d while moving PEB %d (LEB %d:%d) to PEB %d",
+               ubi_err(ubi, "error %d while moving PEB %d (LEB %d:%d) to PEB %d",
                        err, e1->pnum, vol_id, lnum, e2->pnum);
        spin_lock(&ubi->wl_lock);
        ubi->move_from = ubi->move_to = NULL;
@@ -1293,8 +952,8 @@ out_error:
        spin_unlock(&ubi->wl_lock);
 
        ubi_free_vid_hdr(ubi, vid_hdr);
-       kmem_cache_free(ubi_wl_entry_slab, e1);
-       kmem_cache_free(ubi_wl_entry_slab, e2);
+       wl_entry_destroy(ubi, e1);
+       wl_entry_destroy(ubi, e2);
 
 out_ro:
        ubi_ro_mode(ubi);
@@ -1368,8 +1027,15 @@ static int ensure_wear_leveling(struct ubi_device *ubi, int nested)
        wrk->func = &wear_leveling_worker;
        if (nested)
                __schedule_ubi_work(ubi, wrk);
+#ifndef __UBOOT__
        else
                schedule_ubi_work(ubi, wrk);
+#else
+       else {
+               schedule_ubi_work(ubi, wrk);
+               ubi_do_worker(ubi);
+       }
+#endif
        return err;
 
 out_cancel:
@@ -1380,43 +1046,12 @@ out_unlock:
        return err;
 }
 
-#ifdef CONFIG_MTD_UBI_FASTMAP
-/**
- * ubi_ensure_anchor_pebs - schedule wear-leveling to produce an anchor PEB.
- * @ubi: UBI device description object
- */
-int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
-{
-       struct ubi_work *wrk;
-
-       spin_lock(&ubi->wl_lock);
-       if (ubi->wl_scheduled) {
-               spin_unlock(&ubi->wl_lock);
-               return 0;
-       }
-       ubi->wl_scheduled = 1;
-       spin_unlock(&ubi->wl_lock);
-
-       wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
-       if (!wrk) {
-               spin_lock(&ubi->wl_lock);
-               ubi->wl_scheduled = 0;
-               spin_unlock(&ubi->wl_lock);
-               return -ENOMEM;
-       }
-
-       wrk->anchor = 1;
-       wrk->func = &wear_leveling_worker;
-       schedule_ubi_work(ubi, wrk);
-       return 0;
-}
-#endif
-
 /**
  * erase_worker - physical eraseblock erase worker function.
  * @ubi: UBI device description object
  * @wl_wrk: the work object
- * @cancel: non-zero if the worker has to free memory and exit
+ * @shutdown: non-zero if the worker has to free memory and exit
+ * because the WL sub-system is shutting down
  *
  * This function erases a physical eraseblock and perform torture testing if
  * needed. It also takes care about marking the physical eraseblock bad if
@@ -1424,7 +1059,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
  * failure.
  */
 static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
-                       int cancel)
+                       int shutdown)
 {
        struct ubi_wl_entry *e = wl_wrk->e;
        int pnum = e->pnum;
@@ -1432,18 +1067,16 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
        int lnum = wl_wrk->lnum;
        int err, available_consumed = 0;
 
-       if (cancel) {
+       if (shutdown) {
                dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
                kfree(wl_wrk);
-               kmem_cache_free(ubi_wl_entry_slab, e);
+               wl_entry_destroy(ubi, e);
                return 0;
        }
 
        dbg_wl("erase PEB %d EC %d LEB %d:%d",
               pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum);
 
-       ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
-
        err = sync_erase(ubi, e, wl_wrk->torture);
        if (!err) {
                /* Fine, we've erased it successfully */
@@ -1465,7 +1098,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
                return err;
        }
 
-       ubi_err("failed to erase PEB %d, error %d", pnum, err);
+       ubi_err(ubi, "failed to erase PEB %d, error %d", pnum, err);
        kfree(wl_wrk);
 
        if (err == -EINTR || err == -ENOMEM || err == -EAGAIN ||
@@ -1481,7 +1114,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
                return err;
        }
 
-       kmem_cache_free(ubi_wl_entry_slab, e);
+       wl_entry_destroy(ubi, e);
        if (err != -EIO)
                /*
                 * If this is not %-EIO, we have no idea what to do. Scheduling
@@ -1493,7 +1126,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
        /* It is %-EIO, the PEB went bad */
 
        if (!ubi->bad_allowed) {
-               ubi_err("bad physical eraseblock %d detected", pnum);
+               ubi_err(ubi, "bad physical eraseblock %d detected", pnum);
                goto out_ro;
        }
 
@@ -1501,7 +1134,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
        if (ubi->beb_rsvd_pebs == 0) {
                if (ubi->avail_pebs == 0) {
                        spin_unlock(&ubi->volumes_lock);
-                       ubi_err("no reserved/available physical eraseblocks");
+                       ubi_err(ubi, "no reserved/available physical eraseblocks");
                        goto out_ro;
                }
                ubi->avail_pebs -= 1;
@@ -1509,7 +1142,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
        }
        spin_unlock(&ubi->volumes_lock);
 
-       ubi_msg("mark PEB %d as bad", pnum);
+       ubi_msg(ubi, "mark PEB %d as bad", pnum);
        err = ubi_io_mark_bad(ubi, pnum);
        if (err)
                goto out_ro;
@@ -1530,11 +1163,12 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
        ubi->good_peb_count -= 1;
        ubi_calculate_reserved(ubi);
        if (available_consumed)
-               ubi_warn("no PEBs in the reserved pool, used an available PEB");
+               ubi_warn(ubi, "no PEBs in the reserved pool, used an available PEB");
        else if (ubi->beb_rsvd_pebs)
-               ubi_msg("%d PEBs left in the reserve", ubi->beb_rsvd_pebs);
+               ubi_msg(ubi, "%d PEBs left in the reserve",
+                       ubi->beb_rsvd_pebs);
        else
-               ubi_warn("last PEB from the reserve was used");
+               ubi_warn(ubi, "last PEB from the reserve was used");
        spin_unlock(&ubi->volumes_lock);
 
        return err;
@@ -1572,6 +1206,8 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
        ubi_assert(pnum >= 0);
        ubi_assert(pnum < ubi->peb_count);
 
+       down_read(&ubi->fm_protect);
+
 retry:
        spin_lock(&ubi->wl_lock);
        e = ubi->lookuptbl[pnum];
@@ -1602,6 +1238,7 @@ retry:
                ubi_assert(!ubi->move_to_put);
                ubi->move_to_put = 1;
                spin_unlock(&ubi->wl_lock);
+               up_read(&ubi->fm_protect);
                return 0;
        } else {
                if (in_wl_tree(e, &ubi->used)) {
@@ -1620,9 +1257,10 @@ retry:
                } else {
                        err = prot_queue_del(ubi, e->pnum);
                        if (err) {
-                               ubi_err("PEB %d not found", pnum);
+                               ubi_err(ubi, "PEB %d not found", pnum);
                                ubi_ro_mode(ubi);
                                spin_unlock(&ubi->wl_lock);
+                               up_read(&ubi->fm_protect);
                                return err;
                        }
                }
@@ -1636,6 +1274,7 @@ retry:
                spin_unlock(&ubi->wl_lock);
        }
 
+       up_read(&ubi->fm_protect);
        return err;
 }
 
@@ -1653,7 +1292,7 @@ int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum)
 {
        struct ubi_wl_entry *e;
 
-       ubi_msg("schedule PEB %d for scrubbing", pnum);
+       ubi_msg(ubi, "schedule PEB %d for scrubbing", pnum);
 
 retry:
        spin_lock(&ubi->wl_lock);
@@ -1685,7 +1324,7 @@ retry:
 
                err = prot_queue_del(ubi, e->pnum);
                if (err) {
-                       ubi_err("PEB %d not found", pnum);
+                       ubi_err(ubi, "PEB %d not found", pnum);
                        ubi_ro_mode(ubi);
                        spin_unlock(&ubi->wl_lock);
                        return err;
@@ -1727,12 +1366,12 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum)
               vol_id, lnum, ubi->works_count);
 
        while (found) {
-               struct ubi_work *wrk;
+               struct ubi_work *wrk, *tmp;
                found = 0;
 
                down_read(&ubi->work_sem);
                spin_lock(&ubi->wl_lock);
-               list_for_each_entry(wrk, &ubi->works, list) {
+               list_for_each_entry_safe(wrk, tmp, &ubi->works, list) {
                        if ((vol_id == UBI_ALL || wrk->vol_id == vol_id) &&
                            (lnum == UBI_ALL || wrk->lnum == lnum)) {
                                list_del(&wrk->list);
@@ -1767,9 +1406,10 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum)
 
 /**
  * tree_destroy - destroy an RB-tree.
+ * @ubi: UBI device description object
  * @root: the root of the tree to destroy
  */
-static void tree_destroy(struct rb_root *root)
+static void tree_destroy(struct ubi_device *ubi, struct rb_root *root)
 {
        struct rb_node *rb;
        struct ubi_wl_entry *e;
@@ -1791,7 +1431,7 @@ static void tree_destroy(struct rb_root *root)
                                        rb->rb_right = NULL;
                        }
 
-                       kmem_cache_free(ubi_wl_entry_slab, e);
+                       wl_entry_destroy(ubi, e);
                }
        }
 }
@@ -1805,7 +1445,7 @@ int ubi_thread(void *u)
        int failures = 0;
        struct ubi_device *ubi = u;
 
-       ubi_msg("background thread \"%s\" started, PID %d",
+       ubi_msg(ubi, "background thread \"%s\" started, PID %d",
                ubi->bgt_name, task_pid_nr(current));
 
        set_freezable();
@@ -1830,14 +1470,14 @@ int ubi_thread(void *u)
 
                err = do_work(ubi);
                if (err) {
-                       ubi_err("%s: work failed with error code %d",
+                       ubi_err(ubi, "%s: work failed with error code %d",
                                ubi->bgt_name, err);
                        if (failures++ > WL_MAX_FAILURES) {
                                /*
                                 * Too many failures, disable the thread and
                                 * switch to read-only mode.
                                 */
-                               ubi_msg("%s: %d consecutive failures",
+                               ubi_msg(ubi, "%s: %d consecutive failures",
                                        ubi->bgt_name, WL_MAX_FAILURES);
                                ubi_ro_mode(ubi);
                                ubi->thread_enabled = 0;
@@ -1854,11 +1494,18 @@ int ubi_thread(void *u)
 }
 
 /**
- * cancel_pending - cancel all pending works.
+ * shutdown_work - shutdown all pending works.
  * @ubi: UBI device description object
  */
-static void cancel_pending(struct ubi_device *ubi)
+static void shutdown_work(struct ubi_device *ubi)
 {
+#ifdef CONFIG_MTD_UBI_FASTMAP
+#ifndef __UBOOT__
+       flush_work(&ubi->fm_work);
+#else
+       /* in U-Boot, we have all work done */
+#endif
+#endif
        while (!list_empty(&ubi->works)) {
                struct ubi_work *wrk;
 
@@ -1892,11 +1539,6 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
        init_rwsem(&ubi->work_sem);
        ubi->max_ec = ai->max_ec;
        INIT_LIST_HEAD(&ubi->works);
-#ifndef __UBOOT__
-#ifdef CONFIG_MTD_UBI_FASTMAP
-       INIT_WORK(&ubi->fm_work, update_fastmap_work_fn);
-#endif
-#endif
 
        sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
 
@@ -1909,6 +1551,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
                INIT_LIST_HEAD(&ubi->pq[i]);
        ubi->pq_head = 0;
 
+       ubi->free_count = 0;
        list_for_each_entry_safe(aeb, tmp, &ai->erase, u.list) {
                cond_resched();
 
@@ -1918,17 +1561,15 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
 
                e->pnum = aeb->pnum;
                e->ec = aeb->ec;
-               ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
                ubi->lookuptbl[e->pnum] = e;
                if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) {
-                       kmem_cache_free(ubi_wl_entry_slab, e);
+                       wl_entry_destroy(ubi, e);
                        goto out_free;
                }
 
                found_pebs++;
        }
 
-       ubi->free_count = 0;
        list_for_each_entry(aeb, &ai->free, u.list) {
                cond_resched();
 
@@ -1939,7 +1580,6 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
                e->pnum = aeb->pnum;
                e->ec = aeb->ec;
                ubi_assert(e->ec >= 0);
-               ubi_assert(!ubi_is_fm_block(ubi, e->pnum));
 
                wl_tree_add(e, &ubi->free);
                ubi->free_count++;
@@ -1977,23 +1617,26 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
 
        dbg_wl("found %i PEBs", found_pebs);
 
-       if (ubi->fm)
-               ubi_assert(ubi->good_peb_count == \
+       if (ubi->fm) {
+               ubi_assert(ubi->good_peb_count ==
                           found_pebs + ubi->fm->used_blocks);
+
+               for (i = 0; i < ubi->fm->used_blocks; i++) {
+                       e = ubi->fm->e[i];
+                       ubi->lookuptbl[e->pnum] = e;
+               }
+       }
        else
                ubi_assert(ubi->good_peb_count == found_pebs);
 
        reserved_pebs = WL_RESERVED_PEBS;
-#ifdef CONFIG_MTD_UBI_FASTMAP
-       /* Reserve enough LEBs to store two fastmaps. */
-       reserved_pebs += (ubi->fm_size / ubi->leb_size) * 2;
-#endif
+       ubi_fastmap_init(ubi, &reserved_pebs);
 
        if (ubi->avail_pebs < reserved_pebs) {
-               ubi_err("no enough physical eraseblocks (%d, need %d)",
+               ubi_err(ubi, "no enough physical eraseblocks (%d, need %d)",
                        ubi->avail_pebs, reserved_pebs);
                if (ubi->corr_peb_count)
-                       ubi_err("%d PEBs are corrupted and not used",
+                       ubi_err(ubi, "%d PEBs are corrupted and not used",
                                ubi->corr_peb_count);
                goto out_free;
        }
@@ -2008,10 +1651,10 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
        return 0;
 
 out_free:
-       cancel_pending(ubi);
-       tree_destroy(&ubi->used);
-       tree_destroy(&ubi->free);
-       tree_destroy(&ubi->scrub);
+       shutdown_work(ubi);
+       tree_destroy(ubi, &ubi->used);
+       tree_destroy(ubi, &ubi->free);
+       tree_destroy(ubi, &ubi->scrub);
        kfree(ubi->lookuptbl);
        return err;
 }
@@ -2028,7 +1671,7 @@ static void protection_queue_destroy(struct ubi_device *ubi)
        for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) {
                list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) {
                        list_del(&e->u.list);
-                       kmem_cache_free(ubi_wl_entry_slab, e);
+                       wl_entry_destroy(ubi, e);
                }
        }
 }
@@ -2040,12 +1683,13 @@ static void protection_queue_destroy(struct ubi_device *ubi)
 void ubi_wl_close(struct ubi_device *ubi)
 {
        dbg_wl("close the WL sub-system");
-       cancel_pending(ubi);
+       ubi_fastmap_close(ubi);
+       shutdown_work(ubi);
        protection_queue_destroy(ubi);
-       tree_destroy(&ubi->used);
-       tree_destroy(&ubi->erroneous);
-       tree_destroy(&ubi->free);
-       tree_destroy(&ubi->scrub);
+       tree_destroy(ubi, &ubi->used);
+       tree_destroy(ubi, &ubi->erroneous);
+       tree_destroy(ubi, &ubi->free);
+       tree_destroy(ubi, &ubi->scrub);
        kfree(ubi->lookuptbl);
 }
 
@@ -2081,8 +1725,8 @@ static int self_check_ec(struct ubi_device *ubi, int pnum, int ec)
 
        read_ec = be64_to_cpu(ec_hdr->ec);
        if (ec != read_ec && read_ec - ec > 1) {
-               ubi_err("self-check failed for PEB %d", pnum);
-               ubi_err("read EC is %lld, should be %d", read_ec, ec);
+               ubi_err(ubi, "self-check failed for PEB %d", pnum);
+               ubi_err(ubi, "read EC is %lld, should be %d", read_ec, ec);
                dump_stack();
                err = 1;
        } else
@@ -2111,7 +1755,7 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi,
        if (in_wl_tree(e, root))
                return 0;
 
-       ubi_err("self-check failed for PEB %d, EC %d, RB-tree %p ",
+       ubi_err(ubi, "self-check failed for PEB %d, EC %d, RB-tree %p ",
                e->pnum, e->ec, root);
        dump_stack();
        return -EINVAL;
@@ -2139,8 +1783,99 @@ static int self_check_in_pq(const struct ubi_device *ubi,
                        if (p == e)
                                return 0;
 
-       ubi_err("self-check failed for PEB %d, EC %d, Protect queue",
+       ubi_err(ubi, "self-check failed for PEB %d, EC %d, Protect queue",
                e->pnum, e->ec);
        dump_stack();
        return -EINVAL;
 }
+#ifndef CONFIG_MTD_UBI_FASTMAP
+static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
+{
+       struct ubi_wl_entry *e;
+
+       e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF);
+       self_check_in_wl_tree(ubi, e, &ubi->free);
+       ubi->free_count--;
+       ubi_assert(ubi->free_count >= 0);
+       rb_erase(&e->u.rb, &ubi->free);
+
+       return e;
+}
+
+/**
+ * produce_free_peb - produce a free physical eraseblock.
+ * @ubi: UBI device description object
+ *
+ * This function tries to make a free PEB by means of synchronous execution of
+ * pending works. This may be needed if, for example the background thread is
+ * disabled. Returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+static int produce_free_peb(struct ubi_device *ubi)
+{
+       int err;
+
+       while (!ubi->free.rb_node && ubi->works_count) {
+               spin_unlock(&ubi->wl_lock);
+
+               dbg_wl("do one work synchronously");
+               err = do_work(ubi);
+
+               spin_lock(&ubi->wl_lock);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+/**
+ * ubi_wl_get_peb - get a physical eraseblock.
+ * @ubi: UBI device description object
+ *
+ * This function returns a physical eraseblock in case of success and a
+ * negative error code in case of failure.
+ * Returns with ubi->fm_eba_sem held in read mode!
+ */
+int ubi_wl_get_peb(struct ubi_device *ubi)
+{
+       int err;
+       struct ubi_wl_entry *e;
+
+retry:
+       down_read(&ubi->fm_eba_sem);
+       spin_lock(&ubi->wl_lock);
+       if (!ubi->free.rb_node) {
+               if (ubi->works_count == 0) {
+                       ubi_err(ubi, "no free eraseblocks");
+                       ubi_assert(list_empty(&ubi->works));
+                       spin_unlock(&ubi->wl_lock);
+                       return -ENOSPC;
+               }
+
+               err = produce_free_peb(ubi);
+               if (err < 0) {
+                       spin_unlock(&ubi->wl_lock);
+                       return err;
+               }
+               spin_unlock(&ubi->wl_lock);
+               up_read(&ubi->fm_eba_sem);
+               goto retry;
+
+       }
+       e = wl_get_wle(ubi);
+       prot_queue_add(ubi, e);
+       spin_unlock(&ubi->wl_lock);
+
+       err = ubi_self_check_all_ff(ubi, e->pnum, ubi->vid_hdr_aloffset,
+                                   ubi->peb_size - ubi->vid_hdr_aloffset);
+       if (err) {
+               ubi_err(ubi, "new PEB %d does not contain all 0xFF bytes", e->pnum);
+               return err;
+       }
+
+       return e->pnum;
+}
+#else
+#include "fastmap-wl.c"
+#endif