]> err.no Git - linux-2.6/blobdiff - drivers/mtd/ubi/wl.c
Merge branch 'core/stacktrace' of git://git.kernel.org/pub/scm/linux/kernel/git/tip...
[linux-2.6] / drivers / mtd / ubi / wl.c
index a60f9425ab137c90decdd05bc00cbb38948b416f..a471a491f0ab0c2840543594966640487e7bacd4 100644 (file)
@@ -251,15 +251,24 @@ static int do_work(struct ubi_device *ubi)
 
        cond_resched();
 
+       /*
+        * @ubi->work_sem is used to synchronize with the workers. Workers take
+        * it in read mode, so many of them may be doing works at a time. But
+        * the queue flush code has to be sure the whole queue of works is
+        * done, and it takes the mutex in write mode.
+        */
+       down_read(&ubi->work_sem);
        spin_lock(&ubi->wl_lock);
-
        if (list_empty(&ubi->works)) {
                spin_unlock(&ubi->wl_lock);
+               up_read(&ubi->work_sem);
                return 0;
        }
 
        wrk = list_entry(ubi->works.next, struct ubi_work, list);
        list_del(&wrk->list);
+       ubi->works_count -= 1;
+       ubi_assert(ubi->works_count >= 0);
        spin_unlock(&ubi->wl_lock);
 
        /*
@@ -270,11 +279,8 @@ 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);
+       up_read(&ubi->work_sem);
 
-       spin_lock(&ubi->wl_lock);
-       ubi->works_count -= 1;
-       ubi_assert(ubi->works_count >= 0);
-       spin_unlock(&ubi->wl_lock);
        return err;
 }
 
@@ -737,7 +743,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
                                int cancel)
 {
        int err, put = 0, scrubbing = 0, protect = 0;
-       struct ubi_wl_prot_entry *pe;
+       struct ubi_wl_prot_entry *uninitialized_var(pe);
        struct ubi_wl_entry *e1, *e2;
        struct ubi_vid_hdr *vid_hdr;
 
@@ -1173,7 +1179,7 @@ retry:
                 * the WL unit has not put the PEB to the "used" tree yet, but
                 * it is about to do this. So we just set a flag which will
                 * tell the WL worker that the PEB is not needed anymore and
-                * should be sheduled for erasure.
+                * should be scheduled for erasure.
                 */
                dbg_wl("PEB %d is the target of data moving", pnum);
                ubi_assert(!ubi->move_to_put);
@@ -1280,17 +1286,32 @@ retry:
  */
 int ubi_wl_flush(struct ubi_device *ubi)
 {
-       int err, pending_count;
-
-       pending_count = ubi->works_count;
-
-       dbg_wl("flush (%d pending works)", pending_count);
+       int err;
 
        /*
         * Erase while the pending works queue is not empty, but not more then
         * the number of currently pending works.
         */
-       while (pending_count-- > 0) {
+       dbg_wl("flush (%d pending works)", ubi->works_count);
+       while (ubi->works_count) {
+               err = do_work(ubi);
+               if (err)
+                       return err;
+       }
+
+       /*
+        * Make sure all the works which have been done in parallel are
+        * finished.
+        */
+       down_write(&ubi->work_sem);
+       up_write(&ubi->work_sem);
+
+       /*
+        * And in case last was the WL worker and it cancelled the LEB
+        * movement, flush again.
+        */
+       while (ubi->works_count) {
+               dbg_wl("flush more (%d pending works)", ubi->works_count);
                err = do_work(ubi);
                if (err)
                        return err;
@@ -1334,7 +1355,7 @@ static void tree_destroy(struct rb_root *root)
  * ubi_thread - UBI background thread.
  * @u: the UBI device description object pointer
  */
-static int ubi_thread(void *u)
+int ubi_thread(void *u)
 {
        int failures = 0;
        struct ubi_device *ubi = u;
@@ -1426,23 +1447,16 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
        ubi->prot.pnum = ubi->prot.aec = RB_ROOT;
        spin_lock_init(&ubi->wl_lock);
        mutex_init(&ubi->move_mutex);
+       init_rwsem(&ubi->work_sem);
        ubi->max_ec = si->max_ec;
        INIT_LIST_HEAD(&ubi->works);
 
        sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
 
-       ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
-       if (IS_ERR(ubi->bgt_thread)) {
-               err = PTR_ERR(ubi->bgt_thread);
-               ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
-                       err);
-               return err;
-       }
-
        err = -ENOMEM;
        ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL);
        if (!ubi->lookuptbl)
-               goto out_free;
+               return err;
 
        list_for_each_entry_safe(seb, tmp, &si->erase, u.list) {
                cond_resched();
@@ -1575,10 +1589,6 @@ static void protection_trees_destroy(struct ubi_device *ubi)
  */
 void ubi_wl_close(struct ubi_device *ubi)
 {
-       dbg_wl("disable \"%s\"", ubi->bgt_name);
-       if (ubi->bgt_thread)
-               kthread_stop(ubi->bgt_thread);
-
        dbg_wl("close the UBI wear-leveling unit");
 
        cancel_pending(ubi);