START, LAST, static inline, raid1_rb);
static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
- struct serial_info *si, int idx)
+ struct serial_info *si)
{
unsigned long flags;
int ret = 0;
sector_t lo = r1_bio->sector;
sector_t hi = lo + r1_bio->sectors - 1;
+ int idx = sector_to_idx(r1_bio->sector);
struct serial_in_rdev *serial = &rdev->serial[idx];
+ struct serial_info *head_si;
spin_lock_irqsave(&serial->serial_lock, flags);
/* collision happened */
- if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
+ head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
+ if (head_si && head_si != si) {
+ si->start = lo;
+ si->last = hi;
+ si->wnode_start = head_si->wnode_start;
+ list_add_tail(&si->list_node, &head_si->waiters);
ret = -EBUSY;
- else {
+ } else if (!head_si) {
si->start = lo;
si->last = hi;
+ si->wnode_start = si->start;
raid1_rb_insert(si, &serial->serial_rb);
}
spin_unlock_irqrestore(&serial->serial_lock, flags);
{
struct mddev *mddev = rdev->mddev;
struct serial_info *si;
- int idx = sector_to_idx(r1_bio->sector);
- struct serial_in_rdev *serial = &rdev->serial[idx];
if (WARN_ON(!mddev->serial_info_pool))
return;
si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
- wait_event(serial->serial_io_wait,
- check_and_add_serial(rdev, r1_bio, si, idx) == 0);
+ INIT_LIST_HEAD(&si->waiters);
+ INIT_LIST_HEAD(&si->list_node);
+ init_completion(&si->ready);
+ while (check_and_add_serial(rdev, r1_bio, si)) {
+ wait_for_completion(&si->ready);
+ reinit_completion(&si->ready);
+ }
}
static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
{
- struct serial_info *si;
+ struct serial_info *si, *iter_si;
unsigned long flags;
int found = 0;
struct mddev *mddev = rdev->mddev;
for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
si; si = raid1_rb_iter_next(si, lo, hi)) {
if (si->start == lo && si->last == hi) {
- raid1_rb_remove(si, &serial->serial_rb);
- mempool_free(si, mddev->serial_info_pool);
found = 1;
break;
}
}
- if (!found)
+ if (found) {
+ raid1_rb_remove(si, &serial->serial_rb);
+ if (!list_empty(&si->waiters)) {
+ list_for_each_entry(iter_si, &si->waiters, list_node) {
+ if (iter_si->wnode_start == si->wnode_start) {
+ list_del_init(&iter_si->list_node);
+ list_splice_init(&si->waiters, &iter_si->waiters);
+ raid1_rb_insert(iter_si, &serial->serial_rb);
+ complete(&iter_si->ready);
+ break;
+ }
+ }
+ }
+ mempool_free(si, mddev->serial_info_pool);
+ } else {
WARN(1, "The write IO is not recorded for serialization\n");
+ }
spin_unlock_irqrestore(&serial->serial_lock, flags);
- wake_up(&serial->serial_io_wait);
}
/*