Skip to content

Commit

Permalink
UBI: fix ubi_wl_flush
Browse files Browse the repository at this point in the history
The flush function should finish all the pending jobs. But if
somebody else is doing a work, this function should wait and let
it finish.

This patche uses rw semaphore for synchronization purpose - it
just looks quite convinient.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
  • Loading branch information
Artem Bityutskiy authored and Artem Bityutskiy committed Dec 26, 2007
1 parent 458dbb3 commit 593dd33
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 7 deletions.
1 change: 1 addition & 0 deletions drivers/mtd/ubi/ubi.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ struct ubi_device {
} prot;
spinlock_t wl_lock;
struct mutex move_mutex;
struct rw_semaphore work_sem;
int wl_scheduled;
struct ubi_wl_entry **lookuptbl;
unsigned long long abs_ec;
Expand Down
39 changes: 32 additions & 7 deletions drivers/mtd/ubi/wl.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,18 @@ 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;
}

Expand All @@ -275,6 +283,7 @@ static int do_work(struct ubi_device *ubi)
ubi->works_count -= 1;
ubi_assert(ubi->works_count >= 0);
spin_unlock(&ubi->wl_lock);
up_read(&ubi->work_sem);
return err;
}

Expand Down Expand Up @@ -1173,7 +1182,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture)
* 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);
Expand Down Expand Up @@ -1280,17 +1289,32 @@ int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum)
*/
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;
Expand Down Expand Up @@ -1426,6 +1450,7 @@ 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);

Expand Down

0 comments on commit 593dd33

Please sign in to comment.