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);
/*
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;
}
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;
* 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);
*/
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;
* 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;
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();
*/
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);