Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 84236
b: refs/heads/master
c: 43f9b25
h: refs/heads/master
v: v3
  • Loading branch information
Artem Bityutskiy authored and Artem Bityutskiy committed Dec 26, 2007
1 parent 3188139 commit 1ebd1dd
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 136 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d2c468550915ab2f16149e274a6f0da0b925a748
refs/heads/master: 43f9b25a9cdd7b177f77f026b1461abd1abbd174
145 changes: 100 additions & 45 deletions trunk/drivers/mtd/ubi/eba.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,44 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
return 0;
}

/**
* leb_write_lock - lock logical eraseblock for writing.
* @ubi: UBI device description object
* @vol_id: volume ID
* @lnum: logical eraseblock number
*
* This function locks a logical eraseblock for writing if there is no
* contention and does nothing if there is contention. Returns %0 in case of
* success, %1 in case of contention, and and a negative error code in case of
* failure.
*/
static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
{
int free;
struct ubi_ltree_entry *le;

le = ltree_add_entry(ubi, vol_id, lnum);
if (IS_ERR(le))
return PTR_ERR(le);
if (down_write_trylock(&le->mutex))
return 0;

/* Contention, cancel */
spin_lock(&ubi->ltree_lock);
le->users -= 1;
ubi_assert(le->users >= 0);
if (le->users == 0) {
rb_erase(&le->rb, &ubi->ltree);
free = 1;
} else
free = 0;
spin_unlock(&ubi->ltree_lock);
if (free)
kmem_cache_free(ubi_ltree_slab, le);

return 1;
}

/**
* leb_write_unlock - unlock logical eraseblock.
* @ubi: UBI device description object
Expand Down Expand Up @@ -923,14 +961,16 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
*
* This function copies logical eraseblock from physical eraseblock @from to
* physical eraseblock @to. The @vid_hdr buffer may be changed by this
* function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation
* was canceled because bit-flips were detected at the target PEB, and a
* negative error code in case of failure.
* function. Returns:
* o %0 in case of success;
* o %1 if the operation was canceled and should be tried later (e.g.,
* because a bit-flip was detected at the target PEB);
* o %2 if the volume is being deleted and this LEB should not be moved.
*/
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
struct ubi_vid_hdr *vid_hdr)
{
int err, vol_id, lnum, data_size, aldata_size, pnum, idx;
int err, vol_id, lnum, data_size, aldata_size, idx;
struct ubi_volume *vol;
uint32_t crc;

Expand All @@ -946,57 +986,67 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
data_size = aldata_size =
ubi->leb_size - be32_to_cpu(vid_hdr->data_pad);

/*
* We do not want anybody to write to this logical eraseblock while we
* are moving it, so we lock it.
*/
err = leb_write_lock(ubi, vol_id, lnum);
if (err)
return err;

mutex_lock(&ubi->buf_mutex);

/*
* But the logical eraseblock might have been put by this time.
* Cancel if it is true.
*/
idx = vol_id2idx(ubi, vol_id);

spin_lock(&ubi->volumes_lock);
/*
* We may race with volume deletion/re-size, so we have to hold
* @ubi->volumes_lock.
*
* Note, it is not a problem if we race with volume deletion or re-size
* here. If the volume is deleted or re-sized while we are moving an
* eraseblock which belongs to this volume, we'll end up with finding
* out that this LEB was unmapped at the end (see WL), and drop this
* PEB.
* Note, we may race with volume deletion, which means that the volume
* this logical eraseblock belongs to might be being deleted. Since the
* volume deletion unmaps all the volume's logical eraseblocks, it will
* be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish.
*/
spin_lock(&ubi->volumes_lock);
vol = ubi->volumes[idx];
if (!vol) {
dbg_eba("volume %d was removed meanwhile", vol_id);
/* No need to do further work, cancel */
dbg_eba("volume %d is being removed, cancel", vol_id);
spin_unlock(&ubi->volumes_lock);
goto out_unlock;
return 2;
}
spin_unlock(&ubi->volumes_lock);

pnum = vol->eba_tbl[lnum];
if (pnum != from) {
dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to "
"PEB %d, cancel", vol_id, lnum, from, pnum);
spin_unlock(&ubi->volumes_lock);
goto out_unlock;
/*
* We do not want anybody to write to this logical eraseblock while we
* are moving it, so lock it.
*
* Note, we are using non-waiting locking here, because we cannot sleep
* on the LEB, since it may cause deadlocks. Indeed, imagine a task is
* unmapping the LEB which is mapped to the PEB we are going to move
* (@from). This task locks the LEB and goes sleep in the
* 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are
* holding @ubi->move_mutex and go sleep on the LEB lock. So, if the
* LEB is already locked, we just do not move it and return %1.
*/
err = leb_write_trylock(ubi, vol_id, lnum);
if (err) {
dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum);
return err;
}
spin_unlock(&ubi->volumes_lock);

