[Drbd-dev] DRBD-8: BUG when disk write errors occur during heavy I/O
Graham, Simon
Simon.Graham at stratus.com
Sat Jan 6 06:15:36 CET 2007
We have encountered a BUG crash when inserting real disk errors during I/O as follows:
drbd0: drbd_md_sync_page_io(,8191929s,WRITE) failed!
drbd0: Notified peer that my disk is broken.
Jan 5 06:24:09 1:0:28:0: rejecting I/O to dead device
drbd0: got an _req_mod() errno of -5
drbd0: Local WRITE failed sec=675944s size=4096
tennille kernel: drbd0: got an _req_mod() errno of -5
------------[ cut here ]------------
kernel BUG at /test_logs/builds/SuperNova/trunk/070105/platform/drbd/src/drbd/lru_cache.c:120!
This is actually in this code:
struct lc_element* lc_find(struct lru_cache* lc, unsigned int enr)
{
struct hlist_node *n;
struct lc_element *e;
BUG_ON(!lc);
called from
void drbd_al_complete_io(struct Drbd_Conf *mdev, sector_t sector)
{
...
spin_lock_irqsave(&mdev->al_lock,flags);
extent = lc_find(mdev->act_log,enr);
So the act_log field was NULL when the lc_find executed.
Now, I believe the following is what happened:
1. We had a write error in the meta-data region of the disk -- the code I added a while back forces
the error to be processed and will change the state to Diskless. This code path blocks waiting
for the mdev->local_cnt to reach zero (which it isn't because there's a bunch of I/O outstanding)
2. The last outstanding local write completes (either with or without an error) and we end up running
req_mod with write_completed_with_error or completed_ok. This code does a dec_local() BEFORE calling
req_may_be_done -- thus it's entirely possible for the stalled code from above that is waiting
for local_cnt to reach zero will run and release the act_log and resync data.
3. Now the req_may_be_done() call for the last I/O is called which calls drbd_al_complete_io which
calls lc_find which BUGs because act_log is now NULL.
Now, it seems to me there are a couple of ways to fix this:
1. We could delay calling dec_local() until all the code that might reference fields in the mdev is
done -- i.e. after _req_may_be_done is called - I'm worried this might cause problems though.
2. Change drbd_al_complete_io to check act_log inside the spin lock. Also change after_state_change
to acquire the spinlock before freeing act_log and resync AND any other places that use act_log and
resync to check for NULL. I'd be worried about finding all the possible places with this fix.
So -- I'm looking for guidance on the best way to fix this sycnronization issue
Thanks,
Simon
More information about the drbd-dev
mailing list