diff options
author | NeilBrown <neilb@suse.de> | 2008-11-24 11:09:58 +1100 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2008-11-24 11:09:58 +1100 |
commit | 6ac9b8824fd2f82fff508eecc5c74cf797e93cb2 (patch) | |
tree | 00e4693d0406733894bb3deefb987e730377c954 /include/linux/raid | |
parent | dcf260ac14668380fdecb5c2456ae735645653a4 (diff) |
md: make devices disappear when they are no longer needed.
Currently md devices, once created, never disappear until the module
is unloaded. This is essentially because the gendisk holds a
reference to the mddev, and the mddev holds a reference to the
gendisk, this a circular reference.
If we drop the reference from mddev to gendisk, then we need to ensure
that the mddev is destroyed when the gendisk is destroyed. However it
is not possible to hook into the gendisk destruction process to enable
this.
So we drop the reference from the gendisk to the mddev and destroy the
gendisk when the mddev gets destroyed. However this has a
complication.
Between the call
__blkdev_get->get_gendisk->kobj_lookup->md_probe
and the call
__blkdev_get->md_open
there is no obvious way to hold a reference on the mddev any more, so
unless something is done, it will disappear and gendisk will be
destroyed prematurely.
Also, once we decide to destroy the mddev, there will be an unlockable
moment before the gendisk is unlinked (blk_unregister_region) during
which a new reference to the gendisk can be created. We need to
ensure that this reference can not be used. i.e. the ->open must
fail.
So:
1/ in md_probe we set a flag in the mddev (hold_active) which
indicates that the array should be treated as active, even
though there are no references, and no appearance of activity.
This is cleared by md_release when the device is closed.
This ensure that the gendisk will survive between md_probe and
md_open.
2/ In md_open we check if the mddev we expect to open matches
the gendisk that we did open.
If there is a mismatch we return -ERESTARTSYS and modify
__blkdev_get to retry from the top in that case.
In the -ERESTARTSYS sys case we make sure to wait until
the old gendisk (that we succeeded in opening) is really gone so
we loop at most once.
If an inactive md device is opened and closed in quick succession, a
gendisk will be created and destroyed repeatedly. If this happens
often, udev can easily get a backlog of ADD/DELETE requests. However
this is not normally the case (and it does work, just slowly).
Normally the first open of an MD device will involve initialising it
(SET_ARRAY_INFO / NEW_DISK) which will cause it to appear active and
not be destroyed.
When an array is stopped, the gendisk will remain around until the
last open is closed, then it will disappear.
As an array can be stopped by writing to a sysfs attribute
echo clear > /sys/block/mdXXX/md/array_state
we need to use scheduled work for deleting the gendisk and other
kobjects. This allows us to wait for any pending gendisk deletion to
complete by simply calling flush_scheduled_work().
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'include/linux/raid')
-rw-r--r-- | include/linux/raid/md_k.h | 4 |
1 files changed, 4 insertions, 0 deletions
diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 8f9a54c1fb0e..7b6029bfba80 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -137,6 +137,8 @@ struct mddev_s struct gendisk *gendisk; struct kobject kobj; + int hold_active; +#define UNTIL_CLOSE 1 /* Superblock information */ int major_version, @@ -246,6 +248,8 @@ struct mddev_s */ struct sysfs_dirent *sysfs_action; /* handle for 'sync_action' */ + struct work_struct del_work; /* used for delayed sysfs removal */ + spinlock_t write_lock; wait_queue_head_t sb_wait; /* for waiting on superblock updates */ atomic_t pending_writes; /* number of active superblock writes */ |