/* OK, now the LEB is locked and we can safely start moving it */
/*
* The LEB might have been put meanwhile, and the task which put it is
* probably waiting on @ubi->move_mutex. No need to continue the work,
* cancel it.
*/
if (vol->eba_tbl[lnum] != from) {
dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to "
"PEB %d, cancel", vol_id, lnum, from,
vol->eba_tbl[lnum]);
err = 1;
goto out_unlock_leb;
}

/*
* OK, now the LEB is locked and we can safely start moving iy. Since
* this function utilizes thie @ubi->peb1_buf buffer which is shared
* with some other functions, so lock the buffer by taking the
* @ubi->buf_mutex.
*/
mutex_lock(&ubi->buf_mutex);
dbg_eba("read %d bytes of data", aldata_size);
err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size);
if (err && err != UBI_IO_BITFLIPS) {
ubi_warn("error %d while reading data from PEB %d",
err, from);
goto out_unlock;
goto out_unlock_buf;
}

/*
Expand Down Expand Up @@ -1032,7 +1082,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,

err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
if (err)
goto out_unlock;
goto out_unlock_buf;

cond_resched();

Expand All @@ -1041,13 +1091,15 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
if (err) {
if (err != UBI_IO_BITFLIPS)
ubi_warn("cannot read VID header back from PEB %d", to);
goto out_unlock;
else
err = 1;
goto out_unlock_buf;
}

if (data_size > 0) {
err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
if (err)
goto out_unlock;
goto out_unlock_buf;

cond_resched();

Expand All @@ -1061,23 +1113,26 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
if (err != UBI_IO_BITFLIPS)
ubi_warn("cannot read data back from PEB %d",
to);
goto out_unlock;
else
err = 1;
goto out_unlock_buf;
}

cond_resched();

if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) {
ubi_warn("read data back from PEB %d - it is different",
to);
goto out_unlock;
goto out_unlock_buf;
}
}

ubi_assert(vol->eba_tbl[lnum] == from);
vol->eba_tbl[lnum] = to;

out_unlock:
out_unlock_buf:
mutex_unlock(&ubi->buf_mutex);
out_unlock_leb:
leb_write_unlock(ubi, vol_id, lnum);
return err;
}
Expand Down
10 changes: 6 additions & 4 deletions trunk/drivers/mtd/ubi/ubi.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,13 +275,13 @@ struct ubi_wl_entry;
* @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from,
* @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works
* fields
* @move_mutex: serializes eraseblock moves
* @wl_scheduled: non-zero if the wear-leveling was scheduled
* @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any
* physical eraseblock
* @abs_ec: absolute erase counter
* @move_from: physical eraseblock from where the data is being moved
* @move_to: physical eraseblock where the data is being moved to
* @move_from_put: if the "from" PEB was put
* @move_to_put: if the "to" PEB was put
* @works: list of pending works
* @works_count: count of pending works
Expand Down Expand Up @@ -354,12 +354,12 @@ struct ubi_device {
struct rb_root aec;
} prot;
spinlock_t wl_lock;
struct mutex move_mutex;
int wl_scheduled;
struct ubi_wl_entry **lookuptbl;
unsigned long long abs_ec;
struct ubi_wl_entry *move_from;
struct ubi_wl_entry *move_to;
int move_from_put;
int move_to_put;
struct list_head works;
int works_count;
Expand Down Expand Up @@ -561,8 +561,10 @@ static inline int ubi_io_write_data(struct ubi_device *ubi, const void *buf,
*/
static inline void ubi_ro_mode(struct ubi_device *ubi)
{
ubi->ro_mode = 1;
ubi_warn("switch to read-only mode");
if (!ubi->ro_mode) {
ubi->ro_mode = 1;
ubi_warn("switch to read-only mode");
}
}

/**
Expand Down
Loading

0 comments on commit 1ebd1dd

Please sign in to comment.