2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
md.c : Multiple Devices driver for Linux
|
|
|
|
Copyright (C) 1998, 1999, 2000 Ingo Molnar
|
|
|
|
|
|
|
|
completely rewritten, based on the MD driver code from Marc Zyngier
|
|
|
|
|
|
|
|
Changes:
|
|
|
|
|
|
|
|
- RAID-1/RAID-5 extensions by Miguel de Icaza, Gadi Oxman, Ingo Molnar
|
|
|
|
- RAID-6 extensions by H. Peter Anvin <hpa@zytor.com>
|
|
|
|
- boot support for linear and striped mode by Harald Hoyer <HarryH@Royal.Net>
|
|
|
|
- kerneld support by Boris Tobotras <boris@xtalk.msk.su>
|
|
|
|
- kmod support by: Cyrus Durgin
|
|
|
|
- RAID0 bugfixes: Mark Anthony Lisher <markal@iname.com>
|
|
|
|
- Devfs support by Richard Gooch <rgooch@atnf.csiro.au>
|
|
|
|
|
|
|
|
- lots of fixes and improvements to the RAID1/RAID5 and generic
|
|
|
|
RAID code (such as request based resynchronization):
|
|
|
|
|
|
|
|
Neil Brown <neilb@cse.unsw.edu.au>.
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
- persistent bitmap code
|
|
|
|
Copyright (C) 2003-2004, Paul Clements, SteelEye Technology, Inc.
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
(for example /usr/src/linux/COPYING); if not, write to the Free
|
|
|
|
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
2005-09-10 06:23:56 +07:00
|
|
|
#include <linux/kthread.h>
|
2009-03-31 10:33:13 +07:00
|
|
|
#include <linux/blkdev.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/sysctl.h>
|
2009-03-31 10:33:13 +07:00
|
|
|
#include <linux/seq_file.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/buffer_head.h> /* for invalidate_bdev */
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
#include <linux/poll.h>
|
2006-06-26 14:27:37 +07:00
|
|
|
#include <linux/ctype.h>
|
2008-10-13 07:55:12 +07:00
|
|
|
#include <linux/hdreg.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/random.h>
|
|
|
|
#include <linux/reboot.h>
|
2005-06-22 07:17:14 +07:00
|
|
|
#include <linux/file.h>
|
2008-10-15 05:09:21 +07:00
|
|
|
#include <linux/delay.h>
|
2009-03-31 10:33:13 +07:00
|
|
|
#include <linux/raid/md_p.h>
|
|
|
|
#include <linux/raid/md_u.h>
|
2009-03-31 10:33:13 +07:00
|
|
|
#include "md.h"
|
2009-03-31 10:27:03 +07:00
|
|
|
#include "bitmap.h"
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#define DEBUG 0
|
|
|
|
#define dprintk(x...) ((void)(DEBUG && printk(x)))
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef MODULE
|
2008-10-13 07:55:12 +07:00
|
|
|
static void autostart_arrays(int part);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
|
2006-01-06 15:20:36 +07:00
|
|
|
static LIST_HEAD(pers_list);
|
2005-04-17 05:20:36 +07:00
|
|
|
static DEFINE_SPINLOCK(pers_lock);
|
|
|
|
|
2006-06-26 14:27:42 +07:00
|
|
|
static void md_print_devices(void);
|
|
|
|
|
2008-05-24 03:04:38 +07:00
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(resync_wait);
|
|
|
|
|
2006-06-26 14:27:42 +07:00
|
|
|
#define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); }
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Current RAID-1,4,5 parallel reconstruction 'guaranteed speed limit'
|
|
|
|
* is 1000 KB/sec, so the extra system load does not show up that much.
|
|
|
|
* Increase it if you want to have more _guaranteed_ speed. Note that
|
2005-09-10 14:26:54 +07:00
|
|
|
* the RAID driver will use the maximum available bandwidth if the IO
|
2005-04-17 05:20:36 +07:00
|
|
|
* subsystem is idle. There is also an 'absolute maximum' reconstruction
|
|
|
|
* speed limit - in case reconstruction slows down your system despite
|
|
|
|
* idle IO detection.
|
|
|
|
*
|
|
|
|
* you can change it via /proc/sys/dev/raid/speed_limit_min and _max.
|
2006-01-06 15:21:36 +07:00
|
|
|
* or /sys/block/mdX/md/sync_speed_{min,max}
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
static int sysctl_speed_limit_min = 1000;
|
|
|
|
static int sysctl_speed_limit_max = 200000;
|
2006-01-06 15:21:36 +07:00
|
|
|
static inline int speed_min(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
return mddev->sync_speed_min ?
|
|
|
|
mddev->sync_speed_min : sysctl_speed_limit_min;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int speed_max(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
return mddev->sync_speed_max ?
|
|
|
|
mddev->sync_speed_max : sysctl_speed_limit_max;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static struct ctl_table_header *raid_table_header;
|
|
|
|
|
|
|
|
static ctl_table raid_table[] = {
|
|
|
|
{
|
|
|
|
.ctl_name = DEV_RAID_SPEED_LIMIT_MIN,
|
|
|
|
.procname = "speed_limit_min",
|
|
|
|
.data = &sysctl_speed_limit_min,
|
|
|
|
.maxlen = sizeof(int),
|
2006-07-10 18:44:18 +07:00
|
|
|
.mode = S_IRUGO|S_IWUSR,
|
2005-04-17 05:20:36 +07:00
|
|
|
.proc_handler = &proc_dointvec,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.ctl_name = DEV_RAID_SPEED_LIMIT_MAX,
|
|
|
|
.procname = "speed_limit_max",
|
|
|
|
.data = &sysctl_speed_limit_max,
|
|
|
|
.maxlen = sizeof(int),
|
2006-07-10 18:44:18 +07:00
|
|
|
.mode = S_IRUGO|S_IWUSR,
|
2005-04-17 05:20:36 +07:00
|
|
|
.proc_handler = &proc_dointvec,
|
|
|
|
},
|
|
|
|
{ .ctl_name = 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static ctl_table raid_dir_table[] = {
|
|
|
|
{
|
|
|
|
.ctl_name = DEV_RAID,
|
|
|
|
.procname = "raid",
|
|
|
|
.maxlen = 0,
|
2006-07-10 18:44:18 +07:00
|
|
|
.mode = S_IRUGO|S_IXUGO,
|
2005-04-17 05:20:36 +07:00
|
|
|
.child = raid_table,
|
|
|
|
},
|
|
|
|
{ .ctl_name = 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static ctl_table raid_root_table[] = {
|
|
|
|
{
|
|
|
|
.ctl_name = CTL_DEV,
|
|
|
|
.procname = "dev",
|
|
|
|
.maxlen = 0,
|
|
|
|
.mode = 0555,
|
|
|
|
.child = raid_dir_table,
|
|
|
|
},
|
|
|
|
{ .ctl_name = 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct block_device_operations md_fops;
|
|
|
|
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
static int start_readonly;
|
|
|
|
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
/*
|
|
|
|
* We have a system wide 'event count' that is incremented
|
|
|
|
* on any 'interesting' event, and readers of /proc/mdstat
|
|
|
|
* can use 'poll' or 'select' to find out when the event
|
|
|
|
* count increases.
|
|
|
|
*
|
|
|
|
* Events are:
|
|
|
|
* start array, stop array, error, add device, remove device,
|
|
|
|
* start build, activate spare
|
|
|
|
*/
|
2006-01-06 15:20:43 +07:00
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(md_event_waiters);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
static atomic_t md_event_count;
|
2006-03-27 16:18:10 +07:00
|
|
|
void md_new_event(mddev_t *mddev)
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
{
|
|
|
|
atomic_inc(&md_event_count);
|
|
|
|
wake_up(&md_event_waiters);
|
|
|
|
}
|
2006-03-27 16:18:10 +07:00
|
|
|
EXPORT_SYMBOL_GPL(md_new_event);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
|
2006-05-31 11:27:13 +07:00
|
|
|
/* Alternate version that can be called from interrupts
|
|
|
|
* when calling sysfs_notify isn't needed.
|
|
|
|
*/
|
2006-06-26 14:28:01 +07:00
|
|
|
static void md_new_event_inintr(mddev_t *mddev)
|
2006-05-31 11:27:13 +07:00
|
|
|
{
|
|
|
|
atomic_inc(&md_event_count);
|
|
|
|
wake_up(&md_event_waiters);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Enables to iterate over all existing md arrays
|
|
|
|
* all_mddevs_lock protects this list.
|
|
|
|
*/
|
|
|
|
static LIST_HEAD(all_mddevs);
|
|
|
|
static DEFINE_SPINLOCK(all_mddevs_lock);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iterates through all used mddevs in the system.
|
|
|
|
* We take care to grab the all_mddevs_lock whenever navigating
|
|
|
|
* the list, and to always hold a refcount when unlocked.
|
|
|
|
* Any code which breaks out of this loop while own
|
|
|
|
* a reference to the current mddev and must mddev_put it.
|
|
|
|
*/
|
2008-02-06 16:39:58 +07:00
|
|
|
#define for_each_mddev(mddev,tmp) \
|
2005-04-17 05:20:36 +07:00
|
|
|
\
|
|
|
|
for (({ spin_lock(&all_mddevs_lock); \
|
|
|
|
tmp = all_mddevs.next; \
|
|
|
|
mddev = NULL;}); \
|
|
|
|
({ if (tmp != &all_mddevs) \
|
|
|
|
mddev_get(list_entry(tmp, mddev_t, all_mddevs));\
|
|
|
|
spin_unlock(&all_mddevs_lock); \
|
|
|
|
if (mddev) mddev_put(mddev); \
|
|
|
|
mddev = list_entry(tmp, mddev_t, all_mddevs); \
|
|
|
|
tmp != &all_mddevs;}); \
|
|
|
|
({ spin_lock(&all_mddevs_lock); \
|
|
|
|
tmp = tmp->next;}) \
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2008-10-13 07:55:12 +07:00
|
|
|
static int md_fail_request(struct request_queue *q, struct bio *bio)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-09-27 17:47:43 +07:00
|
|
|
bio_io_error(bio);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline mddev_t *mddev_get(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
atomic_inc(&mddev->active);
|
|
|
|
return mddev;
|
|
|
|
}
|
|
|
|
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
static void mddev_delayed_delete(struct work_struct *ws)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = container_of(ws, mddev_t, del_work);
|
|
|
|
kobject_del(&mddev->kobj);
|
|
|
|
kobject_put(&mddev->kobj);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void mddev_put(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock))
|
|
|
|
return;
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
if (!mddev->raid_disks && list_empty(&mddev->disks) &&
|
|
|
|
!mddev->hold_active) {
|
2005-04-17 05:20:36 +07:00
|
|
|
list_del(&mddev->all_mddevs);
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
if (mddev->gendisk) {
|
|
|
|
/* we did a probe so need to clean up.
|
|
|
|
* Call schedule_work inside the spinlock
|
|
|
|
* so that flush_scheduled_work() after
|
|
|
|
* mddev_find will succeed in waiting for the
|
|
|
|
* work to be done.
|
|
|
|
*/
|
|
|
|
INIT_WORK(&mddev->del_work, mddev_delayed_delete);
|
|
|
|
schedule_work(&mddev->del_work);
|
|
|
|
} else
|
|
|
|
kfree(mddev);
|
|
|
|
}
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static mddev_t * mddev_find(dev_t unit)
|
|
|
|
{
|
|
|
|
mddev_t *mddev, *new = NULL;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
spin_lock(&all_mddevs_lock);
|
2009-01-09 04:31:10 +07:00
|
|
|
|
|
|
|
if (unit) {
|
|
|
|
list_for_each_entry(mddev, &all_mddevs, all_mddevs)
|
|
|
|
if (mddev->unit == unit) {
|
|
|
|
mddev_get(mddev);
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
kfree(new);
|
|
|
|
return mddev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new) {
|
|
|
|
list_add(&new->all_mddevs, &all_mddevs);
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&all_mddevs_lock);
|
2009-01-09 04:31:10 +07:00
|
|
|
new->hold_active = UNTIL_IOCTL;
|
|
|
|
return new;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-01-09 04:31:10 +07:00
|
|
|
} else if (new) {
|
|
|
|
/* find an unused unit number */
|
|
|
|
static int next_minor = 512;
|
|
|
|
int start = next_minor;
|
|
|
|
int is_free = 0;
|
|
|
|
int dev = 0;
|
|
|
|
while (!is_free) {
|
|
|
|
dev = MKDEV(MD_MAJOR, next_minor);
|
|
|
|
next_minor++;
|
|
|
|
if (next_minor > MINORMASK)
|
|
|
|
next_minor = 0;
|
|
|
|
if (next_minor == start) {
|
|
|
|
/* Oh dear, all in use. */
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
kfree(new);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
is_free = 1;
|
|
|
|
list_for_each_entry(mddev, &all_mddevs, all_mddevs)
|
|
|
|
if (mddev->unit == dev) {
|
|
|
|
is_free = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
new->unit = dev;
|
|
|
|
new->md_minor = MINOR(dev);
|
|
|
|
new->hold_active = UNTIL_STOP;
|
2005-04-17 05:20:36 +07:00
|
|
|
list_add(&new->all_mddevs, &all_mddevs);
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
|
2006-01-06 15:20:32 +07:00
|
|
|
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!new)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
new->unit = unit;
|
|
|
|
if (MAJOR(unit) == MD_MAJOR)
|
|
|
|
new->md_minor = MINOR(unit);
|
|
|
|
else
|
|
|
|
new->md_minor = MINOR(unit) >> MdpMinorShift;
|
|
|
|
|
2006-03-27 16:18:20 +07:00
|
|
|
mutex_init(&new->reconfig_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
INIT_LIST_HEAD(&new->disks);
|
|
|
|
INIT_LIST_HEAD(&new->all_mddevs);
|
|
|
|
init_timer(&new->safemode_timer);
|
|
|
|
atomic_set(&new->active, 1);
|
2008-07-21 14:05:25 +07:00
|
|
|
atomic_set(&new->openers, 0);
|
2005-06-22 07:17:12 +07:00
|
|
|
spin_lock_init(&new->write_lock);
|
2005-06-22 07:17:26 +07:00
|
|
|
init_waitqueue_head(&new->sb_wait);
|
2008-06-06 12:45:53 +07:00
|
|
|
init_waitqueue_head(&new->recovery_wait);
|
2007-05-09 16:35:38 +07:00
|
|
|
new->reshape_position = MaxSector;
|
2008-06-28 05:31:24 +07:00
|
|
|
new->resync_min = 0;
|
2008-02-06 16:39:52 +07:00
|
|
|
new->resync_max = MaxSector;
|
2008-04-30 14:52:29 +07:00
|
|
|
new->level = LEVEL_NONE;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int mddev_lock(mddev_t * mddev)
|
|
|
|
{
|
2006-03-27 16:18:20 +07:00
|
|
|
return mutex_lock_interruptible(&mddev->reconfig_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int mddev_trylock(mddev_t * mddev)
|
|
|
|
{
|
2006-03-27 16:18:20 +07:00
|
|
|
return mutex_trylock(&mddev->reconfig_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void mddev_unlock(mddev_t * mddev)
|
|
|
|
{
|
2006-03-27 16:18:20 +07:00
|
|
|
mutex_unlock(&mddev->reconfig_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-08-23 03:11:08 +07:00
|
|
|
md_wakeup_thread(mddev->thread);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:43 +07:00
|
|
|
static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-01-09 04:31:08 +07:00
|
|
|
mdk_rdev_t *rdev;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rdev->desc_nr == nr)
|
|
|
|
return rdev;
|
2009-01-09 04:31:08 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev)
|
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rdev->bdev->bd_dev == dev)
|
|
|
|
return rdev;
|
2009-01-09 04:31:08 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:51 +07:00
|
|
|
static struct mdk_personality *find_pers(int level, char *clevel)
|
2006-01-06 15:20:36 +07:00
|
|
|
{
|
|
|
|
struct mdk_personality *pers;
|
2006-01-06 15:20:51 +07:00
|
|
|
list_for_each_entry(pers, &pers_list, list) {
|
|
|
|
if (level != LEVEL_NONE && pers->level == level)
|
2006-01-06 15:20:36 +07:00
|
|
|
return pers;
|
2006-01-06 15:20:51 +07:00
|
|
|
if (strcmp(pers->name, clevel)==0)
|
|
|
|
return pers;
|
|
|
|
}
|
2006-01-06 15:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-07-11 19:02:23 +07:00
|
|
|
/* return the offset of the super block in 512byte sectors */
|
2005-07-28 01:46:09 +07:00
|
|
|
static inline sector_t calc_dev_sboffset(struct block_device *bdev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-07-11 19:02:23 +07:00
|
|
|
sector_t num_sectors = bdev->bd_inode->i_size / 512;
|
|
|
|
return MD_NEW_SIZE_SECTORS(num_sectors);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-07-11 19:02:23 +07:00
|
|
|
static sector_t calc_num_sectors(mdk_rdev_t *rdev, unsigned chunk_size)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-07-11 19:02:23 +07:00
|
|
|
sector_t num_sectors = rdev->sb_start;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (chunk_size)
|
2008-07-11 19:02:23 +07:00
|
|
|
num_sectors &= ~((sector_t)chunk_size/512 - 1);
|
|
|
|
return num_sectors;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int alloc_disk_sb(mdk_rdev_t * rdev)
|
|
|
|
{
|
|
|
|
if (rdev->sb_page)
|
|
|
|
MD_BUG();
|
|
|
|
|
|
|
|
rdev->sb_page = alloc_page(GFP_KERNEL);
|
|
|
|
if (!rdev->sb_page) {
|
|
|
|
printk(KERN_ALERT "md: out of memory.\n");
|
2008-07-11 19:02:20 +07:00
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_disk_sb(mdk_rdev_t * rdev)
|
|
|
|
{
|
|
|
|
if (rdev->sb_page) {
|
2006-01-06 15:20:31 +07:00
|
|
|
put_page(rdev->sb_page);
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->sb_loaded = 0;
|
|
|
|
rdev->sb_page = NULL;
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->size = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-27 17:47:43 +07:00
|
|
|
static void super_written(struct bio *bio, int error)
|
2005-06-22 07:17:28 +07:00
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev = bio->bi_private;
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
mddev_t *mddev = rdev->mddev;
|
2005-06-22 07:17:28 +07:00
|
|
|
|
2006-10-03 15:16:03 +07:00
|
|
|
if (error || !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
|
|
|
|
printk("md: super_written gets error=%d, uptodate=%d\n",
|
|
|
|
error, test_bit(BIO_UPTODATE, &bio->bi_flags));
|
|
|
|
WARN_ON(test_bit(BIO_UPTODATE, &bio->bi_flags));
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
md_error(mddev, rdev);
|
2006-10-03 15:16:03 +07:00
|
|
|
}
|
2005-06-22 07:17:28 +07:00
|
|
|
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
if (atomic_dec_and_test(&mddev->pending_writes))
|
|
|
|
wake_up(&mddev->sb_wait);
|
2005-06-28 12:29:34 +07:00
|
|
|
bio_put(bio);
|
2005-06-22 07:17:28 +07:00
|
|
|
}
|
|
|
|
|
2007-09-27 17:47:43 +07:00
|
|
|
static void super_written_barrier(struct bio *bio, int error)
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
{
|
|
|
|
struct bio *bio2 = bio->bi_private;
|
|
|
|
mdk_rdev_t *rdev = bio2->bi_private;
|
|
|
|
mddev_t *mddev = rdev->mddev;
|
|
|
|
|
|
|
|
if (!test_bit(BIO_UPTODATE, &bio->bi_flags) &&
|
|
|
|
error == -EOPNOTSUPP) {
|
|
|
|
unsigned long flags;
|
|
|
|
/* barriers don't appear to be supported :-( */
|
|
|
|
set_bit(BarriersNotsupp, &rdev->flags);
|
|
|
|
mddev->barriers_work = 0;
|
|
|
|
spin_lock_irqsave(&mddev->write_lock, flags);
|
|
|
|
bio2->bi_next = mddev->biolist;
|
|
|
|
mddev->biolist = bio2;
|
|
|
|
spin_unlock_irqrestore(&mddev->write_lock, flags);
|
|
|
|
wake_up(&mddev->sb_wait);
|
|
|
|
bio_put(bio);
|
2007-09-27 17:47:43 +07:00
|
|
|
} else {
|
|
|
|
bio_put(bio2);
|
|
|
|
bio->bi_private = rdev;
|
|
|
|
super_written(bio, error);
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:28 +07:00
|
|
|
void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
|
|
|
|
sector_t sector, int size, struct page *page)
|
|
|
|
{
|
|
|
|
/* write first size bytes of page to sector of rdev
|
|
|
|
* Increment mddev->pending_writes before returning
|
|
|
|
* and decrement it on completion, waking up sb_wait
|
|
|
|
* if zero is reached.
|
|
|
|
* If an error occurred, call md_error
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
*
|
|
|
|
* As we might need to resubmit the request if BIO_RW_BARRIER
|
|
|
|
* causes ENOTSUPP, we allocate a spare bio...
|
2005-06-22 07:17:28 +07:00
|
|
|
*/
|
|
|
|
struct bio *bio = bio_alloc(GFP_NOIO, 1);
|
2009-02-16 16:25:40 +07:00
|
|
|
int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG);
|
2005-06-22 07:17:28 +07:00
|
|
|
|
|
|
|
bio->bi_bdev = rdev->bdev;
|
|
|
|
bio->bi_sector = sector;
|
|
|
|
bio_add_page(bio, page, size, 0);
|
|
|
|
bio->bi_private = rdev;
|
|
|
|
bio->bi_end_io = super_written;
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
bio->bi_rw = rw;
|
|
|
|
|
2005-06-22 07:17:28 +07:00
|
|
|
atomic_inc(&mddev->pending_writes);
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
if (!test_bit(BarriersNotsupp, &rdev->flags)) {
|
|
|
|
struct bio *rbio;
|
|
|
|
rw |= (1<<BIO_RW_BARRIER);
|
|
|
|
rbio = bio_clone(bio, GFP_NOIO);
|
|
|
|
rbio->bi_private = bio;
|
|
|
|
rbio->bi_end_io = super_written_barrier;
|
|
|
|
submit_bio(rw, rbio);
|
|
|
|
} else
|
|
|
|
submit_bio(rw, bio);
|
|
|
|
}
|
|
|
|
|
|
|
|
void md_super_wait(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
/* wait for all superblock writes that were scheduled to complete.
|
|
|
|
* if any had to be retried (due to BARRIER problems), retry them
|
|
|
|
*/
|
|
|
|
DEFINE_WAIT(wq);
|
|
|
|
for(;;) {
|
|
|
|
prepare_to_wait(&mddev->sb_wait, &wq, TASK_UNINTERRUPTIBLE);
|
|
|
|
if (atomic_read(&mddev->pending_writes)==0)
|
|
|
|
break;
|
|
|
|
while (mddev->biolist) {
|
|
|
|
struct bio *bio;
|
|
|
|
spin_lock_irq(&mddev->write_lock);
|
|
|
|
bio = mddev->biolist;
|
|
|
|
mddev->biolist = bio->bi_next ;
|
|
|
|
bio->bi_next = NULL;
|
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
|
|
|
submit_bio(bio->bi_rw, bio);
|
|
|
|
}
|
|
|
|
schedule();
|
|
|
|
}
|
|
|
|
finish_wait(&mddev->sb_wait, &wq);
|
2005-06-22 07:17:28 +07:00
|
|
|
}
|
|
|
|
|
2007-09-27 17:47:43 +07:00
|
|
|
static void bi_complete(struct bio *bio, int error)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
complete((struct completion*)bio->bi_private);
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
int sync_page_io(struct block_device *bdev, sector_t sector, int size,
|
2005-04-17 05:20:36 +07:00
|
|
|
struct page *page, int rw)
|
|
|
|
{
|
2005-04-17 05:23:54 +07:00
|
|
|
struct bio *bio = bio_alloc(GFP_NOIO, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct completion event;
|
|
|
|
int ret;
|
|
|
|
|
2009-02-16 16:25:40 +07:00
|
|
|
rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
bio->bi_bdev = bdev;
|
|
|
|
bio->bi_sector = sector;
|
|
|
|
bio_add_page(bio, page, size, 0);
|
|
|
|
init_completion(&event);
|
|
|
|
bio->bi_private = &event;
|
|
|
|
bio->bi_end_io = bi_complete;
|
|
|
|
submit_bio(rw, bio);
|
|
|
|
wait_for_completion(&event);
|
|
|
|
|
|
|
|
ret = test_bit(BIO_UPTODATE, &bio->bi_flags);
|
|
|
|
bio_put(bio);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-01-06 15:20:34 +07:00
|
|
|
EXPORT_SYMBOL_GPL(sync_page_io);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 06:23:53 +07:00
|
|
|
static int read_disk_sb(mdk_rdev_t * rdev, int size)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
if (!rdev->sb_page) {
|
|
|
|
MD_BUG();
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (rdev->sb_loaded)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
2008-07-11 19:02:23 +07:00
|
|
|
if (!sync_page_io(rdev->bdev, rdev->sb_start, size, rdev->sb_page, READ))
|
2005-04-17 05:20:36 +07:00
|
|
|
goto fail;
|
|
|
|
rdev->sb_loaded = 1;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
printk(KERN_WARNING "md: disabled device %s, could not read superblock.\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uuid_equal(mdp_super_t *sb1, mdp_super_t *sb2)
|
|
|
|
{
|
2008-07-11 19:02:20 +07:00
|
|
|
return sb1->set_uuid0 == sb2->set_uuid0 &&
|
|
|
|
sb1->set_uuid1 == sb2->set_uuid1 &&
|
|
|
|
sb1->set_uuid2 == sb2->set_uuid2 &&
|
|
|
|
sb1->set_uuid3 == sb2->set_uuid3;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
mdp_super_t *tmp1, *tmp2;
|
|
|
|
|
|
|
|
tmp1 = kmalloc(sizeof(*tmp1),GFP_KERNEL);
|
|
|
|
tmp2 = kmalloc(sizeof(*tmp2),GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!tmp1 || !tmp2) {
|
|
|
|
ret = 0;
|
2008-03-23 21:10:33 +07:00
|
|
|
printk(KERN_INFO "md.c sb_equal(): failed to allocate memory!\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
*tmp1 = *sb1;
|
|
|
|
*tmp2 = *sb2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* nr_disks is not constant
|
|
|
|
*/
|
|
|
|
tmp1->nr_disks = 0;
|
|
|
|
tmp2->nr_disks = 0;
|
|
|
|
|
2008-07-11 19:02:20 +07:00
|
|
|
ret = (memcmp(tmp1, tmp2, MD_SB_GENERIC_CONSTANT_WORDS * 4) == 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
abort:
|
2005-06-22 07:17:30 +07:00
|
|
|
kfree(tmp1);
|
|
|
|
kfree(tmp2);
|
2005-04-17 05:20:36 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-05-09 16:35:37 +07:00
|
|
|
|
|
|
|
static u32 md_csum_fold(u32 csum)
|
|
|
|
{
|
|
|
|
csum = (csum & 0xffff) + (csum >> 16);
|
|
|
|
return (csum & 0xffff) + (csum >> 16);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static unsigned int calc_sb_csum(mdp_super_t * sb)
|
|
|
|
{
|
2007-05-09 16:35:37 +07:00
|
|
|
u64 newcsum = 0;
|
|
|
|
u32 *sb32 = (u32*)sb;
|
|
|
|
int i;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int disk_csum, csum;
|
|
|
|
|
|
|
|
disk_csum = sb->sb_csum;
|
|
|
|
sb->sb_csum = 0;
|
2007-05-09 16:35:37 +07:00
|
|
|
|
|
|
|
for (i = 0; i < MD_SB_BYTES/4 ; i++)
|
|
|
|
newcsum += sb32[i];
|
|
|
|
csum = (newcsum & 0xffffffff) + (newcsum>>32);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_ALPHA
|
|
|
|
/* This used to use csum_partial, which was wrong for several
|
|
|
|
* reasons including that different results are returned on
|
|
|
|
* different architectures. It isn't critical that we get exactly
|
|
|
|
* the same return value as before (we always csum_fold before
|
|
|
|
* testing, and that removes any differences). However as we
|
|
|
|
* know that csum_partial always returned a 16bit value on
|
|
|
|
* alphas, do a fold to maximise conformity to previous behaviour.
|
|
|
|
*/
|
|
|
|
sb->sb_csum = md_csum_fold(disk_csum);
|
|
|
|
#else
|
2005-04-17 05:20:36 +07:00
|
|
|
sb->sb_csum = disk_csum;
|
2007-05-09 16:35:37 +07:00
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
return csum;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle superblock details.
|
|
|
|
* We want to be able to handle multiple superblock formats
|
|
|
|
* so we have a common interface to them all, and an array of
|
|
|
|
* different handlers.
|
|
|
|
* We rely on user-space to write the initial superblock, and support
|
|
|
|
* reading and updating of superblocks.
|
|
|
|
* Interface methods are:
|
|
|
|
* int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev, int minor_version)
|
|
|
|
* loads and validates a superblock on dev.
|
|
|
|
* if refdev != NULL, compare superblocks on both devices
|
|
|
|
* Return:
|
|
|
|
* 0 - dev has a superblock that is compatible with refdev
|
|
|
|
* 1 - dev has a superblock that is compatible and newer than refdev
|
|
|
|
* so dev should be used as the refdev in future
|
|
|
|
* -EINVAL superblock incompatible or invalid
|
|
|
|
* -othererror e.g. -EIO
|
|
|
|
*
|
|
|
|
* int validate_super(mddev_t *mddev, mdk_rdev_t *dev)
|
|
|
|
* Verify that dev is acceptable into mddev.
|
|
|
|
* The first time, mddev->raid_disks will be 0, and data from
|
|
|
|
* dev should be merged in. Subsequent calls check that dev
|
|
|
|
* is new enough. Return 0 or -EINVAL
|
|
|
|
*
|
|
|
|
* void sync_super(mddev_t *mddev, mdk_rdev_t *dev)
|
|
|
|
* Update the superblock for rdev with data in mddev
|
|
|
|
* This does not write to disc.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct super_type {
|
2008-06-28 05:31:46 +07:00
|
|
|
char *name;
|
|
|
|
struct module *owner;
|
|
|
|
int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev,
|
|
|
|
int minor_version);
|
|
|
|
int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev);
|
|
|
|
void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev);
|
|
|
|
unsigned long long (*rdev_size_change)(mdk_rdev_t *rdev,
|
2008-07-21 11:42:12 +07:00
|
|
|
sector_t num_sectors);
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* load_super for 0.90.0
|
|
|
|
*/
|
|
|
|
static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
|
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
|
|
|
|
mdp_super_t *sb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/*
|
2008-07-11 19:02:23 +07:00
|
|
|
* Calculate the position of the superblock (512byte sectors),
|
2005-04-17 05:20:36 +07:00
|
|
|
* it's at the end of the disk.
|
|
|
|
*
|
|
|
|
* It also happens to be a multiple of 4Kb.
|
|
|
|
*/
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 06:23:53 +07:00
|
|
|
ret = read_disk_sb(rdev, MD_SB_BYTES);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
|
|
|
bdevname(rdev->bdev, b);
|
|
|
|
sb = (mdp_super_t*)page_address(rdev->sb_page);
|
|
|
|
|
|
|
|
if (sb->md_magic != MD_SB_MAGIC) {
|
|
|
|
printk(KERN_ERR "md: invalid raid superblock magic on %s\n",
|
|
|
|
b);
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sb->major_version != 0 ||
|
2006-03-27 16:18:11 +07:00
|
|
|
sb->minor_version < 90 ||
|
|
|
|
sb->minor_version > 91) {
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_WARNING "Bad version number %d.%d on %s\n",
|
|
|
|
sb->major_version, sb->minor_version,
|
|
|
|
b);
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sb->raid_disks <= 0)
|
|
|
|
goto abort;
|
|
|
|
|
2007-05-09 16:35:37 +07:00
|
|
|
if (md_csum_fold(calc_sb_csum(sb)) != md_csum_fold(sb->sb_csum)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_WARNING "md: invalid superblock checksum on %s\n",
|
|
|
|
b);
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
rdev->preferred_minor = sb->md_minor;
|
|
|
|
rdev->data_offset = 0;
|
2005-09-10 06:23:53 +07:00
|
|
|
rdev->sb_size = MD_SB_BYTES;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-05-09 16:35:36 +07:00
|
|
|
if (sb->state & (1<<MD_SB_BITMAP_PRESENT)) {
|
|
|
|
if (sb->level != 1 && sb->level != 4
|
|
|
|
&& sb->level != 5 && sb->level != 6
|
|
|
|
&& sb->level != 10) {
|
|
|
|
/* FIXME use a better test */
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: bitmaps not supported for this level.\n");
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (sb->level == LEVEL_MULTIPATH)
|
|
|
|
rdev->desc_nr = -1;
|
|
|
|
else
|
|
|
|
rdev->desc_nr = sb->this_disk.number;
|
|
|
|
|
2008-04-28 16:15:49 +07:00
|
|
|
if (!refdev) {
|
2005-04-17 05:20:36 +07:00
|
|
|
ret = 1;
|
2008-04-28 16:15:49 +07:00
|
|
|
} else {
|
2005-04-17 05:20:36 +07:00
|
|
|
__u64 ev1, ev2;
|
|
|
|
mdp_super_t *refsb = (mdp_super_t*)page_address(refdev->sb_page);
|
|
|
|
if (!uuid_equal(refsb, sb)) {
|
|
|
|
printk(KERN_WARNING "md: %s has different UUID to %s\n",
|
|
|
|
b, bdevname(refdev->bdev,b2));
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
if (!sb_equal(refsb, sb)) {
|
|
|
|
printk(KERN_WARNING "md: %s has same UUID"
|
|
|
|
" but different superblock to %s\n",
|
|
|
|
b, bdevname(refdev->bdev, b2));
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
ev1 = md_event(sb);
|
|
|
|
ev2 = md_event(refsb);
|
|
|
|
if (ev1 > ev2)
|
|
|
|
ret = 1;
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
}
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->size = calc_num_sectors(rdev, sb->chunk_size) / 2;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-06 15:20:55 +07:00
|
|
|
if (rdev->size < sb->size && sb->level > 1)
|
|
|
|
/* "this cannot possibly happen" ... */
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
abort:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* validate_super for 0.90.0
|
|
|
|
*/
|
|
|
|
static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
|
|
|
|
{
|
|
|
|
mdp_disk_t *desc;
|
|
|
|
mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page);
|
2006-06-26 14:27:56 +07:00
|
|
|
__u64 ev1 = md_event(sb);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-22 07:17:25 +07:00
|
|
|
rdev->raid_disk = -1;
|
2008-02-06 16:39:54 +07:00
|
|
|
clear_bit(Faulty, &rdev->flags);
|
|
|
|
clear_bit(In_sync, &rdev->flags);
|
|
|
|
clear_bit(WriteMostly, &rdev->flags);
|
|
|
|
clear_bit(BarriersNotsupp, &rdev->flags);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->raid_disks == 0) {
|
|
|
|
mddev->major_version = 0;
|
|
|
|
mddev->minor_version = sb->minor_version;
|
|
|
|
mddev->patch_version = sb->patch_version;
|
2008-02-06 16:39:51 +07:00
|
|
|
mddev->external = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->chunk_size = sb->chunk_size;
|
|
|
|
mddev->ctime = sb->ctime;
|
|
|
|
mddev->utime = sb->utime;
|
|
|
|
mddev->level = sb->level;
|
2006-01-06 15:20:51 +07:00
|
|
|
mddev->clevel[0] = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->layout = sb->layout;
|
|
|
|
mddev->raid_disks = sb->raid_disks;
|
|
|
|
mddev->size = sb->size;
|
2006-06-26 14:27:56 +07:00
|
|
|
mddev->events = ev1;
|
2005-08-19 01:24:16 +07:00
|
|
|
mddev->bitmap_offset = 0;
|
2005-09-10 06:23:45 +07:00
|
|
|
mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-03-27 16:18:11 +07:00
|
|
|
if (mddev->minor_version >= 91) {
|
|
|
|
mddev->reshape_position = sb->reshape_position;
|
|
|
|
mddev->delta_disks = sb->delta_disks;
|
|
|
|
mddev->new_level = sb->new_level;
|
|
|
|
mddev->new_layout = sb->new_layout;
|
|
|
|
mddev->new_chunk = sb->new_chunk;
|
|
|
|
} else {
|
|
|
|
mddev->reshape_position = MaxSector;
|
|
|
|
mddev->delta_disks = 0;
|
|
|
|
mddev->new_level = mddev->level;
|
|
|
|
mddev->new_layout = mddev->layout;
|
|
|
|
mddev->new_chunk = mddev->chunk_size;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (sb->state & (1<<MD_SB_CLEAN))
|
|
|
|
mddev->recovery_cp = MaxSector;
|
|
|
|
else {
|
|
|
|
if (sb->events_hi == sb->cp_events_hi &&
|
|
|
|
sb->events_lo == sb->cp_events_lo) {
|
|
|
|
mddev->recovery_cp = sb->recovery_cp;
|
|
|
|
} else
|
|
|
|
mddev->recovery_cp = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(mddev->uuid+0, &sb->set_uuid0, 4);
|
|
|
|
memcpy(mddev->uuid+4, &sb->set_uuid1, 4);
|
|
|
|
memcpy(mddev->uuid+8, &sb->set_uuid2, 4);
|
|
|
|
memcpy(mddev->uuid+12,&sb->set_uuid3, 4);
|
|
|
|
|
|
|
|
mddev->max_disks = MD_SB_DISKS;
|
2005-06-22 07:17:27 +07:00
|
|
|
|
|
|
|
if (sb->state & (1<<MD_SB_BITMAP_PRESENT) &&
|
2007-05-09 16:35:36 +07:00
|
|
|
mddev->bitmap_file == NULL)
|
2005-09-10 06:23:45 +07:00
|
|
|
mddev->bitmap_offset = mddev->default_bitmap_offset;
|
2005-06-22 07:17:27 +07:00
|
|
|
|
2005-06-22 07:17:25 +07:00
|
|
|
} else if (mddev->pers == NULL) {
|
|
|
|
/* Insist on good event counter while assembling */
|
2005-04-17 05:20:36 +07:00
|
|
|
++ev1;
|
|
|
|
if (ev1 < mddev->events)
|
|
|
|
return -EINVAL;
|
2005-06-22 07:17:25 +07:00
|
|
|
} else if (mddev->bitmap) {
|
|
|
|
/* if adding to array with a bitmap, then we can accept an
|
|
|
|
* older device ... but not too old.
|
|
|
|
*/
|
|
|
|
if (ev1 < mddev->bitmap->events_cleared)
|
|
|
|
return 0;
|
2006-06-26 14:27:56 +07:00
|
|
|
} else {
|
|
|
|
if (ev1 < mddev->events)
|
|
|
|
/* just a hot-add of a new device, leave raid_disk at -1 */
|
|
|
|
return 0;
|
|
|
|
}
|
2005-06-22 07:17:25 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->level != LEVEL_MULTIPATH) {
|
|
|
|
desc = sb->disks + rdev->desc_nr;
|
|
|
|
|
|
|
|
if (desc->state & (1<<MD_DISK_FAULTY))
|
2005-11-09 12:39:31 +07:00
|
|
|
set_bit(Faulty, &rdev->flags);
|
2006-06-26 14:27:41 +07:00
|
|
|
else if (desc->state & (1<<MD_DISK_SYNC) /* &&
|
|
|
|
desc->raid_disk < mddev->raid_disks */) {
|
2005-11-09 12:39:31 +07:00
|
|
|
set_bit(In_sync, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->raid_disk = desc->raid_disk;
|
|
|
|
}
|
2005-09-10 06:23:45 +07:00
|
|
|
if (desc->state & (1<<MD_DISK_WRITEMOSTLY))
|
|
|
|
set_bit(WriteMostly, &rdev->flags);
|
2005-06-22 07:17:25 +07:00
|
|
|
} else /* MULTIPATH are always insync */
|
2005-11-09 12:39:31 +07:00
|
|
|
set_bit(In_sync, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sync_super for 0.90.0
|
|
|
|
*/
|
|
|
|
static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
|
|
|
|
{
|
|
|
|
mdp_super_t *sb;
|
|
|
|
mdk_rdev_t *rdev2;
|
|
|
|
int next_spare = mddev->raid_disks;
|
2005-11-09 12:39:35 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* make rdev->sb match mddev data..
|
|
|
|
*
|
|
|
|
* 1/ zero out disks
|
|
|
|
* 2/ Add info for each disk, keeping track of highest desc_nr (next_spare);
|
|
|
|
* 3/ any empty disks < next_spare become removed
|
|
|
|
*
|
|
|
|
* disks[0] gets initialised to REMOVED because
|
|
|
|
* we cannot be sure from other fields if it has
|
|
|
|
* been initialised or not.
|
|
|
|
*/
|
|
|
|
int i;
|
|
|
|
int active=0, working=0,failed=0,spare=0,nr_disks=0;
|
|
|
|
|
2005-09-10 06:24:02 +07:00
|
|
|
rdev->sb_size = MD_SB_BYTES;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
sb = (mdp_super_t*)page_address(rdev->sb_page);
|
|
|
|
|
|
|
|
memset(sb, 0, sizeof(*sb));
|
|
|
|
|
|
|
|
sb->md_magic = MD_SB_MAGIC;
|
|
|
|
sb->major_version = mddev->major_version;
|
|
|
|
sb->patch_version = mddev->patch_version;
|
|
|
|
sb->gvalid_words = 0; /* ignored */
|
|
|
|
memcpy(&sb->set_uuid0, mddev->uuid+0, 4);
|
|
|
|
memcpy(&sb->set_uuid1, mddev->uuid+4, 4);
|
|
|
|
memcpy(&sb->set_uuid2, mddev->uuid+8, 4);
|
|
|
|
memcpy(&sb->set_uuid3, mddev->uuid+12,4);
|
|
|
|
|
|
|
|
sb->ctime = mddev->ctime;
|
|
|
|
sb->level = mddev->level;
|
|
|
|
sb->size = mddev->size;
|
|
|
|
sb->raid_disks = mddev->raid_disks;
|
|
|
|
sb->md_minor = mddev->md_minor;
|
2008-02-06 16:39:51 +07:00
|
|
|
sb->not_persistent = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
sb->utime = mddev->utime;
|
|
|
|
sb->state = 0;
|
|
|
|
sb->events_hi = (mddev->events>>32);
|
|
|
|
sb->events_lo = (u32)mddev->events;
|
|
|
|
|
2006-03-27 16:18:11 +07:00
|
|
|
if (mddev->reshape_position == MaxSector)
|
|
|
|
sb->minor_version = 90;
|
|
|
|
else {
|
|
|
|
sb->minor_version = 91;
|
|
|
|
sb->reshape_position = mddev->reshape_position;
|
|
|
|
sb->new_level = mddev->new_level;
|
|
|
|
sb->delta_disks = mddev->delta_disks;
|
|
|
|
sb->new_layout = mddev->new_layout;
|
|
|
|
sb->new_chunk = mddev->new_chunk;
|
|
|
|
}
|
|
|
|
mddev->minor_version = sb->minor_version;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->in_sync)
|
|
|
|
{
|
|
|
|
sb->recovery_cp = mddev->recovery_cp;
|
|
|
|
sb->cp_events_hi = (mddev->events>>32);
|
|
|
|
sb->cp_events_lo = (u32)mddev->events;
|
|
|
|
if (mddev->recovery_cp == MaxSector)
|
|
|
|
sb->state = (1<< MD_SB_CLEAN);
|
|
|
|
} else
|
|
|
|
sb->recovery_cp = 0;
|
|
|
|
|
|
|
|
sb->layout = mddev->layout;
|
|
|
|
sb->chunk_size = mddev->chunk_size;
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
if (mddev->bitmap && mddev->bitmap_file == NULL)
|
|
|
|
sb->state |= (1<<MD_SB_BITMAP_PRESENT);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
sb->disks[0].state = (1<<MD_DISK_REMOVED);
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev2, &mddev->disks, same_set) {
|
2005-04-17 05:20:36 +07:00
|
|
|
mdp_disk_t *d;
|
2005-11-09 12:39:24 +07:00
|
|
|
int desc_nr;
|
2005-11-09 12:39:31 +07:00
|
|
|
if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags)
|
|
|
|
&& !test_bit(Faulty, &rdev2->flags))
|
2005-11-09 12:39:24 +07:00
|
|
|
desc_nr = rdev2->raid_disk;
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
2005-11-09 12:39:24 +07:00
|
|
|
desc_nr = next_spare++;
|
2005-11-09 12:39:35 +07:00
|
|
|
rdev2->desc_nr = desc_nr;
|
2005-04-17 05:20:36 +07:00
|
|
|
d = &sb->disks[rdev2->desc_nr];
|
|
|
|
nr_disks++;
|
|
|
|
d->number = rdev2->desc_nr;
|
|
|
|
d->major = MAJOR(rdev2->bdev->bd_dev);
|
|
|
|
d->minor = MINOR(rdev2->bdev->bd_dev);
|
2005-11-09 12:39:31 +07:00
|
|
|
if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags)
|
|
|
|
&& !test_bit(Faulty, &rdev2->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
d->raid_disk = rdev2->raid_disk;
|
|
|
|
else
|
|
|
|
d->raid_disk = rdev2->desc_nr; /* compatibility */
|
2006-03-27 16:18:03 +07:00
|
|
|
if (test_bit(Faulty, &rdev2->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
d->state = (1<<MD_DISK_FAULTY);
|
2006-03-27 16:18:03 +07:00
|
|
|
else if (test_bit(In_sync, &rdev2->flags)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
d->state = (1<<MD_DISK_ACTIVE);
|
|
|
|
d->state |= (1<<MD_DISK_SYNC);
|
|
|
|
active++;
|
|
|
|
working++;
|
|
|
|
} else {
|
|
|
|
d->state = 0;
|
|
|
|
spare++;
|
|
|
|
working++;
|
|
|
|
}
|
2005-09-10 06:23:45 +07:00
|
|
|
if (test_bit(WriteMostly, &rdev2->flags))
|
|
|
|
d->state |= (1<<MD_DISK_WRITEMOSTLY);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
/* now set the "removed" and "faulty" bits on any missing devices */
|
|
|
|
for (i=0 ; i < mddev->raid_disks ; i++) {
|
|
|
|
mdp_disk_t *d = &sb->disks[i];
|
|
|
|
if (d->state == 0 && d->number == 0) {
|
|
|
|
d->number = i;
|
|
|
|
d->raid_disk = i;
|
|
|
|
d->state = (1<<MD_DISK_REMOVED);
|
|
|
|
d->state |= (1<<MD_DISK_FAULTY);
|
|
|
|
failed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sb->nr_disks = nr_disks;
|
|
|
|
sb->active_disks = active;
|
|
|
|
sb->working_disks = working;
|
|
|
|
sb->failed_disks = failed;
|
|
|
|
sb->spare_disks = spare;
|
|
|
|
|
|
|
|
sb->this_disk = sb->disks[rdev->desc_nr];
|
|
|
|
sb->sb_csum = calc_sb_csum(sb);
|
|
|
|
}
|
|
|
|
|
2008-06-28 05:31:46 +07:00
|
|
|
/*
|
|
|
|
* rdev_size_change for 0.90.0
|
|
|
|
*/
|
|
|
|
static unsigned long long
|
2008-07-21 11:42:12 +07:00
|
|
|
super_90_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
|
2008-06-28 05:31:46 +07:00
|
|
|
{
|
2008-07-21 11:42:12 +07:00
|
|
|
if (num_sectors && num_sectors < rdev->mddev->size * 2)
|
2008-06-28 05:31:46 +07:00
|
|
|
return 0; /* component must fit device */
|
|
|
|
if (rdev->mddev->bitmap_offset)
|
|
|
|
return 0; /* can't move bitmap */
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
2008-07-21 11:42:12 +07:00
|
|
|
if (!num_sectors || num_sectors > rdev->sb_start)
|
|
|
|
num_sectors = rdev->sb_start;
|
2008-07-11 19:02:23 +07:00
|
|
|
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
|
2008-06-28 05:31:46 +07:00
|
|
|
rdev->sb_page);
|
|
|
|
md_super_wait(rdev->mddev);
|
2008-07-21 11:42:12 +07:00
|
|
|
return num_sectors / 2; /* kB for sysfs */
|
2008-06-28 05:31:46 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* version 1 superblock
|
|
|
|
*/
|
|
|
|
|
2006-10-22 00:24:08 +07:00
|
|
|
static __le32 calc_sb_1_csum(struct mdp_superblock_1 * sb)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-10-22 00:24:08 +07:00
|
|
|
__le32 disk_csum;
|
|
|
|
u32 csum;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long long newcsum;
|
|
|
|
int size = 256 + le32_to_cpu(sb->max_dev)*2;
|
2006-10-22 00:24:08 +07:00
|
|
|
__le32 *isuper = (__le32*)sb;
|
2005-04-17 05:20:36 +07:00
|
|
|
int i;
|
|
|
|
|
|
|
|
disk_csum = sb->sb_csum;
|
|
|
|
sb->sb_csum = 0;
|
|
|
|
newcsum = 0;
|
|
|
|
for (i=0; size>=4; size -= 4 )
|
|
|
|
newcsum += le32_to_cpu(*isuper++);
|
|
|
|
|
|
|
|
if (size == 2)
|
2006-10-22 00:24:08 +07:00
|
|
|
newcsum += le16_to_cpu(*(__le16*) isuper);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
csum = (newcsum & 0xffffffff) + (newcsum >> 32);
|
|
|
|
sb->sb_csum = disk_csum;
|
|
|
|
return cpu_to_le32(csum);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
|
|
|
|
{
|
|
|
|
struct mdp_superblock_1 *sb;
|
|
|
|
int ret;
|
2008-07-11 19:02:23 +07:00
|
|
|
sector_t sb_start;
|
2005-04-17 05:20:36 +07:00
|
|
|
char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
|
2005-09-10 06:23:53 +07:00
|
|
|
int bmask;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
2008-07-11 19:02:23 +07:00
|
|
|
* Calculate the position of the superblock in 512byte sectors.
|
2005-04-17 05:20:36 +07:00
|
|
|
* It is always aligned to a 4K boundary and
|
|
|
|
* depeding on minor_version, it can be:
|
|
|
|
* 0: At least 8K, but less than 12K, from end of device
|
|
|
|
* 1: At start of device
|
|
|
|
* 2: 4K from start of device.
|
|
|
|
*/
|
|
|
|
switch(minor_version) {
|
|
|
|
case 0:
|
2008-07-11 19:02:23 +07:00
|
|
|
sb_start = rdev->bdev->bd_inode->i_size >> 9;
|
|
|
|
sb_start -= 8*2;
|
|
|
|
sb_start &= ~(sector_t)(4*2-1);
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case 1:
|
2008-07-11 19:02:23 +07:00
|
|
|
sb_start = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case 2:
|
2008-07-11 19:02:23 +07:00
|
|
|
sb_start = 8;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = sb_start;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 06:23:53 +07:00
|
|
|
/* superblock is rarely larger than 1K, but it can be larger,
|
|
|
|
* and it is safe to read 4k, so we do that
|
|
|
|
*/
|
|
|
|
ret = read_disk_sb(rdev, 4096);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
|
|
sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
|
|
|
|
|
|
|
|
if (sb->magic != cpu_to_le32(MD_SB_MAGIC) ||
|
|
|
|
sb->major_version != cpu_to_le32(1) ||
|
|
|
|
le32_to_cpu(sb->max_dev) > (4096-256)/2 ||
|
2008-07-11 19:02:23 +07:00
|
|
|
le64_to_cpu(sb->super_offset) != rdev->sb_start ||
|
2005-09-10 06:23:51 +07:00
|
|
|
(le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (calc_sb_1_csum(sb) != sb->sb_csum) {
|
|
|
|
printk("md: invalid superblock checksum on %s\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (le64_to_cpu(sb->data_size) < 10) {
|
|
|
|
printk("md: data_size too small on %s\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2007-05-09 16:35:36 +07:00
|
|
|
if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET)) {
|
|
|
|
if (sb->level != cpu_to_le32(1) &&
|
|
|
|
sb->level != cpu_to_le32(4) &&
|
|
|
|
sb->level != cpu_to_le32(5) &&
|
|
|
|
sb->level != cpu_to_le32(6) &&
|
|
|
|
sb->level != cpu_to_le32(10)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: bitmaps not supported for this level.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->preferred_minor = 0xffff;
|
|
|
|
rdev->data_offset = le64_to_cpu(sb->data_offset);
|
2006-01-06 15:20:52 +07:00
|
|
|
atomic_set(&rdev->corrected_errors, le32_to_cpu(sb->cnt_corrected_read));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 06:23:53 +07:00
|
|
|
rdev->sb_size = le32_to_cpu(sb->max_dev) * 2 + 256;
|
2005-09-10 06:23:59 +07:00
|
|
|
bmask = queue_hardsect_size(rdev->bdev->bd_disk->queue)-1;
|
2005-09-10 06:23:53 +07:00
|
|
|
if (rdev->sb_size & bmask)
|
2008-03-05 05:29:31 +07:00
|
|
|
rdev->sb_size = (rdev->sb_size | bmask) + 1;
|
|
|
|
|
|
|
|
if (minor_version
|
2008-07-11 19:02:23 +07:00
|
|
|
&& rdev->data_offset < sb_start + (rdev->sb_size/512))
|
2008-03-05 05:29:31 +07:00
|
|
|
return -EINVAL;
|
2005-09-10 06:23:53 +07:00
|
|
|
|
2006-07-10 18:44:14 +07:00
|
|
|
if (sb->level == cpu_to_le32(LEVEL_MULTIPATH))
|
|
|
|
rdev->desc_nr = -1;
|
|
|
|
else
|
|
|
|
rdev->desc_nr = le32_to_cpu(sb->dev_number);
|
|
|
|
|
2008-04-28 16:15:49 +07:00
|
|
|
if (!refdev) {
|
2006-02-03 18:03:41 +07:00
|
|
|
ret = 1;
|
2008-04-28 16:15:49 +07:00
|
|
|
} else {
|
2005-04-17 05:20:36 +07:00
|
|
|
__u64 ev1, ev2;
|
|
|
|
struct mdp_superblock_1 *refsb =
|
|
|
|
(struct mdp_superblock_1*)page_address(refdev->sb_page);
|
|
|
|
|
|
|
|
if (memcmp(sb->set_uuid, refsb->set_uuid, 16) != 0 ||
|
|
|
|
sb->level != refsb->level ||
|
|
|
|
sb->layout != refsb->layout ||
|
|
|
|
sb->chunksize != refsb->chunksize) {
|
|
|
|
printk(KERN_WARNING "md: %s has strangely different"
|
|
|
|
" superblock to %s\n",
|
|
|
|
bdevname(rdev->bdev,b),
|
|
|
|
bdevname(refdev->bdev,b2));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
ev1 = le64_to_cpu(sb->events);
|
|
|
|
ev2 = le64_to_cpu(refsb->events);
|
|
|
|
|
|
|
|
if (ev1 > ev2)
|
2006-02-03 18:03:41 +07:00
|
|
|
ret = 1;
|
|
|
|
else
|
|
|
|
ret = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2008-03-05 05:29:31 +07:00
|
|
|
if (minor_version)
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2;
|
|
|
|
else
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->size = rdev->sb_start / 2;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rdev->size < le64_to_cpu(sb->data_size)/2)
|
|
|
|
return -EINVAL;
|
|
|
|
rdev->size = le64_to_cpu(sb->data_size)/2;
|
|
|
|
if (le32_to_cpu(sb->chunksize))
|
|
|
|
rdev->size &= ~((sector_t)le32_to_cpu(sb->chunksize)/2 - 1);
|
2006-01-06 15:20:55 +07:00
|
|
|
|
2006-10-22 00:24:08 +07:00
|
|
|
if (le64_to_cpu(sb->size) > rdev->size*2)
|
2006-01-06 15:20:55 +07:00
|
|
|
return -EINVAL;
|
2006-02-03 18:03:41 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
|
|
|
|
{
|
|
|
|
struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
|
2006-06-26 14:27:56 +07:00
|
|
|
__u64 ev1 = le64_to_cpu(sb->events);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-22 07:17:25 +07:00
|
|
|
rdev->raid_disk = -1;
|
2008-02-06 16:39:54 +07:00
|
|
|
clear_bit(Faulty, &rdev->flags);
|
|
|
|
clear_bit(In_sync, &rdev->flags);
|
|
|
|
clear_bit(WriteMostly, &rdev->flags);
|
|
|
|
clear_bit(BarriersNotsupp, &rdev->flags);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->raid_disks == 0) {
|
|
|
|
mddev->major_version = 1;
|
|
|
|
mddev->patch_version = 0;
|
2008-02-06 16:39:51 +07:00
|
|
|
mddev->external = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->chunk_size = le32_to_cpu(sb->chunksize) << 9;
|
|
|
|
mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1);
|
|
|
|
mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1);
|
|
|
|
mddev->level = le32_to_cpu(sb->level);
|
2006-01-06 15:20:51 +07:00
|
|
|
mddev->clevel[0] = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->layout = le32_to_cpu(sb->layout);
|
|
|
|
mddev->raid_disks = le32_to_cpu(sb->raid_disks);
|
|
|
|
mddev->size = le64_to_cpu(sb->size)/2;
|
2006-06-26 14:27:56 +07:00
|
|
|
mddev->events = ev1;
|
2005-08-19 01:24:16 +07:00
|
|
|
mddev->bitmap_offset = 0;
|
2006-02-03 18:03:41 +07:00
|
|
|
mddev->default_bitmap_offset = 1024 >> 9;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
|
|
|
|
memcpy(mddev->uuid, sb->set_uuid, 16);
|
|
|
|
|
|
|
|
mddev->max_disks = (4096-256)/2;
|
2005-06-22 07:17:27 +07:00
|
|
|
|
2005-09-10 06:23:51 +07:00
|
|
|
if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET) &&
|
2007-05-09 16:35:36 +07:00
|
|
|
mddev->bitmap_file == NULL )
|
2005-06-22 07:17:27 +07:00
|
|
|
mddev->bitmap_offset = (__s32)le32_to_cpu(sb->bitmap_offset);
|
2007-05-09 16:35:36 +07:00
|
|
|
|
2006-03-27 16:18:11 +07:00
|
|
|
if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) {
|
|
|
|
mddev->reshape_position = le64_to_cpu(sb->reshape_position);
|
|
|
|
mddev->delta_disks = le32_to_cpu(sb->delta_disks);
|
|
|
|
mddev->new_level = le32_to_cpu(sb->new_level);
|
|
|
|
mddev->new_layout = le32_to_cpu(sb->new_layout);
|
|
|
|
mddev->new_chunk = le32_to_cpu(sb->new_chunk)<<9;
|
|
|
|
} else {
|
|
|
|
mddev->reshape_position = MaxSector;
|
|
|
|
mddev->delta_disks = 0;
|
|
|
|
mddev->new_level = mddev->level;
|
|
|
|
mddev->new_layout = mddev->layout;
|
|
|
|
mddev->new_chunk = mddev->chunk_size;
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:25 +07:00
|
|
|
} else if (mddev->pers == NULL) {
|
|
|
|
/* Insist of good event counter while assembling */
|
2005-04-17 05:20:36 +07:00
|
|
|
++ev1;
|
|
|
|
if (ev1 < mddev->events)
|
|
|
|
return -EINVAL;
|
2005-06-22 07:17:25 +07:00
|
|
|
} else if (mddev->bitmap) {
|
|
|
|
/* If adding to array with a bitmap, then we can accept an
|
|
|
|
* older device, but not too old.
|
|
|
|
*/
|
|
|
|
if (ev1 < mddev->bitmap->events_cleared)
|
|
|
|
return 0;
|
2006-06-26 14:27:56 +07:00
|
|
|
} else {
|
|
|
|
if (ev1 < mddev->events)
|
|
|
|
/* just a hot-add of a new device, leave raid_disk at -1 */
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->level != LEVEL_MULTIPATH) {
|
|
|
|
int role;
|
|
|
|
role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
|
|
|
|
switch(role) {
|
|
|
|
case 0xffff: /* spare */
|
|
|
|
break;
|
|
|
|
case 0xfffe: /* faulty */
|
2005-11-09 12:39:31 +07:00
|
|
|
set_bit(Faulty, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
default:
|
2006-06-26 14:27:40 +07:00
|
|
|
if ((le32_to_cpu(sb->feature_map) &
|
|
|
|
MD_FEATURE_RECOVERY_OFFSET))
|
|
|
|
rdev->recovery_offset = le64_to_cpu(sb->recovery_offset);
|
|
|
|
else
|
|
|
|
set_bit(In_sync, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->raid_disk = role;
|
|
|
|
break;
|
|
|
|
}
|
2005-09-10 06:23:45 +07:00
|
|
|
if (sb->devflags & WriteMostly1)
|
|
|
|
set_bit(WriteMostly, &rdev->flags);
|
2005-06-22 07:17:25 +07:00
|
|
|
} else /* MULTIPATH are always insync */
|
2005-11-09 12:39:31 +07:00
|
|
|
set_bit(In_sync, &rdev->flags);
|
2005-06-22 07:17:25 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
|
|
|
|
{
|
|
|
|
struct mdp_superblock_1 *sb;
|
|
|
|
mdk_rdev_t *rdev2;
|
|
|
|
int max_dev, i;
|
|
|
|
/* make rdev->sb match mddev and rdev data. */
|
|
|
|
|
|
|
|
sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
|
|
|
|
|
|
|
|
sb->feature_map = 0;
|
|
|
|
sb->pad0 = 0;
|
2006-06-26 14:27:40 +07:00
|
|
|
sb->recovery_offset = cpu_to_le64(0);
|
2005-04-17 05:20:36 +07:00
|
|
|
memset(sb->pad1, 0, sizeof(sb->pad1));
|
|
|
|
memset(sb->pad2, 0, sizeof(sb->pad2));
|
|
|
|
memset(sb->pad3, 0, sizeof(sb->pad3));
|
|
|
|
|
|
|
|
sb->utime = cpu_to_le64((__u64)mddev->utime);
|
|
|
|
sb->events = cpu_to_le64(mddev->events);
|
|
|
|
if (mddev->in_sync)
|
|
|
|
sb->resync_offset = cpu_to_le64(mddev->recovery_cp);
|
|
|
|
else
|
|
|
|
sb->resync_offset = cpu_to_le64(0);
|
|
|
|
|
2006-10-22 00:24:08 +07:00
|
|
|
sb->cnt_corrected_read = cpu_to_le32(atomic_read(&rdev->corrected_errors));
|
2006-01-06 15:20:52 +07:00
|
|
|
|
2006-02-03 05:28:04 +07:00
|
|
|
sb->raid_disks = cpu_to_le32(mddev->raid_disks);
|
2006-02-03 18:03:41 +07:00
|
|
|
sb->size = cpu_to_le64(mddev->size<<1);
|
2006-02-03 05:28:04 +07:00
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
if (mddev->bitmap && mddev->bitmap_file == NULL) {
|
|
|
|
sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
|
2005-09-10 06:23:51 +07:00
|
|
|
sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET);
|
2005-06-22 07:17:27 +07:00
|
|
|
}
|
2006-06-26 14:27:40 +07:00
|
|
|
|
|
|
|
if (rdev->raid_disk >= 0 &&
|
|
|
|
!test_bit(In_sync, &rdev->flags) &&
|
|
|
|
rdev->recovery_offset > 0) {
|
|
|
|
sb->feature_map |= cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET);
|
|
|
|
sb->recovery_offset = cpu_to_le64(rdev->recovery_offset);
|
|
|
|
}
|
|
|
|
|
2006-03-27 16:18:11 +07:00
|
|
|
if (mddev->reshape_position != MaxSector) {
|
|
|
|
sb->feature_map |= cpu_to_le32(MD_FEATURE_RESHAPE_ACTIVE);
|
|
|
|
sb->reshape_position = cpu_to_le64(mddev->reshape_position);
|
|
|
|
sb->new_layout = cpu_to_le32(mddev->new_layout);
|
|
|
|
sb->delta_disks = cpu_to_le32(mddev->delta_disks);
|
|
|
|
sb->new_level = cpu_to_le32(mddev->new_level);
|
|
|
|
sb->new_chunk = cpu_to_le32(mddev->new_chunk>>9);
|
|
|
|
}
|
2005-06-22 07:17:27 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
max_dev = 0;
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev2, &mddev->disks, same_set)
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rdev2->desc_nr+1 > max_dev)
|
|
|
|
max_dev = rdev2->desc_nr+1;
|
2007-05-24 03:58:10 +07:00
|
|
|
|
|
|
|
if (max_dev > le32_to_cpu(sb->max_dev))
|
|
|
|
sb->max_dev = cpu_to_le32(max_dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i=0; i<max_dev;i++)
|
|
|
|
sb->dev_roles[i] = cpu_to_le16(0xfffe);
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev2, &mddev->disks, same_set) {
|
2005-04-17 05:20:36 +07:00
|
|
|
i = rdev2->desc_nr;
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev2->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
sb->dev_roles[i] = cpu_to_le16(0xfffe);
|
2005-11-09 12:39:31 +07:00
|
|
|
else if (test_bit(In_sync, &rdev2->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk);
|
2006-06-26 14:27:40 +07:00
|
|
|
else if (rdev2->raid_disk >= 0 && rdev2->recovery_offset > 0)
|
|
|
|
sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk);
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
|
|
|
sb->dev_roles[i] = cpu_to_le16(0xffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
sb->sb_csum = calc_sb_1_csum(sb);
|
|
|
|
}
|
|
|
|
|
2008-06-28 05:31:46 +07:00
|
|
|
static unsigned long long
|
2008-07-21 11:42:12 +07:00
|
|
|
super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
|
2008-06-28 05:31:46 +07:00
|
|
|
{
|
|
|
|
struct mdp_superblock_1 *sb;
|
2008-07-21 11:42:12 +07:00
|
|
|
sector_t max_sectors;
|
|
|
|
if (num_sectors && num_sectors < rdev->mddev->size * 2)
|
2008-06-28 05:31:46 +07:00
|
|
|
return 0; /* component must fit device */
|
2008-07-11 19:02:23 +07:00
|
|
|
if (rdev->sb_start < rdev->data_offset) {
|
2008-06-28 05:31:46 +07:00
|
|
|
/* minor versions 1 and 2; superblock before data */
|
2008-07-21 11:42:12 +07:00
|
|
|
max_sectors = rdev->bdev->bd_inode->i_size >> 9;
|
|
|
|
max_sectors -= rdev->data_offset;
|
|
|
|
if (!num_sectors || num_sectors > max_sectors)
|
|
|
|
num_sectors = max_sectors;
|
2008-06-28 05:31:46 +07:00
|
|
|
} else if (rdev->mddev->bitmap_offset) {
|
|
|
|
/* minor version 0 with bitmap we can't move */
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
/* minor version 0; superblock after data */
|
2008-07-11 19:02:23 +07:00
|
|
|
sector_t sb_start;
|
|
|
|
sb_start = (rdev->bdev->bd_inode->i_size >> 9) - 8*2;
|
|
|
|
sb_start &= ~(sector_t)(4*2 - 1);
|
2008-07-21 11:42:12 +07:00
|
|
|
max_sectors = rdev->size * 2 + sb_start - rdev->sb_start;
|
|
|
|
if (!num_sectors || num_sectors > max_sectors)
|
|
|
|
num_sectors = max_sectors;
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = sb_start;
|
2008-06-28 05:31:46 +07:00
|
|
|
}
|
|
|
|
sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page);
|
2008-07-21 11:42:12 +07:00
|
|
|
sb->data_size = cpu_to_le64(num_sectors);
|
2008-07-11 19:02:23 +07:00
|
|
|
sb->super_offset = rdev->sb_start;
|
2008-06-28 05:31:46 +07:00
|
|
|
sb->sb_csum = calc_sb_1_csum(sb);
|
2008-07-11 19:02:23 +07:00
|
|
|
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
|
2008-06-28 05:31:46 +07:00
|
|
|
rdev->sb_page);
|
|
|
|
md_super_wait(rdev->mddev);
|
2008-07-21 11:42:12 +07:00
|
|
|
return num_sectors / 2; /* kB for sysfs */
|
2008-06-28 05:31:46 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-05-06 06:16:09 +07:00
|
|
|
static struct super_type super_types[] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
[0] = {
|
|
|
|
.name = "0.90.0",
|
|
|
|
.owner = THIS_MODULE,
|
2008-06-28 05:31:46 +07:00
|
|
|
.load_super = super_90_load,
|
|
|
|
.validate_super = super_90_validate,
|
|
|
|
.sync_super = super_90_sync,
|
|
|
|
.rdev_size_change = super_90_rdev_size_change,
|
2005-04-17 05:20:36 +07:00
|
|
|
},
|
|
|
|
[1] = {
|
|
|
|
.name = "md-1",
|
|
|
|
.owner = THIS_MODULE,
|
2008-06-28 05:31:46 +07:00
|
|
|
.load_super = super_1_load,
|
|
|
|
.validate_super = super_1_validate,
|
|
|
|
.sync_super = super_1_sync,
|
|
|
|
.rdev_size_change = super_1_rdev_size_change,
|
2005-04-17 05:20:36 +07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2)
|
|
|
|
{
|
2007-03-01 11:11:35 +07:00
|
|
|
mdk_rdev_t *rdev, *rdev2;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-21 14:05:25 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
rdev_for_each_rcu(rdev, mddev1)
|
|
|
|
rdev_for_each_rcu(rdev2, mddev2)
|
2007-03-01 11:11:35 +07:00
|
|
|
if (rdev->bdev->bd_contains ==
|
2008-07-21 14:05:25 +07:00
|
|
|
rdev2->bdev->bd_contains) {
|
|
|
|
rcu_read_unlock();
|
2007-03-01 11:11:35 +07:00
|
|
|
return 1;
|
2008-07-21 14:05:25 +07:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LIST_HEAD(pending_raid_disks);
|
|
|
|
|
2009-03-31 10:27:02 +07:00
|
|
|
static void md_integrity_check(mdk_rdev_t *rdev, mddev_t *mddev)
|
|
|
|
{
|
|
|
|
struct mdk_personality *pers = mddev->pers;
|
|
|
|
struct gendisk *disk = mddev->gendisk;
|
|
|
|
struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev);
|
|
|
|
struct blk_integrity *bi_mddev = blk_get_integrity(disk);
|
|
|
|
|
|
|
|
/* Data integrity passthrough not supported on RAID 4, 5 and 6 */
|
|
|
|
if (pers && pers->level >= 4 && pers->level <= 6)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* If rdev is integrity capable, register profile for mddev */
|
|
|
|
if (!bi_mddev && bi_rdev) {
|
|
|
|
if (blk_integrity_register(disk, bi_rdev))
|
|
|
|
printk(KERN_ERR "%s: %s Could not register integrity!\n",
|
|
|
|
__func__, disk->disk_name);
|
|
|
|
else
|
|
|
|
printk(KERN_NOTICE "Enabling data integrity on %s\n",
|
|
|
|
disk->disk_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that mddev and rdev have matching profiles */
|
|
|
|
if (blk_integrity_compare(disk, rdev->bdev->bd_disk) < 0) {
|
|
|
|
printk(KERN_ERR "%s: %s/%s integrity mismatch!\n", __func__,
|
|
|
|
disk->disk_name, rdev->bdev->bd_disk->disk_name);
|
|
|
|
printk(KERN_NOTICE "Disabling data integrity on %s\n",
|
|
|
|
disk->disk_name);
|
|
|
|
blk_integrity_unregister(disk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
|
|
|
|
{
|
2007-03-01 11:11:35 +07:00
|
|
|
char b[BDEVNAME_SIZE];
|
2005-11-09 12:39:37 +07:00
|
|
|
struct kobject *ko;
|
2006-01-12 16:05:23 +07:00
|
|
|
char *s;
|
2007-03-27 12:32:14 +07:00
|
|
|
int err;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (rdev->mddev) {
|
|
|
|
MD_BUG();
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-04-30 14:52:32 +07:00
|
|
|
|
|
|
|
/* prevent duplicates */
|
|
|
|
if (find_rdev(mddev, rdev->bdev->bd_dev))
|
|
|
|
return -EEXIST;
|
|
|
|
|
2006-01-06 15:20:55 +07:00
|
|
|
/* make sure rdev->size exceeds mddev->size */
|
|
|
|
if (rdev->size && (mddev->size == 0 || rdev->size < mddev->size)) {
|
2007-05-24 03:58:10 +07:00
|
|
|
if (mddev->pers) {
|
|
|
|
/* Cannot change size, so fail
|
|
|
|
* If mddev->level <= 0, then we don't care
|
|
|
|
* about aligning sizes (e.g. linear)
|
|
|
|
*/
|
|
|
|
if (mddev->level > 0)
|
|
|
|
return -ENOSPC;
|
|
|
|
} else
|
2006-01-06 15:20:55 +07:00
|
|
|
mddev->size = rdev->size;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Verify rdev->desc_nr is unique.
|
|
|
|
* If it is -1, assign a free number, else
|
|
|
|
* check number is not in use
|
|
|
|
*/
|
|
|
|
if (rdev->desc_nr < 0) {
|
|
|
|
int choice = 0;
|
|
|
|
if (mddev->pers) choice = mddev->raid_disks;
|
|
|
|
while (find_rdev_nr(mddev, choice))
|
|
|
|
choice++;
|
|
|
|
rdev->desc_nr = choice;
|
|
|
|
} else {
|
|
|
|
if (find_rdev_nr(mddev, rdev->desc_nr))
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2009-02-06 14:02:46 +07:00
|
|
|
if (mddev->max_disks && rdev->desc_nr >= mddev->max_disks) {
|
|
|
|
printk(KERN_WARNING "md: %s: array is limited to %d devices\n",
|
|
|
|
mdname(mddev), mddev->max_disks);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2005-11-09 12:39:35 +07:00
|
|
|
bdevname(rdev->bdev,b);
|
2007-12-18 13:05:35 +07:00
|
|
|
while ( (s=strchr(b, '/')) != NULL)
|
2006-01-12 16:05:23 +07:00
|
|
|
*s = '!';
|
2007-12-18 13:05:35 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->mddev = mddev;
|
2005-11-09 12:39:35 +07:00
|
|
|
printk(KERN_INFO "md: bind<%s>\n", b);
|
2005-11-09 12:39:24 +07:00
|
|
|
|
2007-12-18 13:05:35 +07:00
|
|
|
if ((err = kobject_add(&rdev->kobj, &mddev->kobj, "dev-%s", b)))
|
2007-03-27 12:32:14 +07:00
|
|
|
goto fail;
|
2005-11-09 12:39:24 +07:00
|
|
|
|
2008-08-25 17:56:12 +07:00
|
|
|
ko = &part_to_dev(rdev->bdev->bd_part)->kobj;
|
2007-03-27 12:32:14 +07:00
|
|
|
if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) {
|
|
|
|
kobject_del(&rdev->kobj);
|
|
|
|
goto fail;
|
|
|
|
}
|
2008-10-21 09:25:28 +07:00
|
|
|
rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, "state");
|
|
|
|
|
2008-07-21 14:05:25 +07:00
|
|
|
list_add_rcu(&rdev->same_set, &mddev->disks);
|
2008-02-06 16:39:54 +07:00
|
|
|
bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk);
|
2009-01-09 04:31:11 +07:00
|
|
|
|
|
|
|
/* May as well allow recovery to be retried once */
|
|
|
|
mddev->recovery_disabled = 0;
|
2009-03-31 10:27:02 +07:00
|
|
|
|
|
|
|
md_integrity_check(rdev, mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
2007-03-27 12:32:14 +07:00
|
|
|
|
|
|
|
fail:
|
|
|
|
printk(KERN_WARNING "md: failed to register dev-%s for %s\n",
|
|
|
|
b, mdname(mddev));
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-02-06 16:39:56 +07:00
|
|
|
static void md_delayed_delete(struct work_struct *ws)
|
2007-04-05 09:08:18 +07:00
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev = container_of(ws, mdk_rdev_t, del_work);
|
|
|
|
kobject_del(&rdev->kobj);
|
2008-02-06 16:39:56 +07:00
|
|
|
kobject_put(&rdev->kobj);
|
2007-04-05 09:08:18 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void unbind_rdev_from_array(mdk_rdev_t * rdev)
|
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
if (!rdev->mddev) {
|
|
|
|
MD_BUG();
|
|
|
|
return;
|
|
|
|
}
|
2006-03-27 16:17:58 +07:00
|
|
|
bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk);
|
2008-07-21 14:05:25 +07:00
|
|
|
list_del_rcu(&rdev->same_set);
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b));
|
|
|
|
rdev->mddev = NULL;
|
2005-11-09 12:39:24 +07:00
|
|
|
sysfs_remove_link(&rdev->kobj, "block");
|
2008-10-21 09:25:28 +07:00
|
|
|
sysfs_put(rdev->sysfs_state);
|
|
|
|
rdev->sysfs_state = NULL;
|
2007-04-05 09:08:18 +07:00
|
|
|
/* We need to delay this, otherwise we can deadlock when
|
2008-07-21 14:05:25 +07:00
|
|
|
* writing to 'remove' to "dev/state". We also need
|
|
|
|
* to delay it due to rcu usage.
|
2007-04-05 09:08:18 +07:00
|
|
|
*/
|
2008-07-21 14:05:25 +07:00
|
|
|
synchronize_rcu();
|
2008-02-06 16:39:56 +07:00
|
|
|
INIT_WORK(&rdev->del_work, md_delayed_delete);
|
|
|
|
kobject_get(&rdev->kobj);
|
2007-04-05 09:08:18 +07:00
|
|
|
schedule_work(&rdev->del_work);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* prevent the device from being mounted, repartitioned or
|
|
|
|
* otherwise reused by a RAID array (or any other kernel
|
|
|
|
* subsystem), by bd_claiming the device.
|
|
|
|
*/
|
2008-02-06 16:39:54 +07:00
|
|
|
static int lock_rdev(mdk_rdev_t *rdev, dev_t dev, int shared)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct block_device *bdev;
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
|
2006-12-08 17:36:13 +07:00
|
|
|
bdev = open_by_devnum(dev, FMODE_READ|FMODE_WRITE);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (IS_ERR(bdev)) {
|
|
|
|
printk(KERN_ERR "md: could not open %s.\n",
|
|
|
|
__bdevname(dev, b));
|
|
|
|
return PTR_ERR(bdev);
|
|
|
|
}
|
2008-02-06 16:39:54 +07:00
|
|
|
err = bd_claim(bdev, shared ? (mdk_rdev_t *)lock_rdev : rdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "md: could not bd_claim %s.\n",
|
|
|
|
bdevname(bdev, b));
|
2008-02-23 08:40:24 +07:00
|
|
|
blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
2008-02-06 16:39:54 +07:00
|
|
|
if (!shared)
|
|
|
|
set_bit(AllReserved, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->bdev = bdev;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unlock_rdev(mdk_rdev_t *rdev)
|
|
|
|
{
|
|
|
|
struct block_device *bdev = rdev->bdev;
|
|
|
|
rdev->bdev = NULL;
|
|
|
|
if (!bdev)
|
|
|
|
MD_BUG();
|
|
|
|
bd_release(bdev);
|
2008-02-23 08:40:24 +07:00
|
|
|
blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void md_autodetect_dev(dev_t dev);
|
|
|
|
|
|
|
|
static void export_rdev(mdk_rdev_t * rdev)
|
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
printk(KERN_INFO "md: export_rdev(%s)\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
if (rdev->mddev)
|
|
|
|
MD_BUG();
|
|
|
|
free_disk_sb(rdev);
|
|
|
|
#ifndef MODULE
|
2008-03-05 05:29:31 +07:00
|
|
|
if (test_bit(AutoDetected, &rdev->flags))
|
|
|
|
md_autodetect_dev(rdev->bdev->bd_dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
unlock_rdev(rdev);
|
2005-11-09 12:39:24 +07:00
|
|
|
kobject_put(&rdev->kobj);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void kick_rdev_from_array(mdk_rdev_t * rdev)
|
|
|
|
{
|
|
|
|
unbind_rdev_from_array(rdev);
|
|
|
|
export_rdev(rdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void export_array(mddev_t *mddev)
|
|
|
|
{
|
2009-01-09 04:31:08 +07:00
|
|
|
mdk_rdev_t *rdev, *tmp;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-02-06 16:39:59 +07:00
|
|
|
rdev_for_each(rdev, tmp, mddev) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!rdev->mddev) {
|
|
|
|
MD_BUG();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
kick_rdev_from_array(rdev);
|
|
|
|
}
|
|
|
|
if (!list_empty(&mddev->disks))
|
|
|
|
MD_BUG();
|
|
|
|
mddev->raid_disks = 0;
|
|
|
|
mddev->major_version = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_desc(mdp_disk_t *desc)
|
|
|
|
{
|
|
|
|
printk(" DISK<N:%d,(%d,%d),R:%d,S:%d>\n", desc->number,
|
|
|
|
desc->major,desc->minor,desc->raid_disk,desc->state);
|
|
|
|
}
|
|
|
|
|
md: need another print_sb for mdp_superblock_1
md_print_devices is called in two code path: MD_BUG(...), and md_ioctl
with PRINT_RAID_DEBUG. it will dump out all in use md devices
information;
However, it wrongly processed two types of superblock in one:
The header file <linux/raid/md_p.h> has defined two types of superblock,
struct mdp_superblock_s (typedefed with mdp_super_t) according to md with
metadata 0.90, and struct mdp_superblock_1 according to md with metadata
1.0 and later,
These two types of superblock are very different,
The md_print_devices code processed them both in mdp_super_t, that would
lead to wrong informaton dump like:
[ 6742.345877]
[ 6742.345887] md: **********************************
[ 6742.345890] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 6742.345892] md: **********************************
[ 6742.345896] md1: <ram7><ram6><ram5><ram4>
[ 6742.345907] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 6742.345909] md: rdev superblock:
[ 6742.345914] md: SB: (V:0.90.0) ID:<42ef13c7.598c059a.5f9f1645.801e9ee6> CT:4919856d
[ 6742.345918] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 6742.345922] md: UT:4919856d ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:b7992907 E:00000001
[ 6742.345924] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 6742.345930] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 6742.345933] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 6742.345937] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 6742.345942] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 6742.346058] md0: <ram3><ram2><ram1><ram0>
[ 6742.346067] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 6742.346070] md: rdev superblock:
[ 6742.346073] md: SB: (V:1.0.0) ID:<369aad81.00000000.00000000.00000000> CT:9a322a9c
[ 6742.346077] md: L-1507699579 S976570180 ND:48 RD:0 md0 LO:65536 CS:196610
[ 6742.346081] md: UT:00000018 ST:0 AD:131048 WD:0 FD:8 SD:0 CSUM:00000000 E:00000000
[ 6742.346084] D 0: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346089] D 1: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346092] D 2: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346096] D 3: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346102] md: THIS: DISK<N:0,(0,0),R:0,S:0>
...
[ 6742.346219] md: **********************************
[ 6742.346221]
Here md1 is metadata 0.90.0, and md0 is metadata 1.2
After some more code to distinguish these two types of superblock, in this patch,
it will generate dump information like:
[ 7906.755790]
[ 7906.755799] md: **********************************
[ 7906.755802] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 7906.755804] md: **********************************
[ 7906.755808] md1: <ram7><ram6><ram5><ram4>
[ 7906.755819] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 7906.755821] md: rdev superblock (MJ:0):
[ 7906.755826] md: SB: (V:0.90.0) ID:<3fca7a0d.a612bfed.5f9f1645.801e9ee6> CT:491989f3
[ 7906.755830] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 7906.755834] md: UT:491989f3 ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:00fb52ad E:00000001
[ 7906.755836] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 7906.755842] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 7906.755845] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 7906.755849] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 7906.755855] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 7906.755972] md0: <ram3><ram2><ram1><ram0>
[ 7906.755981] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 7906.755984] md: rdev superblock (MJ:1):
[ 7906.755989] md: SB: (V:1) (F:0) Array-ID:<5fbcf158:55aa:5fbe:9a79:1e939880dcbd>
[ 7906.755990] md: Name: "DG5:0" CT:1226410480
[ 7906.755998] md: L5 SZ130944 RD:4 LO:2 CS:128 DO:24 DS:131048 SO:8 RO:0
[ 7906.755999] md: Dev:00000003 UUID: 9194d744:87f7:a448:85f2:7497b84ce30a
[ 7906.756001] md: (F:0) UT:1226410480 Events:0 ResyncOffset:-1 CSUM:0dbcd829
[ 7906.756003] md: (MaxDev:384)
...
[ 7906.756113] md: **********************************
[ 7906.756116]
this md0 (metadata 1.2) information dumping is exactly according to struct
mdp_superblock_1.
Signed-off-by: Cheng Renquan <crquan@gmail.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Dan Williams <dan.j.williams@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: NeilBrown <neilb@suse.de>
2009-01-09 04:31:08 +07:00
|
|
|
static void print_sb_90(mdp_super_t *sb)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printk(KERN_INFO
|
|
|
|
"md: SB: (V:%d.%d.%d) ID:<%08x.%08x.%08x.%08x> CT:%08x\n",
|
|
|
|
sb->major_version, sb->minor_version, sb->patch_version,
|
|
|
|
sb->set_uuid0, sb->set_uuid1, sb->set_uuid2, sb->set_uuid3,
|
|
|
|
sb->ctime);
|
|
|
|
printk(KERN_INFO "md: L%d S%08d ND:%d RD:%d md%d LO:%d CS:%d\n",
|
|
|
|
sb->level, sb->size, sb->nr_disks, sb->raid_disks,
|
|
|
|
sb->md_minor, sb->layout, sb->chunk_size);
|
|
|
|
printk(KERN_INFO "md: UT:%08x ST:%d AD:%d WD:%d"
|
|
|
|
" FD:%d SD:%d CSUM:%08x E:%08lx\n",
|
|
|
|
sb->utime, sb->state, sb->active_disks, sb->working_disks,
|
|
|
|
sb->failed_disks, sb->spare_disks,
|
|
|
|
sb->sb_csum, (unsigned long)sb->events_lo);
|
|
|
|
|
|
|
|
printk(KERN_INFO);
|
|
|
|
for (i = 0; i < MD_SB_DISKS; i++) {
|
|
|
|
mdp_disk_t *desc;
|
|
|
|
|
|
|
|
desc = sb->disks + i;
|
|
|
|
if (desc->number || desc->major || desc->minor ||
|
|
|
|
desc->raid_disk || (desc->state && (desc->state != 4))) {
|
|
|
|
printk(" D %2d: ", i);
|
|
|
|
print_desc(desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printk(KERN_INFO "md: THIS: ");
|
|
|
|
print_desc(&sb->this_disk);
|
md: need another print_sb for mdp_superblock_1
md_print_devices is called in two code path: MD_BUG(...), and md_ioctl
with PRINT_RAID_DEBUG. it will dump out all in use md devices
information;
However, it wrongly processed two types of superblock in one:
The header file <linux/raid/md_p.h> has defined two types of superblock,
struct mdp_superblock_s (typedefed with mdp_super_t) according to md with
metadata 0.90, and struct mdp_superblock_1 according to md with metadata
1.0 and later,
These two types of superblock are very different,
The md_print_devices code processed them both in mdp_super_t, that would
lead to wrong informaton dump like:
[ 6742.345877]
[ 6742.345887] md: **********************************
[ 6742.345890] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 6742.345892] md: **********************************
[ 6742.345896] md1: <ram7><ram6><ram5><ram4>
[ 6742.345907] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 6742.345909] md: rdev superblock:
[ 6742.345914] md: SB: (V:0.90.0) ID:<42ef13c7.598c059a.5f9f1645.801e9ee6> CT:4919856d
[ 6742.345918] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 6742.345922] md: UT:4919856d ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:b7992907 E:00000001
[ 6742.345924] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 6742.345930] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 6742.345933] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 6742.345937] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 6742.345942] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 6742.346058] md0: <ram3><ram2><ram1><ram0>
[ 6742.346067] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 6742.346070] md: rdev superblock:
[ 6742.346073] md: SB: (V:1.0.0) ID:<369aad81.00000000.00000000.00000000> CT:9a322a9c
[ 6742.346077] md: L-1507699579 S976570180 ND:48 RD:0 md0 LO:65536 CS:196610
[ 6742.346081] md: UT:00000018 ST:0 AD:131048 WD:0 FD:8 SD:0 CSUM:00000000 E:00000000
[ 6742.346084] D 0: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346089] D 1: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346092] D 2: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346096] D 3: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346102] md: THIS: DISK<N:0,(0,0),R:0,S:0>
...
[ 6742.346219] md: **********************************
[ 6742.346221]
Here md1 is metadata 0.90.0, and md0 is metadata 1.2
After some more code to distinguish these two types of superblock, in this patch,
it will generate dump information like:
[ 7906.755790]
[ 7906.755799] md: **********************************
[ 7906.755802] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 7906.755804] md: **********************************
[ 7906.755808] md1: <ram7><ram6><ram5><ram4>
[ 7906.755819] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 7906.755821] md: rdev superblock (MJ:0):
[ 7906.755826] md: SB: (V:0.90.0) ID:<3fca7a0d.a612bfed.5f9f1645.801e9ee6> CT:491989f3
[ 7906.755830] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 7906.755834] md: UT:491989f3 ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:00fb52ad E:00000001
[ 7906.755836] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 7906.755842] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 7906.755845] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 7906.755849] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 7906.755855] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 7906.755972] md0: <ram3><ram2><ram1><ram0>
[ 7906.755981] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 7906.755984] md: rdev superblock (MJ:1):
[ 7906.755989] md: SB: (V:1) (F:0) Array-ID:<5fbcf158:55aa:5fbe:9a79:1e939880dcbd>
[ 7906.755990] md: Name: "DG5:0" CT:1226410480
[ 7906.755998] md: L5 SZ130944 RD:4 LO:2 CS:128 DO:24 DS:131048 SO:8 RO:0
[ 7906.755999] md: Dev:00000003 UUID: 9194d744:87f7:a448:85f2:7497b84ce30a
[ 7906.756001] md: (F:0) UT:1226410480 Events:0 ResyncOffset:-1 CSUM:0dbcd829
[ 7906.756003] md: (MaxDev:384)
...
[ 7906.756113] md: **********************************
[ 7906.756116]
this md0 (metadata 1.2) information dumping is exactly according to struct
mdp_superblock_1.
Signed-off-by: Cheng Renquan <crquan@gmail.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Dan Williams <dan.j.williams@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: NeilBrown <neilb@suse.de>
2009-01-09 04:31:08 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
md: need another print_sb for mdp_superblock_1
md_print_devices is called in two code path: MD_BUG(...), and md_ioctl
with PRINT_RAID_DEBUG. it will dump out all in use md devices
information;
However, it wrongly processed two types of superblock in one:
The header file <linux/raid/md_p.h> has defined two types of superblock,
struct mdp_superblock_s (typedefed with mdp_super_t) according to md with
metadata 0.90, and struct mdp_superblock_1 according to md with metadata
1.0 and later,
These two types of superblock are very different,
The md_print_devices code processed them both in mdp_super_t, that would
lead to wrong informaton dump like:
[ 6742.345877]
[ 6742.345887] md: **********************************
[ 6742.345890] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 6742.345892] md: **********************************
[ 6742.345896] md1: <ram7><ram6><ram5><ram4>
[ 6742.345907] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 6742.345909] md: rdev superblock:
[ 6742.345914] md: SB: (V:0.90.0) ID:<42ef13c7.598c059a.5f9f1645.801e9ee6> CT:4919856d
[ 6742.345918] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 6742.345922] md: UT:4919856d ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:b7992907 E:00000001
[ 6742.345924] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 6742.345930] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 6742.345933] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 6742.345937] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 6742.345942] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 6742.346058] md0: <ram3><ram2><ram1><ram0>
[ 6742.346067] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 6742.346070] md: rdev superblock:
[ 6742.346073] md: SB: (V:1.0.0) ID:<369aad81.00000000.00000000.00000000> CT:9a322a9c
[ 6742.346077] md: L-1507699579 S976570180 ND:48 RD:0 md0 LO:65536 CS:196610
[ 6742.346081] md: UT:00000018 ST:0 AD:131048 WD:0 FD:8 SD:0 CSUM:00000000 E:00000000
[ 6742.346084] D 0: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346089] D 1: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346092] D 2: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346096] D 3: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346102] md: THIS: DISK<N:0,(0,0),R:0,S:0>
...
[ 6742.346219] md: **********************************
[ 6742.346221]
Here md1 is metadata 0.90.0, and md0 is metadata 1.2
After some more code to distinguish these two types of superblock, in this patch,
it will generate dump information like:
[ 7906.755790]
[ 7906.755799] md: **********************************
[ 7906.755802] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 7906.755804] md: **********************************
[ 7906.755808] md1: <ram7><ram6><ram5><ram4>
[ 7906.755819] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 7906.755821] md: rdev superblock (MJ:0):
[ 7906.755826] md: SB: (V:0.90.0) ID:<3fca7a0d.a612bfed.5f9f1645.801e9ee6> CT:491989f3
[ 7906.755830] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 7906.755834] md: UT:491989f3 ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:00fb52ad E:00000001
[ 7906.755836] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 7906.755842] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 7906.755845] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 7906.755849] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 7906.755855] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 7906.755972] md0: <ram3><ram2><ram1><ram0>
[ 7906.755981] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 7906.755984] md: rdev superblock (MJ:1):
[ 7906.755989] md: SB: (V:1) (F:0) Array-ID:<5fbcf158:55aa:5fbe:9a79:1e939880dcbd>
[ 7906.755990] md: Name: "DG5:0" CT:1226410480
[ 7906.755998] md: L5 SZ130944 RD:4 LO:2 CS:128 DO:24 DS:131048 SO:8 RO:0
[ 7906.755999] md: Dev:00000003 UUID: 9194d744:87f7:a448:85f2:7497b84ce30a
[ 7906.756001] md: (F:0) UT:1226410480 Events:0 ResyncOffset:-1 CSUM:0dbcd829
[ 7906.756003] md: (MaxDev:384)
...
[ 7906.756113] md: **********************************
[ 7906.756116]
this md0 (metadata 1.2) information dumping is exactly according to struct
mdp_superblock_1.
Signed-off-by: Cheng Renquan <crquan@gmail.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Dan Williams <dan.j.williams@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: NeilBrown <neilb@suse.de>
2009-01-09 04:31:08 +07:00
|
|
|
static void print_sb_1(struct mdp_superblock_1 *sb)
|
|
|
|
{
|
|
|
|
__u8 *uuid;
|
|
|
|
|
|
|
|
uuid = sb->set_uuid;
|
|
|
|
printk(KERN_INFO "md: SB: (V:%u) (F:0x%08x) Array-ID:<%02x%02x%02x%02x"
|
|
|
|
":%02x%02x:%02x%02x:%02x%02x:%02x%02x%02x%02x%02x%02x>\n"
|
|
|
|
KERN_INFO "md: Name: \"%s\" CT:%llu\n",
|
|
|
|
le32_to_cpu(sb->major_version),
|
|
|
|
le32_to_cpu(sb->feature_map),
|
|
|
|
uuid[0], uuid[1], uuid[2], uuid[3],
|
|
|
|
uuid[4], uuid[5], uuid[6], uuid[7],
|
|
|
|
uuid[8], uuid[9], uuid[10], uuid[11],
|
|
|
|
uuid[12], uuid[13], uuid[14], uuid[15],
|
|
|
|
sb->set_name,
|
|
|
|
(unsigned long long)le64_to_cpu(sb->ctime)
|
|
|
|
& MD_SUPERBLOCK_1_TIME_SEC_MASK);
|
|
|
|
|
|
|
|
uuid = sb->device_uuid;
|
|
|
|
printk(KERN_INFO "md: L%u SZ%llu RD:%u LO:%u CS:%u DO:%llu DS:%llu SO:%llu"
|
|
|
|
" RO:%llu\n"
|
|
|
|
KERN_INFO "md: Dev:%08x UUID: %02x%02x%02x%02x:%02x%02x:%02x%02x:%02x%02x"
|
|
|
|
":%02x%02x%02x%02x%02x%02x\n"
|
|
|
|
KERN_INFO "md: (F:0x%08x) UT:%llu Events:%llu ResyncOffset:%llu CSUM:0x%08x\n"
|
|
|
|
KERN_INFO "md: (MaxDev:%u) \n",
|
|
|
|
le32_to_cpu(sb->level),
|
|
|
|
(unsigned long long)le64_to_cpu(sb->size),
|
|
|
|
le32_to_cpu(sb->raid_disks),
|
|
|
|
le32_to_cpu(sb->layout),
|
|
|
|
le32_to_cpu(sb->chunksize),
|
|
|
|
(unsigned long long)le64_to_cpu(sb->data_offset),
|
|
|
|
(unsigned long long)le64_to_cpu(sb->data_size),
|
|
|
|
(unsigned long long)le64_to_cpu(sb->super_offset),
|
|
|
|
(unsigned long long)le64_to_cpu(sb->recovery_offset),
|
|
|
|
le32_to_cpu(sb->dev_number),
|
|
|
|
uuid[0], uuid[1], uuid[2], uuid[3],
|
|
|
|
uuid[4], uuid[5], uuid[6], uuid[7],
|
|
|
|
uuid[8], uuid[9], uuid[10], uuid[11],
|
|
|
|
uuid[12], uuid[13], uuid[14], uuid[15],
|
|
|
|
sb->devflags,
|
|
|
|
(unsigned long long)le64_to_cpu(sb->utime) & MD_SUPERBLOCK_1_TIME_SEC_MASK,
|
|
|
|
(unsigned long long)le64_to_cpu(sb->events),
|
|
|
|
(unsigned long long)le64_to_cpu(sb->resync_offset),
|
|
|
|
le32_to_cpu(sb->sb_csum),
|
|
|
|
le32_to_cpu(sb->max_dev)
|
|
|
|
);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
md: need another print_sb for mdp_superblock_1
md_print_devices is called in two code path: MD_BUG(...), and md_ioctl
with PRINT_RAID_DEBUG. it will dump out all in use md devices
information;
However, it wrongly processed two types of superblock in one:
The header file <linux/raid/md_p.h> has defined two types of superblock,
struct mdp_superblock_s (typedefed with mdp_super_t) according to md with
metadata 0.90, and struct mdp_superblock_1 according to md with metadata
1.0 and later,
These two types of superblock are very different,
The md_print_devices code processed them both in mdp_super_t, that would
lead to wrong informaton dump like:
[ 6742.345877]
[ 6742.345887] md: **********************************
[ 6742.345890] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 6742.345892] md: **********************************
[ 6742.345896] md1: <ram7><ram6><ram5><ram4>
[ 6742.345907] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 6742.345909] md: rdev superblock:
[ 6742.345914] md: SB: (V:0.90.0) ID:<42ef13c7.598c059a.5f9f1645.801e9ee6> CT:4919856d
[ 6742.345918] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 6742.345922] md: UT:4919856d ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:b7992907 E:00000001
[ 6742.345924] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 6742.345930] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 6742.345933] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 6742.345937] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 6742.345942] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 6742.346058] md0: <ram3><ram2><ram1><ram0>
[ 6742.346067] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 6742.346070] md: rdev superblock:
[ 6742.346073] md: SB: (V:1.0.0) ID:<369aad81.00000000.00000000.00000000> CT:9a322a9c
[ 6742.346077] md: L-1507699579 S976570180 ND:48 RD:0 md0 LO:65536 CS:196610
[ 6742.346081] md: UT:00000018 ST:0 AD:131048 WD:0 FD:8 SD:0 CSUM:00000000 E:00000000
[ 6742.346084] D 0: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346089] D 1: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346092] D 2: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346096] D 3: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346102] md: THIS: DISK<N:0,(0,0),R:0,S:0>
...
[ 6742.346219] md: **********************************
[ 6742.346221]
Here md1 is metadata 0.90.0, and md0 is metadata 1.2
After some more code to distinguish these two types of superblock, in this patch,
it will generate dump information like:
[ 7906.755790]
[ 7906.755799] md: **********************************
[ 7906.755802] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 7906.755804] md: **********************************
[ 7906.755808] md1: <ram7><ram6><ram5><ram4>
[ 7906.755819] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 7906.755821] md: rdev superblock (MJ:0):
[ 7906.755826] md: SB: (V:0.90.0) ID:<3fca7a0d.a612bfed.5f9f1645.801e9ee6> CT:491989f3
[ 7906.755830] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 7906.755834] md: UT:491989f3 ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:00fb52ad E:00000001
[ 7906.755836] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 7906.755842] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 7906.755845] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 7906.755849] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 7906.755855] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 7906.755972] md0: <ram3><ram2><ram1><ram0>
[ 7906.755981] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 7906.755984] md: rdev superblock (MJ:1):
[ 7906.755989] md: SB: (V:1) (F:0) Array-ID:<5fbcf158:55aa:5fbe:9a79:1e939880dcbd>
[ 7906.755990] md: Name: "DG5:0" CT:1226410480
[ 7906.755998] md: L5 SZ130944 RD:4 LO:2 CS:128 DO:24 DS:131048 SO:8 RO:0
[ 7906.755999] md: Dev:00000003 UUID: 9194d744:87f7:a448:85f2:7497b84ce30a
[ 7906.756001] md: (F:0) UT:1226410480 Events:0 ResyncOffset:-1 CSUM:0dbcd829
[ 7906.756003] md: (MaxDev:384)
...
[ 7906.756113] md: **********************************
[ 7906.756116]
this md0 (metadata 1.2) information dumping is exactly according to struct
mdp_superblock_1.
Signed-off-by: Cheng Renquan <crquan@gmail.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Dan Williams <dan.j.williams@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: NeilBrown <neilb@suse.de>
2009-01-09 04:31:08 +07:00
|
|
|
static void print_rdev(mdk_rdev_t *rdev, int major_version)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
printk(KERN_INFO "md: rdev %s, SZ:%08llu F:%d S:%d DN:%u\n",
|
|
|
|
bdevname(rdev->bdev,b), (unsigned long long)rdev->size,
|
2005-11-09 12:39:31 +07:00
|
|
|
test_bit(Faulty, &rdev->flags), test_bit(In_sync, &rdev->flags),
|
|
|
|
rdev->desc_nr);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rdev->sb_loaded) {
|
md: need another print_sb for mdp_superblock_1
md_print_devices is called in two code path: MD_BUG(...), and md_ioctl
with PRINT_RAID_DEBUG. it will dump out all in use md devices
information;
However, it wrongly processed two types of superblock in one:
The header file <linux/raid/md_p.h> has defined two types of superblock,
struct mdp_superblock_s (typedefed with mdp_super_t) according to md with
metadata 0.90, and struct mdp_superblock_1 according to md with metadata
1.0 and later,
These two types of superblock are very different,
The md_print_devices code processed them both in mdp_super_t, that would
lead to wrong informaton dump like:
[ 6742.345877]
[ 6742.345887] md: **********************************
[ 6742.345890] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 6742.345892] md: **********************************
[ 6742.345896] md1: <ram7><ram6><ram5><ram4>
[ 6742.345907] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 6742.345909] md: rdev superblock:
[ 6742.345914] md: SB: (V:0.90.0) ID:<42ef13c7.598c059a.5f9f1645.801e9ee6> CT:4919856d
[ 6742.345918] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 6742.345922] md: UT:4919856d ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:b7992907 E:00000001
[ 6742.345924] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 6742.345930] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 6742.345933] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 6742.345937] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 6742.345942] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 6742.346058] md0: <ram3><ram2><ram1><ram0>
[ 6742.346067] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 6742.346070] md: rdev superblock:
[ 6742.346073] md: SB: (V:1.0.0) ID:<369aad81.00000000.00000000.00000000> CT:9a322a9c
[ 6742.346077] md: L-1507699579 S976570180 ND:48 RD:0 md0 LO:65536 CS:196610
[ 6742.346081] md: UT:00000018 ST:0 AD:131048 WD:0 FD:8 SD:0 CSUM:00000000 E:00000000
[ 6742.346084] D 0: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346089] D 1: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346092] D 2: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346096] D 3: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346102] md: THIS: DISK<N:0,(0,0),R:0,S:0>
...
[ 6742.346219] md: **********************************
[ 6742.346221]
Here md1 is metadata 0.90.0, and md0 is metadata 1.2
After some more code to distinguish these two types of superblock, in this patch,
it will generate dump information like:
[ 7906.755790]
[ 7906.755799] md: **********************************
[ 7906.755802] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 7906.755804] md: **********************************
[ 7906.755808] md1: <ram7><ram6><ram5><ram4>
[ 7906.755819] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 7906.755821] md: rdev superblock (MJ:0):
[ 7906.755826] md: SB: (V:0.90.0) ID:<3fca7a0d.a612bfed.5f9f1645.801e9ee6> CT:491989f3
[ 7906.755830] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 7906.755834] md: UT:491989f3 ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:00fb52ad E:00000001
[ 7906.755836] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 7906.755842] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 7906.755845] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 7906.755849] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 7906.755855] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 7906.755972] md0: <ram3><ram2><ram1><ram0>
[ 7906.755981] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 7906.755984] md: rdev superblock (MJ:1):
[ 7906.755989] md: SB: (V:1) (F:0) Array-ID:<5fbcf158:55aa:5fbe:9a79:1e939880dcbd>
[ 7906.755990] md: Name: "DG5:0" CT:1226410480
[ 7906.755998] md: L5 SZ130944 RD:4 LO:2 CS:128 DO:24 DS:131048 SO:8 RO:0
[ 7906.755999] md: Dev:00000003 UUID: 9194d744:87f7:a448:85f2:7497b84ce30a
[ 7906.756001] md: (F:0) UT:1226410480 Events:0 ResyncOffset:-1 CSUM:0dbcd829
[ 7906.756003] md: (MaxDev:384)
...
[ 7906.756113] md: **********************************
[ 7906.756116]
this md0 (metadata 1.2) information dumping is exactly according to struct
mdp_superblock_1.
Signed-off-by: Cheng Renquan <crquan@gmail.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Dan Williams <dan.j.williams@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: NeilBrown <neilb@suse.de>
2009-01-09 04:31:08 +07:00
|
|
|
printk(KERN_INFO "md: rdev superblock (MJ:%d):\n", major_version);
|
|
|
|
switch (major_version) {
|
|
|
|
case 0:
|
|
|
|
print_sb_90((mdp_super_t*)page_address(rdev->sb_page));
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
print_sb_1((struct mdp_superblock_1 *)page_address(rdev->sb_page));
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
} else
|
|
|
|
printk(KERN_INFO "md: no rdev superblock!\n");
|
|
|
|
}
|
|
|
|
|
2006-06-26 14:27:42 +07:00
|
|
|
static void md_print_devices(void)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-01-09 04:31:08 +07:00
|
|
|
struct list_head *tmp;
|
2005-04-17 05:20:36 +07:00
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
mddev_t *mddev;
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
|
|
|
|
printk("\n");
|
|
|
|
printk("md: **********************************\n");
|
|
|
|
printk("md: * <COMPLETE RAID STATE PRINTOUT> *\n");
|
|
|
|
printk("md: **********************************\n");
|
2008-02-06 16:39:58 +07:00
|
|
|
for_each_mddev(mddev, tmp) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
if (mddev->bitmap)
|
|
|
|
bitmap_print_sb(mddev->bitmap);
|
|
|
|
else
|
|
|
|
printk("%s: ", mdname(mddev));
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("<%s>", bdevname(rdev->bdev,b));
|
|
|
|
printk("\n");
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
md: need another print_sb for mdp_superblock_1
md_print_devices is called in two code path: MD_BUG(...), and md_ioctl
with PRINT_RAID_DEBUG. it will dump out all in use md devices
information;
However, it wrongly processed two types of superblock in one:
The header file <linux/raid/md_p.h> has defined two types of superblock,
struct mdp_superblock_s (typedefed with mdp_super_t) according to md with
metadata 0.90, and struct mdp_superblock_1 according to md with metadata
1.0 and later,
These two types of superblock are very different,
The md_print_devices code processed them both in mdp_super_t, that would
lead to wrong informaton dump like:
[ 6742.345877]
[ 6742.345887] md: **********************************
[ 6742.345890] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 6742.345892] md: **********************************
[ 6742.345896] md1: <ram7><ram6><ram5><ram4>
[ 6742.345907] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 6742.345909] md: rdev superblock:
[ 6742.345914] md: SB: (V:0.90.0) ID:<42ef13c7.598c059a.5f9f1645.801e9ee6> CT:4919856d
[ 6742.345918] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 6742.345922] md: UT:4919856d ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:b7992907 E:00000001
[ 6742.345924] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 6742.345930] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 6742.345933] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 6742.345937] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 6742.345942] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 6742.346058] md0: <ram3><ram2><ram1><ram0>
[ 6742.346067] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 6742.346070] md: rdev superblock:
[ 6742.346073] md: SB: (V:1.0.0) ID:<369aad81.00000000.00000000.00000000> CT:9a322a9c
[ 6742.346077] md: L-1507699579 S976570180 ND:48 RD:0 md0 LO:65536 CS:196610
[ 6742.346081] md: UT:00000018 ST:0 AD:131048 WD:0 FD:8 SD:0 CSUM:00000000 E:00000000
[ 6742.346084] D 0: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346089] D 1: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346092] D 2: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346096] D 3: DISK<N:-1,(-1,-1),R:-1,S:-1>
[ 6742.346102] md: THIS: DISK<N:0,(0,0),R:0,S:0>
...
[ 6742.346219] md: **********************************
[ 6742.346221]
Here md1 is metadata 0.90.0, and md0 is metadata 1.2
After some more code to distinguish these two types of superblock, in this patch,
it will generate dump information like:
[ 7906.755790]
[ 7906.755799] md: **********************************
[ 7906.755802] md: * <COMPLETE RAID STATE PRINTOUT> *
[ 7906.755804] md: **********************************
[ 7906.755808] md1: <ram7><ram6><ram5><ram4>
[ 7906.755819] md: rdev ram7, SZ:00065472 F:0 S:1 DN:3
[ 7906.755821] md: rdev superblock (MJ:0):
[ 7906.755826] md: SB: (V:0.90.0) ID:<3fca7a0d.a612bfed.5f9f1645.801e9ee6> CT:491989f3
[ 7906.755830] md: L5 S00065472 ND:4 RD:4 md1 LO:2 CS:65536
[ 7906.755834] md: UT:491989f3 ST:1 AD:4 WD:4 FD:0 SD:0 CSUM:00fb52ad E:00000001
[ 7906.755836] D 0: DISK<N:0,(1,8),R:0,S:6>
[ 7906.755842] D 1: DISK<N:1,(1,10),R:1,S:6>
[ 7906.755845] D 2: DISK<N:2,(1,12),R:2,S:6>
[ 7906.755849] D 3: DISK<N:3,(1,14),R:3,S:6>
[ 7906.755855] md: THIS: DISK<N:3,(1,14),R:3,S:6>
...
[ 7906.755972] md0: <ram3><ram2><ram1><ram0>
[ 7906.755981] md: rdev ram3, SZ:00065472 F:0 S:1 DN:3
[ 7906.755984] md: rdev superblock (MJ:1):
[ 7906.755989] md: SB: (V:1) (F:0) Array-ID:<5fbcf158:55aa:5fbe:9a79:1e939880dcbd>
[ 7906.755990] md: Name: "DG5:0" CT:1226410480
[ 7906.755998] md: L5 SZ130944 RD:4 LO:2 CS:128 DO:24 DS:131048 SO:8 RO:0
[ 7906.755999] md: Dev:00000003 UUID: 9194d744:87f7:a448:85f2:7497b84ce30a
[ 7906.756001] md: (F:0) UT:1226410480 Events:0 ResyncOffset:-1 CSUM:0dbcd829
[ 7906.756003] md: (MaxDev:384)
...
[ 7906.756113] md: **********************************
[ 7906.756116]
this md0 (metadata 1.2) information dumping is exactly according to struct
mdp_superblock_1.
Signed-off-by: Cheng Renquan <crquan@gmail.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Dan Williams <dan.j.williams@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: NeilBrown <neilb@suse.de>
2009-01-09 04:31:08 +07:00
|
|
|
print_rdev(rdev, mddev->major_version);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
printk("md: **********************************\n");
|
|
|
|
printk("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-26 14:27:57 +07:00
|
|
|
static void sync_sbs(mddev_t * mddev, int nospares)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-06-26 14:27:57 +07:00
|
|
|
/* Update each superblock (in-memory image), but
|
|
|
|
* if we are allowed to, skip spares which already
|
|
|
|
* have the right event counter, or have one earlier
|
|
|
|
* (which would mean they aren't being marked as dirty
|
|
|
|
* with the rest of the array)
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2006-06-26 14:27:57 +07:00
|
|
|
if (rdev->sb_events == mddev->events ||
|
|
|
|
(nospares &&
|
|
|
|
rdev->raid_disk < 0 &&
|
|
|
|
(rdev->sb_events&1)==0 &&
|
|
|
|
rdev->sb_events+1 == mddev->events)) {
|
|
|
|
/* Don't update this superblock */
|
|
|
|
rdev->sb_loaded = 2;
|
|
|
|
} else {
|
|
|
|
super_types[mddev->major_version].
|
|
|
|
sync_super(mddev, rdev);
|
|
|
|
rdev->sb_loaded = 1;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-03 15:15:46 +07:00
|
|
|
static void md_update_sb(mddev_t * mddev, int force_change)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
2005-06-22 07:17:12 +07:00
|
|
|
int sync_req;
|
2006-06-26 14:27:57 +07:00
|
|
|
int nospares = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-04-30 14:52:28 +07:00
|
|
|
if (mddev->external)
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
repeat:
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_lock_irq(&mddev->write_lock);
|
2006-08-27 15:23:49 +07:00
|
|
|
|
2006-10-03 15:15:46 +07:00
|
|
|
set_bit(MD_CHANGE_PENDING, &mddev->flags);
|
|
|
|
if (test_and_clear_bit(MD_CHANGE_DEVS, &mddev->flags))
|
|
|
|
force_change = 1;
|
|
|
|
if (test_and_clear_bit(MD_CHANGE_CLEAN, &mddev->flags))
|
|
|
|
/* just a clean<-> dirty transition, possibly leave spares alone,
|
|
|
|
* though if events isn't the right even/odd, we will have to do
|
|
|
|
* spares after all
|
|
|
|
*/
|
|
|
|
nospares = 1;
|
|
|
|
if (force_change)
|
|
|
|
nospares = 0;
|
|
|
|
if (mddev->degraded)
|
2006-08-27 15:23:49 +07:00
|
|
|
/* If the array is degraded, then skipping spares is both
|
|
|
|
* dangerous and fairly pointless.
|
|
|
|
* Dangerous because a device that was removed from the array
|
|
|
|
* might have a event_count that still looks up-to-date,
|
|
|
|
* so it can be re-added without a resync.
|
|
|
|
* Pointless because if there are any spares to skip,
|
|
|
|
* then a recovery will happen and soon that array won't
|
|
|
|
* be degraded any more and the spare can go back to sleep then.
|
|
|
|
*/
|
2006-10-03 15:15:46 +07:00
|
|
|
nospares = 0;
|
2006-08-27 15:23:49 +07:00
|
|
|
|
2005-06-22 07:17:12 +07:00
|
|
|
sync_req = mddev->in_sync;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->utime = get_seconds();
|
2006-06-26 14:27:57 +07:00
|
|
|
|
|
|
|
/* If this is just a dirty<->clean transition, and the array is clean
|
|
|
|
* and 'events' is odd, we can roll back to the previous clean state */
|
2006-10-03 15:15:46 +07:00
|
|
|
if (nospares
|
2006-06-26 14:27:57 +07:00
|
|
|
&& (mddev->in_sync && mddev->recovery_cp == MaxSector)
|
2007-01-26 15:57:02 +07:00
|
|
|
&& (mddev->events & 1)
|
|
|
|
&& mddev->events != 1)
|
2006-06-26 14:27:57 +07:00
|
|
|
mddev->events--;
|
|
|
|
else {
|
|
|
|
/* otherwise we have to go forward and ... */
|
|
|
|
mddev->events ++;
|
|
|
|
if (!mddev->in_sync || mddev->recovery_cp != MaxSector) { /* not clean */
|
|
|
|
/* .. if the array isn't clean, insist on an odd 'events' */
|
|
|
|
if ((mddev->events&1)==0) {
|
|
|
|
mddev->events++;
|
|
|
|
nospares = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* otherwise insist on an even 'events' (for clean states) */
|
|
|
|
if ((mddev->events&1)) {
|
|
|
|
mddev->events++;
|
|
|
|
nospares = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!mddev->events) {
|
|
|
|
/*
|
|
|
|
* oops, this 64-bit counter should never wrap.
|
|
|
|
* Either we are in around ~1 trillion A.C., assuming
|
|
|
|
* 1 reboot per second, or we have a bug:
|
|
|
|
*/
|
|
|
|
MD_BUG();
|
|
|
|
mddev->events --;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do not write anything to disk if using
|
|
|
|
* nonpersistent superblocks
|
|
|
|
*/
|
2005-06-22 07:17:12 +07:00
|
|
|
if (!mddev->persistent) {
|
2008-02-06 16:39:51 +07:00
|
|
|
if (!mddev->external)
|
|
|
|
clear_bit(MD_CHANGE_PENDING, &mddev->flags);
|
|
|
|
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
2005-06-22 07:17:26 +07:00
|
|
|
wake_up(&mddev->sb_wait);
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2005-06-22 07:17:12 +07:00
|
|
|
}
|
2008-02-06 16:39:51 +07:00
|
|
|
sync_sbs(mddev, nospares);
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
dprintk(KERN_INFO
|
|
|
|
"md: updating %s RAID superblock on device (in sync %d)\n",
|
|
|
|
mdname(mddev),mddev->in_sync);
|
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
bitmap_update_sb(mddev->bitmap);
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2005-04-17 05:20:36 +07:00
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
dprintk(KERN_INFO "md: ");
|
2006-06-26 14:27:57 +07:00
|
|
|
if (rdev->sb_loaded != 1)
|
|
|
|
continue; /* no noise on spare devices */
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
dprintk("(skipping faulty ");
|
|
|
|
|
|
|
|
dprintk("%s ", bdevname(rdev->bdev,b));
|
2005-11-09 12:39:31 +07:00
|
|
|
if (!test_bit(Faulty, &rdev->flags)) {
|
2005-06-22 07:17:28 +07:00
|
|
|
md_super_write(mddev,rdev,
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start, rdev->sb_size,
|
2005-06-22 07:17:28 +07:00
|
|
|
rdev->sb_page);
|
|
|
|
dprintk(KERN_INFO "(write) %s's sb offset: %llu\n",
|
|
|
|
bdevname(rdev->bdev,b),
|
2008-07-11 19:02:23 +07:00
|
|
|
(unsigned long long)rdev->sb_start);
|
2006-06-26 14:27:57 +07:00
|
|
|
rdev->sb_events = mddev->events;
|
2005-06-22 07:17:28 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
} else
|
|
|
|
dprintk(")\n");
|
2005-06-22 07:17:28 +07:00
|
|
|
if (mddev->level == LEVEL_MULTIPATH)
|
2005-04-17 05:20:36 +07:00
|
|
|
/* only need to write one superblock... */
|
|
|
|
break;
|
|
|
|
}
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
md_super_wait(mddev);
|
2006-10-03 15:15:46 +07:00
|
|
|
/* if there was a failure, MD_CHANGE_DEVS was set, and we re-write super */
|
2005-06-22 07:17:28 +07:00
|
|
|
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_lock_irq(&mddev->write_lock);
|
2006-10-03 15:15:46 +07:00
|
|
|
if (mddev->in_sync != sync_req ||
|
|
|
|
test_bit(MD_CHANGE_DEVS, &mddev->flags)) {
|
2005-06-22 07:17:12 +07:00
|
|
|
/* have to write it out again */
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
2005-06-22 07:17:12 +07:00
|
|
|
goto repeat;
|
|
|
|
}
|
2006-10-03 15:15:46 +07:00
|
|
|
clear_bit(MD_CHANGE_PENDING, &mddev->flags);
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
2005-06-22 07:17:26 +07:00
|
|
|
wake_up(&mddev->sb_wait);
|
2005-06-22 07:17:12 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-03-24 00:34:54 +07:00
|
|
|
/* words written to sysfs files may, or may not, be \n terminated.
|
2006-01-06 15:20:41 +07:00
|
|
|
* We want to accept with case. For this we use cmd_match.
|
|
|
|
*/
|
|
|
|
static int cmd_match(const char *cmd, const char *str)
|
|
|
|
{
|
|
|
|
/* See if cmd, written into a sysfs file, matches
|
|
|
|
* str. They must either be the same, or cmd can
|
|
|
|
* have a trailing newline
|
|
|
|
*/
|
|
|
|
while (*cmd && *str && *cmd == *str) {
|
|
|
|
cmd++;
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
if (*cmd == '\n')
|
|
|
|
cmd++;
|
|
|
|
if (*str || *cmd)
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2005-11-09 12:39:24 +07:00
|
|
|
struct rdev_sysfs_entry {
|
|
|
|
struct attribute attr;
|
|
|
|
ssize_t (*show)(mdk_rdev_t *, char *);
|
|
|
|
ssize_t (*store)(mdk_rdev_t *, const char *, size_t);
|
|
|
|
};
|
|
|
|
|
|
|
|
static ssize_t
|
2005-11-09 12:39:39 +07:00
|
|
|
state_show(mdk_rdev_t *rdev, char *page)
|
2005-11-09 12:39:24 +07:00
|
|
|
{
|
|
|
|
char *sep = "";
|
2008-02-06 16:39:57 +07:00
|
|
|
size_t len = 0;
|
2005-11-09 12:39:24 +07:00
|
|
|
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags)) {
|
2005-11-09 12:39:24 +07:00
|
|
|
len+= sprintf(page+len, "%sfaulty",sep);
|
|
|
|
sep = ",";
|
|
|
|
}
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(In_sync, &rdev->flags)) {
|
2005-11-09 12:39:24 +07:00
|
|
|
len += sprintf(page+len, "%sin_sync",sep);
|
|
|
|
sep = ",";
|
|
|
|
}
|
2006-06-26 14:28:01 +07:00
|
|
|
if (test_bit(WriteMostly, &rdev->flags)) {
|
|
|
|
len += sprintf(page+len, "%swrite_mostly",sep);
|
|
|
|
sep = ",";
|
|
|
|
}
|
2008-04-30 14:52:32 +07:00
|
|
|
if (test_bit(Blocked, &rdev->flags)) {
|
|
|
|
len += sprintf(page+len, "%sblocked", sep);
|
|
|
|
sep = ",";
|
|
|
|
}
|
2005-11-09 12:39:31 +07:00
|
|
|
if (!test_bit(Faulty, &rdev->flags) &&
|
|
|
|
!test_bit(In_sync, &rdev->flags)) {
|
2005-11-09 12:39:24 +07:00
|
|
|
len += sprintf(page+len, "%sspare", sep);
|
|
|
|
sep = ",";
|
|
|
|
}
|
|
|
|
return len+sprintf(page+len, "\n");
|
|
|
|
}
|
|
|
|
|
2006-06-26 14:27:58 +07:00
|
|
|
static ssize_t
|
|
|
|
state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
/* can write
|
|
|
|
* faulty - simulates and error
|
|
|
|
* remove - disconnects the device
|
2006-06-26 14:28:01 +07:00
|
|
|
* writemostly - sets write_mostly
|
|
|
|
* -writemostly - clears write_mostly
|
2008-04-30 14:52:32 +07:00
|
|
|
* blocked - sets the Blocked flag
|
|
|
|
* -blocked - clears the Blocked flag
|
2006-06-26 14:27:58 +07:00
|
|
|
*/
|
|
|
|
int err = -EINVAL;
|
|
|
|
if (cmd_match(buf, "faulty") && rdev->mddev->pers) {
|
|
|
|
md_error(rdev->mddev, rdev);
|
|
|
|
err = 0;
|
|
|
|
} else if (cmd_match(buf, "remove")) {
|
|
|
|
if (rdev->raid_disk >= 0)
|
|
|
|
err = -EBUSY;
|
|
|
|
else {
|
|
|
|
mddev_t *mddev = rdev->mddev;
|
|
|
|
kick_rdev_from_array(rdev);
|
2006-12-22 16:11:41 +07:00
|
|
|
if (mddev->pers)
|
|
|
|
md_update_sb(mddev, 1);
|
2006-06-26 14:27:58 +07:00
|
|
|
md_new_event(mddev);
|
|
|
|
err = 0;
|
|
|
|
}
|
2006-06-26 14:28:01 +07:00
|
|
|
} else if (cmd_match(buf, "writemostly")) {
|
|
|
|
set_bit(WriteMostly, &rdev->flags);
|
|
|
|
err = 0;
|
|
|
|
} else if (cmd_match(buf, "-writemostly")) {
|
|
|
|
clear_bit(WriteMostly, &rdev->flags);
|
2008-04-30 14:52:32 +07:00
|
|
|
err = 0;
|
|
|
|
} else if (cmd_match(buf, "blocked")) {
|
|
|
|
set_bit(Blocked, &rdev->flags);
|
|
|
|
err = 0;
|
|
|
|
} else if (cmd_match(buf, "-blocked")) {
|
|
|
|
clear_bit(Blocked, &rdev->flags);
|
|
|
|
wake_up(&rdev->blocked_wait);
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
|
|
|
|
md_wakeup_thread(rdev->mddev->thread);
|
|
|
|
|
2006-06-26 14:28:01 +07:00
|
|
|
err = 0;
|
2006-06-26 14:27:58 +07:00
|
|
|
}
|
2008-10-21 09:25:28 +07:00
|
|
|
if (!err && rdev->sysfs_state)
|
|
|
|
sysfs_notify_dirent(rdev->sysfs_state);
|
2006-06-26 14:27:58 +07:00
|
|
|
return err ? err : len;
|
|
|
|
}
|
2006-07-10 18:44:18 +07:00
|
|
|
static struct rdev_sysfs_entry rdev_state =
|
|
|
|
__ATTR(state, S_IRUGO|S_IWUSR, state_show, state_store);
|
2005-11-09 12:39:24 +07:00
|
|
|
|
2006-01-06 15:20:52 +07:00
|
|
|
static ssize_t
|
|
|
|
errors_show(mdk_rdev_t *rdev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%d\n", atomic_read(&rdev->corrected_errors));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
errors_store(mdk_rdev_t *rdev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
unsigned long n = simple_strtoul(buf, &e, 10);
|
|
|
|
if (*buf && (*e == 0 || *e == '\n')) {
|
|
|
|
atomic_set(&rdev->corrected_errors, n);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
static struct rdev_sysfs_entry rdev_errors =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(errors, S_IRUGO|S_IWUSR, errors_show, errors_store);
|
2006-01-06 15:20:52 +07:00
|
|
|
|
2006-01-06 15:20:55 +07:00
|
|
|
static ssize_t
|
|
|
|
slot_show(mdk_rdev_t *rdev, char *page)
|
|
|
|
{
|
|
|
|
if (rdev->raid_disk < 0)
|
|
|
|
return sprintf(page, "none\n");
|
|
|
|
else
|
|
|
|
return sprintf(page, "%d\n", rdev->raid_disk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
2008-02-06 16:39:51 +07:00
|
|
|
int err;
|
|
|
|
char nm[20];
|
2006-01-06 15:20:55 +07:00
|
|
|
int slot = simple_strtoul(buf, &e, 10);
|
|
|
|
if (strncmp(buf, "none", 4)==0)
|
|
|
|
slot = -1;
|
|
|
|
else if (e==buf || (*e && *e!= '\n'))
|
|
|
|
return -EINVAL;
|
2008-06-28 05:31:31 +07:00
|
|
|
if (rdev->mddev->pers && slot == -1) {
|
2008-02-06 16:39:51 +07:00
|
|
|
/* Setting 'slot' on an active array requires also
|
|
|
|
* updating the 'rd%d' link, and communicating
|
|
|
|
* with the personality with ->hot_*_disk.
|
|
|
|
* For now we only support removing
|
|
|
|
* failed/spare devices. This normally happens automatically,
|
|
|
|
* but not when the metadata is externally managed.
|
|
|
|
*/
|
|
|
|
if (rdev->raid_disk == -1)
|
|
|
|
return -EEXIST;
|
|
|
|
/* personality does all needed checks */
|
|
|
|
if (rdev->mddev->pers->hot_add_disk == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
err = rdev->mddev->pers->
|
|
|
|
hot_remove_disk(rdev->mddev, rdev->raid_disk);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
sprintf(nm, "rd%d", rdev->raid_disk);
|
|
|
|
sysfs_remove_link(&rdev->mddev->kobj, nm);
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
|
|
|
|
md_wakeup_thread(rdev->mddev->thread);
|
2008-06-28 05:31:31 +07:00
|
|
|
} else if (rdev->mddev->pers) {
|
|
|
|
mdk_rdev_t *rdev2;
|
|
|
|
/* Activating a spare .. or possibly reactivating
|
|
|
|
* if we every get bitmaps working here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (rdev->raid_disk != -1)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (rdev->mddev->pers->hot_add_disk == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev2, &rdev->mddev->disks, same_set)
|
2008-06-28 05:31:31 +07:00
|
|
|
if (rdev2->raid_disk == slot)
|
|
|
|
return -EEXIST;
|
|
|
|
|
|
|
|
rdev->raid_disk = slot;
|
|
|
|
if (test_bit(In_sync, &rdev->flags))
|
|
|
|
rdev->saved_raid_disk = slot;
|
|
|
|
else
|
|
|
|
rdev->saved_raid_disk = -1;
|
|
|
|
err = rdev->mddev->pers->
|
|
|
|
hot_add_disk(rdev->mddev, rdev);
|
2008-06-28 05:31:33 +07:00
|
|
|
if (err) {
|
2008-06-28 05:31:31 +07:00
|
|
|
rdev->raid_disk = -1;
|
|
|
|
return err;
|
2008-06-28 05:31:44 +07:00
|
|
|
} else
|
2008-10-21 09:25:28 +07:00
|
|
|
sysfs_notify_dirent(rdev->sysfs_state);
|
2008-06-28 05:31:31 +07:00
|
|
|
sprintf(nm, "rd%d", rdev->raid_disk);
|
|
|
|
if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm))
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: cannot register "
|
|
|
|
"%s for %s\n",
|
|
|
|
nm, mdname(rdev->mddev));
|
|
|
|
|
|
|
|
/* don't wakeup anyone, leave that to userspace. */
|
2008-02-06 16:39:51 +07:00
|
|
|
} else {
|
|
|
|
if (slot >= rdev->mddev->raid_disks)
|
|
|
|
return -ENOSPC;
|
|
|
|
rdev->raid_disk = slot;
|
|
|
|
/* assume it is working */
|
2008-02-06 16:39:54 +07:00
|
|
|
clear_bit(Faulty, &rdev->flags);
|
|
|
|
clear_bit(WriteMostly, &rdev->flags);
|
2008-02-06 16:39:51 +07:00
|
|
|
set_bit(In_sync, &rdev->flags);
|
2008-10-21 09:25:28 +07:00
|
|
|
sysfs_notify_dirent(rdev->sysfs_state);
|
2008-02-06 16:39:51 +07:00
|
|
|
}
|
2006-01-06 15:20:55 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct rdev_sysfs_entry rdev_slot =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(slot, S_IRUGO|S_IWUSR, slot_show, slot_store);
|
2006-01-06 15:20:55 +07:00
|
|
|
|
2006-01-06 15:20:56 +07:00
|
|
|
static ssize_t
|
|
|
|
offset_show(mdk_rdev_t *rdev, char *page)
|
|
|
|
{
|
2006-01-06 15:20:59 +07:00
|
|
|
return sprintf(page, "%llu\n", (unsigned long long)rdev->data_offset);
|
2006-01-06 15:20:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
offset_store(mdk_rdev_t *rdev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
unsigned long long offset = simple_strtoull(buf, &e, 10);
|
|
|
|
if (e==buf || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
2008-06-28 05:31:29 +07:00
|
|
|
if (rdev->mddev->pers && rdev->raid_disk >= 0)
|
2006-01-06 15:20:56 +07:00
|
|
|
return -EBUSY;
|
2008-02-06 16:39:54 +07:00
|
|
|
if (rdev->size && rdev->mddev->external)
|
|
|
|
/* Must set offset before size, so overlap checks
|
|
|
|
* can be sane */
|
|
|
|
return -EBUSY;
|
2006-01-06 15:20:56 +07:00
|
|
|
rdev->data_offset = offset;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rdev_sysfs_entry rdev_offset =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(offset, S_IRUGO|S_IWUSR, offset_show, offset_store);
|
2006-01-06 15:20:56 +07:00
|
|
|
|
2006-01-06 15:21:06 +07:00
|
|
|
static ssize_t
|
|
|
|
rdev_size_show(mdk_rdev_t *rdev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%llu\n", (unsigned long long)rdev->size);
|
|
|
|
}
|
|
|
|
|
2008-02-06 16:39:54 +07:00
|
|
|
static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2)
|
|
|
|
{
|
|
|
|
/* check if two start/length pairs overlap */
|
|
|
|
if (s1+l1 <= s2)
|
|
|
|
return 0;
|
|
|
|
if (s2+l2 <= s1)
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:21:06 +07:00
|
|
|
static ssize_t
|
|
|
|
rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
|
|
|
|
{
|
2008-07-12 07:37:50 +07:00
|
|
|
unsigned long long size;
|
2008-02-06 16:39:54 +07:00
|
|
|
unsigned long long oldsize = rdev->size;
|
2008-03-05 05:29:33 +07:00
|
|
|
mddev_t *my_mddev = rdev->mddev;
|
|
|
|
|
2008-07-12 07:37:50 +07:00
|
|
|
if (strict_strtoull(buf, 10, &size) < 0)
|
|
|
|
return -EINVAL;
|
2008-06-28 05:31:46 +07:00
|
|
|
if (my_mddev->pers && rdev->raid_disk >= 0) {
|
2008-07-12 07:37:50 +07:00
|
|
|
if (my_mddev->persistent) {
|
|
|
|
size = super_types[my_mddev->major_version].
|
2008-07-21 11:42:12 +07:00
|
|
|
rdev_size_change(rdev, size * 2);
|
2008-06-28 05:31:46 +07:00
|
|
|
if (!size)
|
|
|
|
return -EBUSY;
|
|
|
|
} else if (!size) {
|
|
|
|
size = (rdev->bdev->bd_inode->i_size >> 10);
|
|
|
|
size -= rdev->data_offset/2;
|
|
|
|
}
|
|
|
|
}
|
2008-10-13 07:55:11 +07:00
|
|
|
if (size < my_mddev->size)
|
|
|
|
return -EINVAL; /* component must fit device */
|
2008-06-28 05:31:46 +07:00
|
|
|
|
2006-01-06 15:21:06 +07:00
|
|
|
rdev->size = size;
|
2008-07-12 07:37:50 +07:00
|
|
|
if (size > oldsize && my_mddev->external) {
|
2008-02-06 16:39:54 +07:00
|
|
|
/* need to check that all other rdevs with the same ->bdev
|
|
|
|
* do not overlap. We need to unlock the mddev to avoid
|
|
|
|
* a deadlock. We have already changed rdev->size, and if
|
|
|
|
* we have to change it back, we will have the lock again.
|
|
|
|
*/
|
|
|
|
mddev_t *mddev;
|
|
|
|
int overlap = 0;
|
2009-01-09 04:31:08 +07:00
|
|
|
struct list_head *tmp;
|
2008-02-06 16:39:54 +07:00
|
|
|
|
2008-03-05 05:29:33 +07:00
|
|
|
mddev_unlock(my_mddev);
|
2008-02-06 16:39:58 +07:00
|
|
|
for_each_mddev(mddev, tmp) {
|
2008-02-06 16:39:54 +07:00
|
|
|
mdk_rdev_t *rdev2;
|
|
|
|
|
|
|
|
mddev_lock(mddev);
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev2, &mddev->disks, same_set)
|
2008-02-06 16:39:54 +07:00
|
|
|
if (test_bit(AllReserved, &rdev2->flags) ||
|
|
|
|
(rdev->bdev == rdev2->bdev &&
|
|
|
|
rdev != rdev2 &&
|
2008-07-21 11:42:07 +07:00
|
|
|
overlaps(rdev->data_offset, rdev->size * 2,
|
|
|
|
rdev2->data_offset,
|
|
|
|
rdev2->size * 2))) {
|
2008-02-06 16:39:54 +07:00
|
|
|
overlap = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mddev_unlock(mddev);
|
|
|
|
if (overlap) {
|
|
|
|
mddev_put(mddev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-03-05 05:29:33 +07:00
|
|
|
mddev_lock(my_mddev);
|
2008-02-06 16:39:54 +07:00
|
|
|
if (overlap) {
|
|
|
|
/* Someone else could have slipped in a size
|
|
|
|
* change here, but doing so is just silly.
|
|
|
|
* We put oldsize back because we *know* it is
|
|
|
|
* safe, and trust userspace not to race with
|
|
|
|
* itself
|
|
|
|
*/
|
|
|
|
rdev->size = oldsize;
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
2006-01-06 15:21:06 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rdev_sysfs_entry rdev_size =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(size, S_IRUGO|S_IWUSR, rdev_size_show, rdev_size_store);
|
2006-01-06 15:21:06 +07:00
|
|
|
|
2005-11-09 12:39:24 +07:00
|
|
|
static struct attribute *rdev_default_attrs[] = {
|
|
|
|
&rdev_state.attr,
|
2006-01-06 15:20:52 +07:00
|
|
|
&rdev_errors.attr,
|
2006-01-06 15:20:55 +07:00
|
|
|
&rdev_slot.attr,
|
2006-01-06 15:20:56 +07:00
|
|
|
&rdev_offset.attr,
|
2006-01-06 15:21:06 +07:00
|
|
|
&rdev_size.attr,
|
2005-11-09 12:39:24 +07:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
static ssize_t
|
|
|
|
rdev_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
|
|
|
|
{
|
|
|
|
struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
|
|
|
|
mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj);
|
2008-03-05 05:29:33 +07:00
|
|
|
mddev_t *mddev = rdev->mddev;
|
|
|
|
ssize_t rv;
|
2005-11-09 12:39:24 +07:00
|
|
|
|
|
|
|
if (!entry->show)
|
|
|
|
return -EIO;
|
2008-03-05 05:29:33 +07:00
|
|
|
|
|
|
|
rv = mddev ? mddev_lock(mddev) : -EBUSY;
|
|
|
|
if (!rv) {
|
|
|
|
if (rdev->mddev == NULL)
|
|
|
|
rv = -EBUSY;
|
|
|
|
else
|
|
|
|
rv = entry->show(rdev, page);
|
|
|
|
mddev_unlock(mddev);
|
|
|
|
}
|
|
|
|
return rv;
|
2005-11-09 12:39:24 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
rdev_attr_store(struct kobject *kobj, struct attribute *attr,
|
|
|
|
const char *page, size_t length)
|
|
|
|
{
|
|
|
|
struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
|
|
|
|
mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj);
|
2008-03-05 05:29:33 +07:00
|
|
|
ssize_t rv;
|
|
|
|
mddev_t *mddev = rdev->mddev;
|
2005-11-09 12:39:24 +07:00
|
|
|
|
|
|
|
if (!entry->store)
|
|
|
|
return -EIO;
|
2006-07-10 18:44:19 +07:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2008-03-05 05:29:33 +07:00
|
|
|
rv = mddev ? mddev_lock(mddev): -EBUSY;
|
2008-02-06 16:39:55 +07:00
|
|
|
if (!rv) {
|
2008-03-05 05:29:33 +07:00
|
|
|
if (rdev->mddev == NULL)
|
|
|
|
rv = -EBUSY;
|
|
|
|
else
|
|
|
|
rv = entry->store(rdev, page, length);
|
2008-04-30 14:52:28 +07:00
|
|
|
mddev_unlock(mddev);
|
2008-02-06 16:39:55 +07:00
|
|
|
}
|
|
|
|
return rv;
|
2005-11-09 12:39:24 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rdev_free(struct kobject *ko)
|
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev = container_of(ko, mdk_rdev_t, kobj);
|
|
|
|
kfree(rdev);
|
|
|
|
}
|
|
|
|
static struct sysfs_ops rdev_sysfs_ops = {
|
|
|
|
.show = rdev_attr_show,
|
|
|
|
.store = rdev_attr_store,
|
|
|
|
};
|
|
|
|
static struct kobj_type rdev_ktype = {
|
|
|
|
.release = rdev_free,
|
|
|
|
.sysfs_ops = &rdev_sysfs_ops,
|
|
|
|
.default_attrs = rdev_default_attrs,
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Import a device. If 'super_format' >= 0, then sanity check the superblock
|
|
|
|
*
|
|
|
|
* mark the device faulty if:
|
|
|
|
*
|
|
|
|
* - the device is nonexistent (zero size)
|
|
|
|
* - the device has no valid superblock
|
|
|
|
*
|
|
|
|
* a faulty rdev _never_ has rdev->sb set.
|
|
|
|
*/
|
|
|
|
static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor)
|
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
int err;
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
sector_t size;
|
|
|
|
|
2006-01-06 15:20:32 +07:00
|
|
|
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!rdev) {
|
|
|
|
printk(KERN_ERR "md: could not alloc mem for new device!\n");
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = alloc_disk_sb(rdev)))
|
|
|
|
goto abort_free;
|
|
|
|
|
2008-02-06 16:39:54 +07:00
|
|
|
err = lock_rdev(rdev, newdev, super_format == -2);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err)
|
|
|
|
goto abort_free;
|
|
|
|
|
2007-12-18 13:05:35 +07:00
|
|
|
kobject_init(&rdev->kobj, &rdev_ktype);
|
2005-11-09 12:39:24 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->desc_nr = -1;
|
2006-10-29 00:38:30 +07:00
|
|
|
rdev->saved_raid_disk = -1;
|
2006-12-22 16:11:41 +07:00
|
|
|
rdev->raid_disk = -1;
|
2005-11-09 12:39:31 +07:00
|
|
|
rdev->flags = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->data_offset = 0;
|
2006-06-26 14:27:57 +07:00
|
|
|
rdev->sb_events = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
atomic_set(&rdev->nr_pending, 0);
|
2005-11-09 12:39:31 +07:00
|
|
|
atomic_set(&rdev->read_errors, 0);
|
2006-01-06 15:20:52 +07:00
|
|
|
atomic_set(&rdev->corrected_errors, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
|
|
|
|
if (!size) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: %s has zero or unknown size, marking faulty!\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
err = -EINVAL;
|
|
|
|
goto abort_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (super_format >= 0) {
|
|
|
|
err = super_types[super_format].
|
|
|
|
load_super(rdev, NULL, super_minor);
|
|
|
|
if (err == -EINVAL) {
|
2007-07-17 18:06:11 +07:00
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: %s does not have a valid v%d.%d "
|
|
|
|
"superblock, not importing!\n",
|
|
|
|
bdevname(rdev->bdev,b),
|
|
|
|
super_format, super_minor);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto abort_free;
|
|
|
|
}
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: could not read %s's sb, not importing!\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
goto abort_free;
|
|
|
|
}
|
|
|
|
}
|
2008-04-30 14:52:32 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
INIT_LIST_HEAD(&rdev->same_set);
|
2008-04-30 14:52:32 +07:00
|
|
|
init_waitqueue_head(&rdev->blocked_wait);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return rdev;
|
|
|
|
|
|
|
|
abort_free:
|
|
|
|
if (rdev->sb_page) {
|
|
|
|
if (rdev->bdev)
|
|
|
|
unlock_rdev(rdev);
|
|
|
|
free_disk_sb(rdev);
|
|
|
|
}
|
|
|
|
kfree(rdev);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check a full RAID array for plausibility
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2005-04-17 05:26:42 +07:00
|
|
|
static void analyze_sbs(mddev_t * mddev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
2009-01-09 04:31:08 +07:00
|
|
|
mdk_rdev_t *rdev, *freshest, *tmp;
|
2005-04-17 05:20:36 +07:00
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
|
|
|
|
freshest = NULL;
|
2008-02-06 16:39:59 +07:00
|
|
|
rdev_for_each(rdev, tmp, mddev)
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (super_types[mddev->major_version].
|
|
|
|
load_super(rdev, freshest, mddev->minor_version)) {
|
|
|
|
case 1:
|
|
|
|
freshest = rdev;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk( KERN_ERR \
|
|
|
|
"md: fatal superblock inconsistency in %s"
|
|
|
|
" -- removing from array\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
kick_rdev_from_array(rdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
super_types[mddev->major_version].
|
|
|
|
validate_super(mddev, freshest);
|
|
|
|
|
|
|
|
i = 0;
|
2008-02-06 16:39:59 +07:00
|
|
|
rdev_for_each(rdev, tmp, mddev) {
|
2009-02-06 14:02:46 +07:00
|
|
|
if (rdev->desc_nr >= mddev->max_disks ||
|
|
|
|
i > mddev->max_disks) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: %s: %s: only %d devices permitted\n",
|
|
|
|
mdname(mddev), bdevname(rdev->bdev, b),
|
|
|
|
mddev->max_disks);
|
|
|
|
kick_rdev_from_array(rdev);
|
|
|
|
continue;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rdev != freshest)
|
|
|
|
if (super_types[mddev->major_version].
|
|
|
|
validate_super(mddev, rdev)) {
|
|
|
|
printk(KERN_WARNING "md: kicking non-fresh %s"
|
|
|
|
" from array!\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
kick_rdev_from_array(rdev);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (mddev->level == LEVEL_MULTIPATH) {
|
|
|
|
rdev->desc_nr = i++;
|
|
|
|
rdev->raid_disk = rdev->desc_nr;
|
2005-11-09 12:39:31 +07:00
|
|
|
set_bit(In_sync, &rdev->flags);
|
2007-05-24 03:58:10 +07:00
|
|
|
} else if (rdev->raid_disk >= mddev->raid_disks) {
|
|
|
|
rdev->raid_disk = -1;
|
|
|
|
clear_bit(In_sync, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (mddev->recovery_cp != MaxSector &&
|
|
|
|
mddev->level >= 1)
|
|
|
|
printk(KERN_ERR "md: %s: raid array is not clean"
|
|
|
|
" -- starting background reconstruction\n",
|
|
|
|
mdname(mddev));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2008-08-05 12:54:13 +07:00
|
|
|
static void md_safemode_timeout(unsigned long data);
|
|
|
|
|
2006-06-26 14:27:37 +07:00
|
|
|
static ssize_t
|
|
|
|
safe_delay_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
int msec = (mddev->safemode_delay*1000)/HZ;
|
|
|
|
return sprintf(page, "%d.%03d\n", msec/1000, msec%1000);
|
|
|
|
}
|
|
|
|
static ssize_t
|
|
|
|
safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len)
|
|
|
|
{
|
|
|
|
int scale=1;
|
|
|
|
int dot=0;
|
|
|
|
int i;
|
|
|
|
unsigned long msec;
|
|
|
|
char buf[30];
|
2008-09-25 12:48:19 +07:00
|
|
|
|
2006-06-26 14:27:37 +07:00
|
|
|
/* remove a period, and count digits after it */
|
|
|
|
if (len >= sizeof(buf))
|
|
|
|
return -EINVAL;
|
2008-09-25 12:48:19 +07:00
|
|
|
strlcpy(buf, cbuf, sizeof(buf));
|
2006-06-26 14:27:37 +07:00
|
|
|
for (i=0; i<len; i++) {
|
|
|
|
if (dot) {
|
|
|
|
if (isdigit(buf[i])) {
|
|
|
|
buf[i-1] = buf[i];
|
|
|
|
scale *= 10;
|
|
|
|
}
|
|
|
|
buf[i] = 0;
|
|
|
|
} else if (buf[i] == '.') {
|
|
|
|
dot=1;
|
|
|
|
buf[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
2008-09-25 12:48:19 +07:00
|
|
|
if (strict_strtoul(buf, 10, &msec) < 0)
|
2006-06-26 14:27:37 +07:00
|
|
|
return -EINVAL;
|
|
|
|
msec = (msec * 1000) / scale;
|
|
|
|
if (msec == 0)
|
|
|
|
mddev->safemode_delay = 0;
|
|
|
|
else {
|
2008-08-05 12:54:13 +07:00
|
|
|
unsigned long old_delay = mddev->safemode_delay;
|
2006-06-26 14:27:37 +07:00
|
|
|
mddev->safemode_delay = (msec*HZ)/1000;
|
|
|
|
if (mddev->safemode_delay == 0)
|
|
|
|
mddev->safemode_delay = 1;
|
2008-08-05 12:54:13 +07:00
|
|
|
if (mddev->safemode_delay < old_delay)
|
|
|
|
md_safemode_timeout((unsigned long)mddev);
|
2006-06-26 14:27:37 +07:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_safe_delay =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(safe_mode_delay, S_IRUGO|S_IWUSR,safe_delay_show, safe_delay_store);
|
2006-06-26 14:27:37 +07:00
|
|
|
|
2005-11-09 12:39:23 +07:00
|
|
|
static ssize_t
|
2005-11-09 12:39:39 +07:00
|
|
|
level_show(mddev_t *mddev, char *page)
|
2005-11-09 12:39:23 +07:00
|
|
|
{
|
2006-01-06 15:20:36 +07:00
|
|
|
struct mdk_personality *p = mddev->pers;
|
2006-01-06 15:20:51 +07:00
|
|
|
if (p)
|
2005-11-09 12:39:23 +07:00
|
|
|
return sprintf(page, "%s\n", p->name);
|
2006-01-06 15:20:51 +07:00
|
|
|
else if (mddev->clevel[0])
|
|
|
|
return sprintf(page, "%s\n", mddev->clevel);
|
|
|
|
else if (mddev->level != LEVEL_NONE)
|
|
|
|
return sprintf(page, "%d\n", mddev->level);
|
|
|
|
else
|
|
|
|
return 0;
|
2005-11-09 12:39:23 +07:00
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:51 +07:00
|
|
|
static ssize_t
|
|
|
|
level_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
2008-02-06 16:39:57 +07:00
|
|
|
ssize_t rv = len;
|
2006-01-06 15:20:51 +07:00
|
|
|
if (mddev->pers)
|
|
|
|
return -EBUSY;
|
|
|
|
if (len == 0)
|
|
|
|
return 0;
|
|
|
|
if (len >= sizeof(mddev->clevel))
|
|
|
|
return -ENOSPC;
|
|
|
|
strncpy(mddev->clevel, buf, len);
|
|
|
|
if (mddev->clevel[len-1] == '\n')
|
|
|
|
len--;
|
|
|
|
mddev->clevel[len] = 0;
|
|
|
|
mddev->level = LEVEL_NONE;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_level =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(level, S_IRUGO|S_IWUSR, level_show, level_store);
|
2005-11-09 12:39:23 +07:00
|
|
|
|
2006-06-26 14:27:59 +07:00
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
layout_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
/* just a number, not meaningful for all levels */
|
2007-05-09 16:35:38 +07:00
|
|
|
if (mddev->reshape_position != MaxSector &&
|
|
|
|
mddev->layout != mddev->new_layout)
|
|
|
|
return sprintf(page, "%d (%d)\n",
|
|
|
|
mddev->new_layout, mddev->layout);
|
2006-06-26 14:27:59 +07:00
|
|
|
return sprintf(page, "%d\n", mddev->layout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
layout_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
unsigned long n = simple_strtoul(buf, &e, 10);
|
|
|
|
|
|
|
|
if (!*buf || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-05-09 16:35:38 +07:00
|
|
|
if (mddev->pers)
|
|
|
|
return -EBUSY;
|
|
|
|
if (mddev->reshape_position != MaxSector)
|
|
|
|
mddev->new_layout = n;
|
|
|
|
else
|
|
|
|
mddev->layout = n;
|
2006-06-26 14:27:59 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_layout =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store);
|
2006-06-26 14:27:59 +07:00
|
|
|
|
|
|
|
|
2005-11-09 12:39:23 +07:00
|
|
|
static ssize_t
|
2005-11-09 12:39:39 +07:00
|
|
|
raid_disks_show(mddev_t *mddev, char *page)
|
2005-11-09 12:39:23 +07:00
|
|
|
{
|
2005-11-09 12:39:45 +07:00
|
|
|
if (mddev->raid_disks == 0)
|
|
|
|
return 0;
|
2007-05-09 16:35:38 +07:00
|
|
|
if (mddev->reshape_position != MaxSector &&
|
|
|
|
mddev->delta_disks != 0)
|
|
|
|
return sprintf(page, "%d (%d)\n", mddev->raid_disks,
|
|
|
|
mddev->raid_disks - mddev->delta_disks);
|
2005-11-09 12:39:23 +07:00
|
|
|
return sprintf(page, "%d\n", mddev->raid_disks);
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:54 +07:00
|
|
|
static int update_raid_disks(mddev_t *mddev, int raid_disks);
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
raid_disks_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
int rv = 0;
|
|
|
|
unsigned long n = simple_strtoul(buf, &e, 10);
|
|
|
|
|
|
|
|
if (!*buf || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (mddev->pers)
|
|
|
|
rv = update_raid_disks(mddev, n);
|
2007-05-09 16:35:38 +07:00
|
|
|
else if (mddev->reshape_position != MaxSector) {
|
|
|
|
int olddisks = mddev->raid_disks - mddev->delta_disks;
|
|
|
|
mddev->delta_disks = n - olddisks;
|
|
|
|
mddev->raid_disks = n;
|
|
|
|
} else
|
2006-01-06 15:20:54 +07:00
|
|
|
mddev->raid_disks = n;
|
|
|
|
return rv ? rv : len;
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_raid_disks =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store);
|
2005-11-09 12:39:23 +07:00
|
|
|
|
2006-01-06 15:20:47 +07:00
|
|
|
static ssize_t
|
|
|
|
chunk_size_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
2007-05-09 16:35:38 +07:00
|
|
|
if (mddev->reshape_position != MaxSector &&
|
|
|
|
mddev->chunk_size != mddev->new_chunk)
|
|
|
|
return sprintf(page, "%d (%d)\n", mddev->new_chunk,
|
|
|
|
mddev->chunk_size);
|
2006-01-06 15:20:47 +07:00
|
|
|
return sprintf(page, "%d\n", mddev->chunk_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
chunk_size_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
/* can only set chunk_size if array is not yet active */
|
|
|
|
char *e;
|
|
|
|
unsigned long n = simple_strtoul(buf, &e, 10);
|
|
|
|
|
|
|
|
if (!*buf || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-05-09 16:35:38 +07:00
|
|
|
if (mddev->pers)
|
|
|
|
return -EBUSY;
|
|
|
|
else if (mddev->reshape_position != MaxSector)
|
|
|
|
mddev->new_chunk = n;
|
|
|
|
else
|
|
|
|
mddev->chunk_size = n;
|
2006-01-06 15:20:47 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_chunk_size =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store);
|
2006-01-06 15:20:47 +07:00
|
|
|
|
2006-06-26 14:28:00 +07:00
|
|
|
static ssize_t
|
|
|
|
resync_start_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%llu\n", (unsigned long long)mddev->recovery_cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
resync_start_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
unsigned long long n = simple_strtoull(buf, &e, 10);
|
|
|
|
|
|
|
|
if (mddev->pers)
|
|
|
|
return -EBUSY;
|
|
|
|
if (!*buf || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mddev->recovery_cp = n;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_resync_start =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store);
|
2006-06-26 14:28:00 +07:00
|
|
|
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
/*
|
|
|
|
* The array state can be:
|
|
|
|
*
|
|
|
|
* clear
|
|
|
|
* No devices, no size, no level
|
|
|
|
* Equivalent to STOP_ARRAY ioctl
|
|
|
|
* inactive
|
|
|
|
* May have some settings, but array is not active
|
|
|
|
* all IO results in error
|
|
|
|
* When written, doesn't tear down array, but just stops it
|
|
|
|
* suspended (not supported yet)
|
|
|
|
* All IO requests will block. The array can be reconfigured.
|
2008-03-26 03:00:53 +07:00
|
|
|
* Writing this, if accepted, will block until array is quiescent
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
* readonly
|
|
|
|
* no resync can happen. no superblocks get written.
|
|
|
|
* write requests fail
|
|
|
|
* read-auto
|
|
|
|
* like readonly, but behaves like 'clean' on a write request.
|
|
|
|
*
|
|
|
|
* clean - no pending writes, but otherwise active.
|
|
|
|
* When written to inactive array, starts without resync
|
|
|
|
* If a write request arrives then
|
|
|
|
* if metadata is known, mark 'dirty' and switch to 'active'.
|
|
|
|
* if not known, block and switch to write-pending
|
|
|
|
* If written to an active array that has pending writes, then fails.
|
|
|
|
* active
|
|
|
|
* fully active: IO and resync can be happening.
|
|
|
|
* When written to inactive array, starts with resync
|
|
|
|
*
|
|
|
|
* write-pending
|
|
|
|
* clean, but writes are blocked waiting for 'active' to be written.
|
|
|
|
*
|
|
|
|
* active-idle
|
|
|
|
* like active, but no writes have been seen for a while (100msec).
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
enum array_state { clear, inactive, suspended, readonly, read_auto, clean, active,
|
|
|
|
write_pending, active_idle, bad_word};
|
2006-06-26 14:28:01 +07:00
|
|
|
static char *array_states[] = {
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
"clear", "inactive", "suspended", "readonly", "read-auto", "clean", "active",
|
|
|
|
"write-pending", "active-idle", NULL };
|
|
|
|
|
|
|
|
static int match_word(const char *word, char **list)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
for (n=0; list[n]; n++)
|
|
|
|
if (cmd_match(word, list[n]))
|
|
|
|
break;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
array_state_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
enum array_state st = inactive;
|
|
|
|
|
|
|
|
if (mddev->pers)
|
|
|
|
switch(mddev->ro) {
|
|
|
|
case 1:
|
|
|
|
st = readonly;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
st = read_auto;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
if (mddev->in_sync)
|
|
|
|
st = clean;
|
2008-02-06 16:39:51 +07:00
|
|
|
else if (test_bit(MD_CHANGE_CLEAN, &mddev->flags))
|
|
|
|
st = write_pending;
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
else if (mddev->safemode)
|
|
|
|
st = active_idle;
|
|
|
|
else
|
|
|
|
st = active;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (list_empty(&mddev->disks) &&
|
|
|
|
mddev->raid_disks == 0 &&
|
|
|
|
mddev->size == 0)
|
|
|
|
st = clear;
|
|
|
|
else
|
|
|
|
st = inactive;
|
|
|
|
}
|
|
|
|
return sprintf(page, "%s\n", array_states[st]);
|
|
|
|
}
|
|
|
|
|
2008-07-11 19:02:22 +07:00
|
|
|
static int do_md_stop(mddev_t * mddev, int ro, int is_open);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
static int do_md_run(mddev_t * mddev);
|
|
|
|
static int restart_array(mddev_t *mddev);
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
array_state_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
int err = -EINVAL;
|
|
|
|
enum array_state st = match_word(buf, array_states);
|
|
|
|
switch(st) {
|
|
|
|
case bad_word:
|
|
|
|
break;
|
|
|
|
case clear:
|
|
|
|
/* stopping an active array */
|
2008-07-21 14:05:25 +07:00
|
|
|
if (atomic_read(&mddev->openers) > 0)
|
2008-02-06 16:39:51 +07:00
|
|
|
return -EBUSY;
|
2008-07-11 19:02:22 +07:00
|
|
|
err = do_md_stop(mddev, 0, 0);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
break;
|
|
|
|
case inactive:
|
|
|
|
/* stopping an active array */
|
|
|
|
if (mddev->pers) {
|
2008-07-21 14:05:25 +07:00
|
|
|
if (atomic_read(&mddev->openers) > 0)
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
return -EBUSY;
|
2008-07-11 19:02:22 +07:00
|
|
|
err = do_md_stop(mddev, 2, 0);
|
2008-02-06 16:39:51 +07:00
|
|
|
} else
|
|
|
|
err = 0; /* already inactive */
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
break;
|
|
|
|
case suspended:
|
|
|
|
break; /* not supported yet */
|
|
|
|
case readonly:
|
|
|
|
if (mddev->pers)
|
2008-07-11 19:02:22 +07:00
|
|
|
err = do_md_stop(mddev, 1, 0);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
else {
|
|
|
|
mddev->ro = 1;
|
2008-04-30 14:52:30 +07:00
|
|
|
set_disk_ro(mddev->gendisk, 1);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
err = do_md_run(mddev);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case read_auto:
|
|
|
|
if (mddev->pers) {
|
2008-10-13 07:55:12 +07:00
|
|
|
if (mddev->ro == 0)
|
2008-07-11 19:02:22 +07:00
|
|
|
err = do_md_stop(mddev, 1, 0);
|
2008-10-13 07:55:12 +07:00
|
|
|
else if (mddev->ro == 1)
|
2008-04-30 14:52:30 +07:00
|
|
|
err = restart_array(mddev);
|
|
|
|
if (err == 0) {
|
|
|
|
mddev->ro = 2;
|
|
|
|
set_disk_ro(mddev->gendisk, 0);
|
|
|
|
}
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
} else {
|
|
|
|
mddev->ro = 2;
|
|
|
|
err = do_md_run(mddev);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case clean:
|
|
|
|
if (mddev->pers) {
|
|
|
|
restart_array(mddev);
|
|
|
|
spin_lock_irq(&mddev->write_lock);
|
|
|
|
if (atomic_read(&mddev->writes_pending) == 0) {
|
2008-02-06 16:39:51 +07:00
|
|
|
if (mddev->in_sync == 0) {
|
|
|
|
mddev->in_sync = 1;
|
2008-04-30 14:52:30 +07:00
|
|
|
if (mddev->safemode == 1)
|
|
|
|
mddev->safemode = 0;
|
2008-02-06 16:39:51 +07:00
|
|
|
if (mddev->persistent)
|
|
|
|
set_bit(MD_CHANGE_CLEAN,
|
|
|
|
&mddev->flags);
|
|
|
|
}
|
|
|
|
err = 0;
|
|
|
|
} else
|
|
|
|
err = -EBUSY;
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
|
|
|
} else {
|
|
|
|
mddev->ro = 0;
|
|
|
|
mddev->recovery_cp = MaxSector;
|
|
|
|
err = do_md_run(mddev);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case active:
|
|
|
|
if (mddev->pers) {
|
|
|
|
restart_array(mddev);
|
2008-02-06 16:39:51 +07:00
|
|
|
if (mddev->external)
|
|
|
|
clear_bit(MD_CHANGE_CLEAN, &mddev->flags);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
wake_up(&mddev->sb_wait);
|
|
|
|
err = 0;
|
|
|
|
} else {
|
|
|
|
mddev->ro = 0;
|
2008-04-30 14:52:30 +07:00
|
|
|
set_disk_ro(mddev->gendisk, 0);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
err = do_md_run(mddev);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case write_pending:
|
|
|
|
case active_idle:
|
|
|
|
/* these cannot be set */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (err)
|
|
|
|
return err;
|
2008-06-28 05:31:36 +07:00
|
|
|
else {
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
return len;
|
2008-06-28 05:31:36 +07:00
|
|
|
}
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
}
|
2006-07-10 18:44:18 +07:00
|
|
|
static struct md_sysfs_entry md_array_state =
|
|
|
|
__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
|
2006-01-06 15:21:16 +07:00
|
|
|
static ssize_t
|
|
|
|
null_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
new_dev_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
/* buf must be %d:%d\n? giving major and minor numbers */
|
|
|
|
/* The new device is added to the array.
|
|
|
|
* If the array has a persistent superblock, we read the
|
|
|
|
* superblock to initialise info and check validity.
|
|
|
|
* Otherwise, only checking done is that in bind_rdev_to_array,
|
|
|
|
* which mainly checks size.
|
|
|
|
*/
|
|
|
|
char *e;
|
|
|
|
int major = simple_strtoul(buf, &e, 10);
|
|
|
|
int minor;
|
|
|
|
dev_t dev;
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!*buf || *e != ':' || !e[1] || e[1] == '\n')
|
|
|
|
return -EINVAL;
|
|
|
|
minor = simple_strtoul(e+1, &e, 10);
|
|
|
|
if (*e && *e != '\n')
|
|
|
|
return -EINVAL;
|
|
|
|
dev = MKDEV(major, minor);
|
|
|
|
if (major != MAJOR(dev) ||
|
|
|
|
minor != MINOR(dev))
|
|
|
|
return -EOVERFLOW;
|
|
|
|
|
|
|
|
|
|
|
|
if (mddev->persistent) {
|
|
|
|
rdev = md_import_device(dev, mddev->major_version,
|
|
|
|
mddev->minor_version);
|
|
|
|
if (!IS_ERR(rdev) && !list_empty(&mddev->disks)) {
|
|
|
|
mdk_rdev_t *rdev0 = list_entry(mddev->disks.next,
|
|
|
|
mdk_rdev_t, same_set);
|
|
|
|
err = super_types[mddev->major_version]
|
|
|
|
.load_super(rdev, rdev0, mddev->minor_version);
|
|
|
|
if (err < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
2008-02-06 16:39:54 +07:00
|
|
|
} else if (mddev->external)
|
|
|
|
rdev = md_import_device(dev, -2, -1);
|
|
|
|
else
|
2006-01-06 15:21:16 +07:00
|
|
|
rdev = md_import_device(dev, -1, -1);
|
|
|
|
|
|
|
|
if (IS_ERR(rdev))
|
|
|
|
return PTR_ERR(rdev);
|
|
|
|
err = bind_rdev_to_array(rdev, mddev);
|
|
|
|
out:
|
|
|
|
if (err)
|
|
|
|
export_rdev(rdev);
|
|
|
|
return err ? err : len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_new_device =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(new_dev, S_IWUSR, null_show, new_dev_store);
|
2006-01-06 15:20:47 +07:00
|
|
|
|
2006-10-03 15:15:49 +07:00
|
|
|
static ssize_t
|
|
|
|
bitmap_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
unsigned long chunk, end_chunk;
|
|
|
|
|
|
|
|
if (!mddev->bitmap)
|
|
|
|
goto out;
|
|
|
|
/* buf should be <chunk> <chunk> ... or <chunk>-<chunk> ... (range) */
|
|
|
|
while (*buf) {
|
|
|
|
chunk = end_chunk = simple_strtoul(buf, &end, 0);
|
|
|
|
if (buf == end) break;
|
|
|
|
if (*end == '-') { /* range */
|
|
|
|
buf = end + 1;
|
|
|
|
end_chunk = simple_strtoul(buf, &end, 0);
|
|
|
|
if (buf == end) break;
|
|
|
|
}
|
|
|
|
if (*end && !isspace(*end)) break;
|
|
|
|
bitmap_dirty_bits(mddev->bitmap, chunk, end_chunk);
|
|
|
|
buf = end;
|
|
|
|
while (isspace(*buf)) buf++;
|
|
|
|
}
|
|
|
|
bitmap_unplug(mddev->bitmap); /* flush the bits to disk */
|
|
|
|
out:
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_bitmap =
|
|
|
|
__ATTR(bitmap_set_bits, S_IWUSR, null_show, bitmap_store);
|
|
|
|
|
2006-01-06 15:20:49 +07:00
|
|
|
static ssize_t
|
|
|
|
size_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%llu\n", (unsigned long long)mddev->size);
|
|
|
|
}
|
|
|
|
|
2008-07-11 19:02:22 +07:00
|
|
|
static int update_size(mddev_t *mddev, sector_t num_sectors);
|
2006-01-06 15:20:49 +07:00
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
size_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
/* If array is inactive, we can reduce the component size, but
|
|
|
|
* not increase it (except from 0).
|
|
|
|
* If array is active, we can try an on-line resize
|
|
|
|
*/
|
|
|
|
char *e;
|
|
|
|
int err = 0;
|
|
|
|
unsigned long long size = simple_strtoull(buf, &e, 10);
|
|
|
|
if (!*buf || *buf == '\n' ||
|
|
|
|
(*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (mddev->pers) {
|
2008-07-11 19:02:22 +07:00
|
|
|
err = update_size(mddev, size * 2);
|
2006-10-03 15:15:46 +07:00
|
|
|
md_update_sb(mddev, 1);
|
2006-01-06 15:20:49 +07:00
|
|
|
} else {
|
|
|
|
if (mddev->size == 0 ||
|
|
|
|
mddev->size > size)
|
|
|
|
mddev->size = size;
|
|
|
|
else
|
|
|
|
err = -ENOSPC;
|
|
|
|
}
|
|
|
|
return err ? err : len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_size =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(component_size, S_IRUGO|S_IWUSR, size_show, size_store);
|
2006-01-06 15:20:49 +07:00
|
|
|
|
2006-01-06 15:20:50 +07:00
|
|
|
|
|
|
|
/* Metdata version.
|
2008-02-06 16:39:51 +07:00
|
|
|
* This is one of
|
|
|
|
* 'none' for arrays with no metadata (good luck...)
|
|
|
|
* 'external' for arrays with externally managed metadata,
|
2006-01-06 15:20:50 +07:00
|
|
|
* or N.M for internally known formats
|
|
|
|
*/
|
|
|
|
static ssize_t
|
|
|
|
metadata_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
if (mddev->persistent)
|
|
|
|
return sprintf(page, "%d.%d\n",
|
|
|
|
mddev->major_version, mddev->minor_version);
|
2008-02-06 16:39:51 +07:00
|
|
|
else if (mddev->external)
|
|
|
|
return sprintf(page, "external:%s\n", mddev->metadata_type);
|
2006-01-06 15:20:50 +07:00
|
|
|
else
|
|
|
|
return sprintf(page, "none\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
metadata_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
int major, minor;
|
|
|
|
char *e;
|
2008-10-13 07:55:11 +07:00
|
|
|
/* Changing the details of 'external' metadata is
|
|
|
|
* always permitted. Otherwise there must be
|
|
|
|
* no devices attached to the array.
|
|
|
|
*/
|
|
|
|
if (mddev->external && strncmp(buf, "external:", 9) == 0)
|
|
|
|
;
|
|
|
|
else if (!list_empty(&mddev->disks))
|
2006-01-06 15:20:50 +07:00
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (cmd_match(buf, "none")) {
|
|
|
|
mddev->persistent = 0;
|
2008-02-06 16:39:51 +07:00
|
|
|
mddev->external = 0;
|
|
|
|
mddev->major_version = 0;
|
|
|
|
mddev->minor_version = 90;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
if (strncmp(buf, "external:", 9) == 0) {
|
2008-02-06 16:39:57 +07:00
|
|
|
size_t namelen = len-9;
|
2008-02-06 16:39:51 +07:00
|
|
|
if (namelen >= sizeof(mddev->metadata_type))
|
|
|
|
namelen = sizeof(mddev->metadata_type)-1;
|
|
|
|
strncpy(mddev->metadata_type, buf+9, namelen);
|
|
|
|
mddev->metadata_type[namelen] = 0;
|
|
|
|
if (namelen && mddev->metadata_type[namelen-1] == '\n')
|
|
|
|
mddev->metadata_type[--namelen] = 0;
|
|
|
|
mddev->persistent = 0;
|
|
|
|
mddev->external = 1;
|
2006-01-06 15:20:50 +07:00
|
|
|
mddev->major_version = 0;
|
|
|
|
mddev->minor_version = 90;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
major = simple_strtoul(buf, &e, 10);
|
|
|
|
if (e==buf || *e != '.')
|
|
|
|
return -EINVAL;
|
|
|
|
buf = e+1;
|
|
|
|
minor = simple_strtoul(buf, &e, 10);
|
2006-12-22 16:11:41 +07:00
|
|
|
if (e==buf || (*e && *e != '\n') )
|
2006-01-06 15:20:50 +07:00
|
|
|
return -EINVAL;
|
2007-05-09 16:35:34 +07:00
|
|
|
if (major >= ARRAY_SIZE(super_types) || super_types[major].name == NULL)
|
2006-01-06 15:20:50 +07:00
|
|
|
return -ENOENT;
|
|
|
|
mddev->major_version = major;
|
|
|
|
mddev->minor_version = minor;
|
|
|
|
mddev->persistent = 1;
|
2008-02-06 16:39:51 +07:00
|
|
|
mddev->external = 0;
|
2006-01-06 15:20:50 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_metadata =
|
2006-07-10 18:44:18 +07:00
|
|
|
__ATTR(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
|
2006-01-06 15:20:50 +07:00
|
|
|
|
2005-11-09 12:39:26 +07:00
|
|
|
static ssize_t
|
2005-11-09 12:39:44 +07:00
|
|
|
action_show(mddev_t *mddev, char *page)
|
2005-11-09 12:39:26 +07:00
|
|
|
{
|
2005-11-09 12:39:44 +07:00
|
|
|
char *type = "idle";
|
2005-11-09 12:39:29 +07:00
|
|
|
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
|
2007-10-17 13:30:53 +07:00
|
|
|
(!mddev->ro && test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))) {
|
2006-03-27 16:18:09 +07:00
|
|
|
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
|
|
|
|
type = "reshape";
|
|
|
|
else if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
|
2005-11-09 12:39:26 +07:00
|
|
|
if (!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
|
|
|
|
type = "resync";
|
|
|
|
else if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
|
|
|
|
type = "check";
|
|
|
|
else
|
|
|
|
type = "repair";
|
2008-06-28 05:31:41 +07:00
|
|
|
} else if (test_bit(MD_RECOVERY_RECOVER, &mddev->recovery))
|
2005-11-09 12:39:26 +07:00
|
|
|
type = "recover";
|
|
|
|
}
|
|
|
|
return sprintf(page, "%s\n", type);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
2005-11-09 12:39:44 +07:00
|
|
|
action_store(mddev_t *mddev, const char *page, size_t len)
|
2005-11-09 12:39:26 +07:00
|
|
|
{
|
2005-11-09 12:39:44 +07:00
|
|
|
if (!mddev->pers || !mddev->pers->sync_request)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2006-01-06 15:20:41 +07:00
|
|
|
if (cmd_match(page, "idle")) {
|
2005-11-09 12:39:44 +07:00
|
|
|
if (mddev->sync_thread) {
|
|
|
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
|
|
|
md_unregister_thread(mddev->sync_thread);
|
|
|
|
mddev->sync_thread = NULL;
|
|
|
|
mddev->recovery = 0;
|
|
|
|
}
|
2006-01-06 15:20:46 +07:00
|
|
|
} else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
|
|
|
|
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
|
2005-11-09 12:39:26 +07:00
|
|
|
return -EBUSY;
|
2008-06-28 05:31:41 +07:00
|
|
|
else if (cmd_match(page, "resync"))
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
else if (cmd_match(page, "recover")) {
|
|
|
|
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
|
2005-11-09 12:39:44 +07:00
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
2008-06-28 05:31:41 +07:00
|
|
|
} else if (cmd_match(page, "reshape")) {
|
2006-03-27 16:18:13 +07:00
|
|
|
int err;
|
|
|
|
if (mddev->pers->start_reshape == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
err = mddev->pers->start_reshape(mddev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2008-06-28 05:31:43 +07:00
|
|
|
sysfs_notify(&mddev->kobj, NULL, "degraded");
|
2006-03-27 16:18:13 +07:00
|
|
|
} else {
|
2006-01-06 15:20:41 +07:00
|
|
|
if (cmd_match(page, "check"))
|
2005-11-09 12:39:44 +07:00
|
|
|
set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
|
2006-05-21 04:59:57 +07:00
|
|
|
else if (!cmd_match(page, "repair"))
|
2005-11-09 12:39:44 +07:00
|
|
|
return -EINVAL;
|
|
|
|
set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
|
|
|
|
set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
|
|
|
|
}
|
2006-01-06 15:20:46 +07:00
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
2005-11-09 12:39:26 +07:00
|
|
|
md_wakeup_thread(mddev->thread);
|
2009-01-09 04:31:05 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_action);
|
2005-11-09 12:39:26 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2005-11-09 12:39:26 +07:00
|
|
|
static ssize_t
|
2005-11-09 12:39:39 +07:00
|
|
|
mismatch_cnt_show(mddev_t *mddev, char *page)
|
2005-11-09 12:39:26 +07:00
|
|
|
{
|
|
|
|
return sprintf(page, "%llu\n",
|
|
|
|
(unsigned long long) mddev->resync_mismatches);
|
|
|
|
}
|
|
|
|
|
2006-07-10 18:44:18 +07:00
|
|
|
static struct md_sysfs_entry md_scan_mode =
|
|
|
|
__ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
|
2005-11-09 12:39:26 +07:00
|
|
|
|
2005-11-09 12:39:39 +07:00
|
|
|
|
2006-07-10 18:44:18 +07:00
|
|
|
static struct md_sysfs_entry md_mismatches = __ATTR_RO(mismatch_cnt);
|
2005-11-09 12:39:26 +07:00
|
|
|
|
2006-01-06 15:21:36 +07:00
|
|
|
static ssize_t
|
|
|
|
sync_min_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%d (%s)\n", speed_min(mddev),
|
|
|
|
mddev->sync_speed_min ? "local": "system");
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sync_min_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
int min;
|
|
|
|
char *e;
|
|
|
|
if (strncmp(buf, "system", 6)==0) {
|
|
|
|
mddev->sync_speed_min = 0;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
min = simple_strtoul(buf, &e, 10);
|
|
|
|
if (buf == e || (*e && *e != '\n') || min <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
mddev->sync_speed_min = min;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_sync_min =
|
|
|
|
__ATTR(sync_speed_min, S_IRUGO|S_IWUSR, sync_min_show, sync_min_store);
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sync_max_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%d (%s)\n", speed_max(mddev),
|
|
|
|
mddev->sync_speed_max ? "local": "system");
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sync_max_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
int max;
|
|
|
|
char *e;
|
|
|
|
if (strncmp(buf, "system", 6)==0) {
|
|
|
|
mddev->sync_speed_max = 0;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
max = simple_strtoul(buf, &e, 10);
|
|
|
|
if (buf == e || (*e && *e != '\n') || max <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
mddev->sync_speed_max = max;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_sync_max =
|
|
|
|
__ATTR(sync_speed_max, S_IRUGO|S_IWUSR, sync_max_show, sync_max_store);
|
|
|
|
|
2007-10-17 13:30:54 +07:00
|
|
|
static ssize_t
|
|
|
|
degraded_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%d\n", mddev->degraded);
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_degraded = __ATTR_RO(degraded);
|
2006-01-06 15:21:36 +07:00
|
|
|
|
2008-05-24 03:04:38 +07:00
|
|
|
static ssize_t
|
|
|
|
sync_force_parallel_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%d\n", mddev->parallel_resync);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sync_force_parallel_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
long n;
|
|
|
|
|
|
|
|
if (strict_strtol(buf, 10, &n))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (n != 0 && n != 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mddev->parallel_resync = n;
|
|
|
|
|
|
|
|
if (mddev->sync_thread)
|
|
|
|
wake_up(&resync_wait);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* force parallel resync, even with shared block devices */
|
|
|
|
static struct md_sysfs_entry md_sync_force_parallel =
|
|
|
|
__ATTR(sync_force_parallel, S_IRUGO|S_IWUSR,
|
|
|
|
sync_force_parallel_show, sync_force_parallel_store);
|
|
|
|
|
2006-01-06 15:21:36 +07:00
|
|
|
static ssize_t
|
|
|
|
sync_speed_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
unsigned long resync, dt, db;
|
2008-03-26 04:24:09 +07:00
|
|
|
resync = mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active);
|
|
|
|
dt = (jiffies - mddev->resync_mark) / HZ;
|
2006-01-06 15:21:36 +07:00
|
|
|
if (!dt) dt++;
|
2008-03-26 04:24:09 +07:00
|
|
|
db = resync - mddev->resync_mark_cnt;
|
|
|
|
return sprintf(page, "%lu\n", db/dt/2); /* K/sec */
|
2006-01-06 15:21:36 +07:00
|
|
|
}
|
|
|
|
|
2006-07-10 18:44:18 +07:00
|
|
|
static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed);
|
2006-01-06 15:21:36 +07:00
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sync_completed_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
unsigned long max_blocks, resync;
|
|
|
|
|
|
|
|
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
|
|
|
|
max_blocks = mddev->resync_max_sectors;
|
|
|
|
else
|
|
|
|
max_blocks = mddev->size << 1;
|
|
|
|
|
|
|
|
resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active));
|
|
|
|
return sprintf(page, "%lu / %lu\n", resync, max_blocks);
|
|
|
|
}
|
|
|
|
|
2006-07-10 18:44:18 +07:00
|
|
|
static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
|
2006-01-06 15:21:36 +07:00
|
|
|
|
2008-06-28 05:31:24 +07:00
|
|
|
static ssize_t
|
|
|
|
min_sync_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%llu\n",
|
|
|
|
(unsigned long long)mddev->resync_min);
|
|
|
|
}
|
|
|
|
static ssize_t
|
|
|
|
min_sync_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
unsigned long long min;
|
|
|
|
if (strict_strtoull(buf, 10, &min))
|
|
|
|
return -EINVAL;
|
|
|
|
if (min > mddev->resync_max)
|
|
|
|
return -EINVAL;
|
|
|
|
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
/* Must be a multiple of chunk_size */
|
|
|
|
if (mddev->chunk_size) {
|
|
|
|
if (min & (sector_t)((mddev->chunk_size>>9)-1))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
mddev->resync_min = min;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_min_sync =
|
|
|
|
__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store);
|
|
|
|
|
2008-02-06 16:39:52 +07:00
|
|
|
static ssize_t
|
|
|
|
max_sync_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
if (mddev->resync_max == MaxSector)
|
|
|
|
return sprintf(page, "max\n");
|
|
|
|
else
|
|
|
|
return sprintf(page, "%llu\n",
|
|
|
|
(unsigned long long)mddev->resync_max);
|
|
|
|
}
|
|
|
|
static ssize_t
|
|
|
|
max_sync_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
if (strncmp(buf, "max", 3) == 0)
|
|
|
|
mddev->resync_max = MaxSector;
|
|
|
|
else {
|
2008-06-28 05:31:24 +07:00
|
|
|
unsigned long long max;
|
|
|
|
if (strict_strtoull(buf, 10, &max))
|
|
|
|
return -EINVAL;
|
|
|
|
if (max < mddev->resync_min)
|
2008-02-06 16:39:52 +07:00
|
|
|
return -EINVAL;
|
|
|
|
if (max < mddev->resync_max &&
|
|
|
|
test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
/* Must be a multiple of chunk_size */
|
|
|
|
if (mddev->chunk_size) {
|
|
|
|
if (max & (sector_t)((mddev->chunk_size>>9)-1))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
mddev->resync_max = max;
|
|
|
|
}
|
|
|
|
wake_up(&mddev->recovery_wait);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_max_sync =
|
|
|
|
__ATTR(sync_max, S_IRUGO|S_IWUSR, max_sync_show, max_sync_store);
|
|
|
|
|
2006-03-27 16:18:14 +07:00
|
|
|
static ssize_t
|
|
|
|
suspend_lo_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_lo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
suspend_lo_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
unsigned long long new = simple_strtoull(buf, &e, 10);
|
|
|
|
|
|
|
|
if (mddev->pers->quiesce == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
if (buf == e || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
if (new >= mddev->suspend_hi ||
|
|
|
|
(new > mddev->suspend_lo && new < mddev->suspend_hi)) {
|
|
|
|
mddev->suspend_lo = new;
|
|
|
|
mddev->pers->quiesce(mddev, 2);
|
|
|
|
return len;
|
|
|
|
} else
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_suspend_lo =
|
|
|
|
__ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
suspend_hi_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_hi);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
suspend_hi_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
unsigned long long new = simple_strtoull(buf, &e, 10);
|
|
|
|
|
|
|
|
if (mddev->pers->quiesce == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
if (buf == e || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
if ((new <= mddev->suspend_lo && mddev->suspend_lo >= mddev->suspend_hi) ||
|
|
|
|
(new > mddev->suspend_lo && new > mddev->suspend_hi)) {
|
|
|
|
mddev->suspend_hi = new;
|
|
|
|
mddev->pers->quiesce(mddev, 1);
|
|
|
|
mddev->pers->quiesce(mddev, 0);
|
|
|
|
return len;
|
|
|
|
} else
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
static struct md_sysfs_entry md_suspend_hi =
|
|
|
|
__ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
|
|
|
|
|
2007-05-09 16:35:38 +07:00
|
|
|
static ssize_t
|
|
|
|
reshape_position_show(mddev_t *mddev, char *page)
|
|
|
|
{
|
|
|
|
if (mddev->reshape_position != MaxSector)
|
|
|
|
return sprintf(page, "%llu\n",
|
|
|
|
(unsigned long long)mddev->reshape_position);
|
|
|
|
strcpy(page, "none\n");
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
reshape_position_store(mddev_t *mddev, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
unsigned long long new = simple_strtoull(buf, &e, 10);
|
|
|
|
if (mddev->pers)
|
|
|
|
return -EBUSY;
|
|
|
|
if (buf == e || (*e && *e != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
mddev->reshape_position = new;
|
|
|
|
mddev->delta_disks = 0;
|
|
|
|
mddev->new_level = mddev->level;
|
|
|
|
mddev->new_layout = mddev->layout;
|
|
|
|
mddev->new_chunk = mddev->chunk_size;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct md_sysfs_entry md_reshape_position =
|
|
|
|
__ATTR(reshape_position, S_IRUGO|S_IWUSR, reshape_position_show,
|
|
|
|
reshape_position_store);
|
|
|
|
|
2006-03-27 16:18:14 +07:00
|
|
|
|
2005-11-09 12:39:23 +07:00
|
|
|
static struct attribute *md_default_attrs[] = {
|
|
|
|
&md_level.attr,
|
2006-06-26 14:27:59 +07:00
|
|
|
&md_layout.attr,
|
2005-11-09 12:39:23 +07:00
|
|
|
&md_raid_disks.attr,
|
2006-01-06 15:20:47 +07:00
|
|
|
&md_chunk_size.attr,
|
2006-01-06 15:20:49 +07:00
|
|
|
&md_size.attr,
|
2006-06-26 14:28:00 +07:00
|
|
|
&md_resync_start.attr,
|
2006-01-06 15:20:50 +07:00
|
|
|
&md_metadata.attr,
|
2006-01-06 15:21:16 +07:00
|
|
|
&md_new_device.attr,
|
2006-06-26 14:27:37 +07:00
|
|
|
&md_safe_delay.attr,
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
&md_array_state.attr,
|
2007-05-09 16:35:38 +07:00
|
|
|
&md_reshape_position.attr,
|
2005-11-09 12:39:40 +07:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct attribute *md_redundancy_attrs[] = {
|
2005-11-09 12:39:26 +07:00
|
|
|
&md_scan_mode.attr,
|
2005-11-09 12:39:26 +07:00
|
|
|
&md_mismatches.attr,
|
2006-01-06 15:21:36 +07:00
|
|
|
&md_sync_min.attr,
|
|
|
|
&md_sync_max.attr,
|
|
|
|
&md_sync_speed.attr,
|
2008-05-24 03:04:38 +07:00
|
|
|
&md_sync_force_parallel.attr,
|
2006-01-06 15:21:36 +07:00
|
|
|
&md_sync_completed.attr,
|
2008-06-28 05:31:24 +07:00
|
|
|
&md_min_sync.attr,
|
2008-02-06 16:39:52 +07:00
|
|
|
&md_max_sync.attr,
|
2006-03-27 16:18:14 +07:00
|
|
|
&md_suspend_lo.attr,
|
|
|
|
&md_suspend_hi.attr,
|
2006-10-03 15:15:49 +07:00
|
|
|
&md_bitmap.attr,
|
2007-10-17 13:30:54 +07:00
|
|
|
&md_degraded.attr,
|
2005-11-09 12:39:23 +07:00
|
|
|
NULL,
|
|
|
|
};
|
2005-11-09 12:39:40 +07:00
|
|
|
static struct attribute_group md_redundancy_group = {
|
|
|
|
.name = NULL,
|
|
|
|
.attrs = md_redundancy_attrs,
|
|
|
|
};
|
|
|
|
|
2005-11-09 12:39:23 +07:00
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
|
|
|
|
{
|
|
|
|
struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr);
|
|
|
|
mddev_t *mddev = container_of(kobj, struct mddev_s, kobj);
|
2005-11-09 12:39:39 +07:00
|
|
|
ssize_t rv;
|
2005-11-09 12:39:23 +07:00
|
|
|
|
|
|
|
if (!entry->show)
|
|
|
|
return -EIO;
|
2006-04-20 16:43:23 +07:00
|
|
|
rv = mddev_lock(mddev);
|
|
|
|
if (!rv) {
|
|
|
|
rv = entry->show(mddev, page);
|
|
|
|
mddev_unlock(mddev);
|
|
|
|
}
|
2005-11-09 12:39:39 +07:00
|
|
|
return rv;
|
2005-11-09 12:39:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
md_attr_store(struct kobject *kobj, struct attribute *attr,
|
|
|
|
const char *page, size_t length)
|
|
|
|
{
|
|
|
|
struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr);
|
|
|
|
mddev_t *mddev = container_of(kobj, struct mddev_s, kobj);
|
2005-11-09 12:39:39 +07:00
|
|
|
ssize_t rv;
|
2005-11-09 12:39:23 +07:00
|
|
|
|
|
|
|
if (!entry->store)
|
|
|
|
return -EIO;
|
2006-07-10 18:44:19 +07:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2006-04-20 16:43:23 +07:00
|
|
|
rv = mddev_lock(mddev);
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
if (mddev->hold_active == UNTIL_IOCTL)
|
|
|
|
mddev->hold_active = 0;
|
2006-04-20 16:43:23 +07:00
|
|
|
if (!rv) {
|
|
|
|
rv = entry->store(mddev, page, length);
|
|
|
|
mddev_unlock(mddev);
|
|
|
|
}
|
2005-11-09 12:39:39 +07:00
|
|
|
return rv;
|
2005-11-09 12:39:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void md_free(struct kobject *ko)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = container_of(ko, mddev_t, kobj);
|
2009-01-09 04:31:09 +07:00
|
|
|
|
|
|
|
if (mddev->sysfs_state)
|
|
|
|
sysfs_put(mddev->sysfs_state);
|
|
|
|
|
|
|
|
if (mddev->gendisk) {
|
|
|
|
del_gendisk(mddev->gendisk);
|
|
|
|
put_disk(mddev->gendisk);
|
|
|
|
}
|
|
|
|
if (mddev->queue)
|
|
|
|
blk_cleanup_queue(mddev->queue);
|
|
|
|
|
2005-11-09 12:39:23 +07:00
|
|
|
kfree(mddev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sysfs_ops md_sysfs_ops = {
|
|
|
|
.show = md_attr_show,
|
|
|
|
.store = md_attr_store,
|
|
|
|
};
|
|
|
|
static struct kobj_type md_ktype = {
|
|
|
|
.release = md_free,
|
|
|
|
.sysfs_ops = &md_sysfs_ops,
|
|
|
|
.default_attrs = md_default_attrs,
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
int mdp_major = 0;
|
|
|
|
|
2009-01-09 04:31:10 +07:00
|
|
|
static int md_alloc(dev_t dev, char *name)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-03-27 16:18:20 +07:00
|
|
|
static DEFINE_MUTEX(disks_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_t *mddev = mddev_find(dev);
|
|
|
|
struct gendisk *disk;
|
2009-01-09 04:31:10 +07:00
|
|
|
int partitioned;
|
|
|
|
int shift;
|
|
|
|
int unit;
|
2007-12-18 02:54:39 +07:00
|
|
|
int error;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!mddev)
|
2009-01-09 04:31:10 +07:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
partitioned = (MAJOR(mddev->unit) != MD_MAJOR);
|
|
|
|
shift = partitioned ? MdpMinorShift : 0;
|
|
|
|
unit = MINOR(mddev->unit) >> shift;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
/* wait for any previous instance if this device
|
|
|
|
* to be completed removed (mddev_delayed_delete).
|
|
|
|
*/
|
|
|
|
flush_scheduled_work();
|
|
|
|
|
2006-03-27 16:18:20 +07:00
|
|
|
mutex_lock(&disks_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->gendisk) {
|
2006-03-27 16:18:20 +07:00
|
|
|
mutex_unlock(&disks_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_put(mddev);
|
2009-01-09 04:31:10 +07:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
/* Need to ensure that 'name' is not a duplicate.
|
|
|
|
*/
|
|
|
|
mddev_t *mddev2;
|
|
|
|
spin_lock(&all_mddevs_lock);
|
|
|
|
|
|
|
|
list_for_each_entry(mddev2, &all_mddevs, all_mddevs)
|
|
|
|
if (mddev2->gendisk &&
|
|
|
|
strcmp(mddev2->gendisk->disk_name, name) == 0) {
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-01-09 04:31:08 +07:00
|
|
|
|
|
|
|
mddev->queue = blk_alloc_queue(GFP_KERNEL);
|
|
|
|
if (!mddev->queue) {
|
|
|
|
mutex_unlock(&disks_mutex);
|
|
|
|
mddev_put(mddev);
|
2009-01-09 04:31:10 +07:00
|
|
|
return -ENOMEM;
|
2009-01-09 04:31:08 +07:00
|
|
|
}
|
|
|
|
/* Can be unlocked because the queue is new: no concurrency */
|
|
|
|
queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, mddev->queue);
|
|
|
|
|
|
|
|
blk_queue_make_request(mddev->queue, md_fail_request);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
disk = alloc_disk(1 << shift);
|
|
|
|
if (!disk) {
|
2006-03-27 16:18:20 +07:00
|
|
|
mutex_unlock(&disks_mutex);
|
2009-01-09 04:31:08 +07:00
|
|
|
blk_cleanup_queue(mddev->queue);
|
|
|
|
mddev->queue = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_put(mddev);
|
2009-01-09 04:31:10 +07:00
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-01-09 04:31:10 +07:00
|
|
|
disk->major = MAJOR(mddev->unit);
|
2005-04-17 05:20:36 +07:00
|
|
|
disk->first_minor = unit << shift;
|
2009-01-09 04:31:10 +07:00
|
|
|
if (name)
|
|
|
|
strcpy(disk->disk_name, name);
|
|
|
|
else if (partitioned)
|
2005-04-17 05:20:36 +07:00
|
|
|
sprintf(disk->disk_name, "md_d%d", unit);
|
2005-06-21 11:15:16 +07:00
|
|
|
else
|
2005-04-17 05:20:36 +07:00
|
|
|
sprintf(disk->disk_name, "md%d", unit);
|
|
|
|
disk->fops = &md_fops;
|
|
|
|
disk->private_data = mddev;
|
|
|
|
disk->queue = mddev->queue;
|
2008-10-21 09:25:32 +07:00
|
|
|
/* Allow extended partitions. This makes the
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
* 'mdp' device redundant, but we can't really
|
2008-10-21 09:25:32 +07:00
|
|
|
* remove it now.
|
|
|
|
*/
|
|
|
|
disk->flags |= GENHD_FL_EXT_DEVT;
|
2005-04-17 05:20:36 +07:00
|
|
|
add_disk(disk);
|
|
|
|
mddev->gendisk = disk;
|
2008-08-25 17:56:05 +07:00
|
|
|
error = kobject_init_and_add(&mddev->kobj, &md_ktype,
|
|
|
|
&disk_to_dev(disk)->kobj, "%s", "md");
|
2008-06-28 05:31:26 +07:00
|
|
|
mutex_unlock(&disks_mutex);
|
2007-12-18 02:54:39 +07:00
|
|
|
if (error)
|
2007-03-27 12:32:14 +07:00
|
|
|
printk(KERN_WARNING "md: cannot register %s/md - name in use\n",
|
|
|
|
disk->disk_name);
|
2008-10-21 09:25:21 +07:00
|
|
|
else {
|
2007-12-18 02:54:39 +07:00
|
|
|
kobject_uevent(&mddev->kobj, KOBJ_ADD);
|
2008-10-21 09:25:21 +07:00
|
|
|
mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state");
|
|
|
|
}
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
mddev_put(mddev);
|
2009-01-09 04:31:10 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct kobject *md_probe(dev_t dev, int *part, void *data)
|
|
|
|
{
|
|
|
|
md_alloc(dev, NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-01-09 04:31:10 +07:00
|
|
|
static int add_named_array(const char *val, struct kernel_param *kp)
|
|
|
|
{
|
|
|
|
/* val must be "md_*" where * is not all digits.
|
|
|
|
* We allocate an array with a large free minor number, and
|
|
|
|
* set the name to val. val must not already be an active name.
|
|
|
|
*/
|
|
|
|
int len = strlen(val);
|
|
|
|
char buf[DISK_NAME_LEN];
|
|
|
|
|
|
|
|
while (len && val[len-1] == '\n')
|
|
|
|
len--;
|
|
|
|
if (len >= DISK_NAME_LEN)
|
|
|
|
return -E2BIG;
|
|
|
|
strlcpy(buf, val, len+1);
|
|
|
|
if (strncmp(buf, "md_", 3) != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
return md_alloc(0, buf);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void md_safemode_timeout(unsigned long data)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = (mddev_t *) data;
|
|
|
|
|
2008-06-28 05:31:36 +07:00
|
|
|
if (!atomic_read(&mddev->writes_pending)) {
|
|
|
|
mddev->safemode = 1;
|
|
|
|
if (mddev->external)
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2008-06-28 05:31:36 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
md_wakeup_thread(mddev->thread);
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:15 +07:00
|
|
|
static int start_dirty_degraded;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static int do_md_run(mddev_t * mddev)
|
|
|
|
{
|
2006-01-06 15:20:36 +07:00
|
|
|
int err;
|
2005-04-17 05:20:36 +07:00
|
|
|
int chunk_size;
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
struct gendisk *disk;
|
2006-01-06 15:20:36 +07:00
|
|
|
struct mdk_personality *pers;
|
2005-04-17 05:20:36 +07:00
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
|
2005-04-17 05:26:42 +07:00
|
|
|
if (list_empty(&mddev->disks))
|
|
|
|
/* cannot run an array with no devices.. */
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (mddev->pers)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Analyze all RAID superblock(s)
|
|
|
|
*/
|
2008-02-06 16:39:53 +07:00
|
|
|
if (!mddev->raid_disks) {
|
|
|
|
if (!mddev->persistent)
|
|
|
|
return -EINVAL;
|
2005-04-17 05:26:42 +07:00
|
|
|
analyze_sbs(mddev);
|
2008-02-06 16:39:53 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
chunk_size = mddev->chunk_size;
|
2006-01-06 15:20:36 +07:00
|
|
|
|
|
|
|
if (chunk_size) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (chunk_size > MAX_CHUNK_SIZE) {
|
|
|
|
printk(KERN_ERR "too big chunk_size: %d > %d\n",
|
|
|
|
chunk_size, MAX_CHUNK_SIZE);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/*
|
2008-10-13 07:55:12 +07:00
|
|
|
* chunk-size has to be a power of 2
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
if ( (1 << ffz(~chunk_size)) != chunk_size) {
|
2005-04-17 05:26:42 +07:00
|
|
|
printk(KERN_ERR "chunk_size of %d not valid\n", chunk_size);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* devices must have minimum size of one chunk */
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
|
|
|
if (rdev->size < chunk_size / 1024) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: Dev %s smaller than chunk_size:"
|
|
|
|
" %lluk < %dk\n",
|
|
|
|
bdevname(rdev->bdev,b),
|
|
|
|
(unsigned long long)rdev->size,
|
|
|
|
chunk_size / 1024);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:51 +07:00
|
|
|
if (mddev->level != LEVEL_NONE)
|
|
|
|
request_module("md-level-%d", mddev->level);
|
|
|
|
else if (mddev->clevel[0])
|
|
|
|
request_module("md-%s", mddev->clevel);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Drop all container device buffers, from now on
|
|
|
|
* the only valid external interface is through the md
|
|
|
|
* device.
|
|
|
|
*/
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
|
|
|
sync_blockdev(rdev->bdev);
|
2007-05-07 04:49:54 +07:00
|
|
|
invalidate_bdev(rdev->bdev);
|
2007-07-17 18:06:12 +07:00
|
|
|
|
|
|
|
/* perform some consistency tests on the device.
|
|
|
|
* We don't want the data to overlap the metadata,
|
|
|
|
* Internal Bitmap issues has handled elsewhere.
|
|
|
|
*/
|
2008-07-11 19:02:23 +07:00
|
|
|
if (rdev->data_offset < rdev->sb_start) {
|
2007-07-17 18:06:12 +07:00
|
|
|
if (mddev->size &&
|
|
|
|
rdev->data_offset + mddev->size*2
|
2008-07-11 19:02:23 +07:00
|
|
|
> rdev->sb_start) {
|
2007-07-17 18:06:12 +07:00
|
|
|
printk("md: %s: data overlaps metadata\n",
|
|
|
|
mdname(mddev));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else {
|
2008-07-11 19:02:23 +07:00
|
|
|
if (rdev->sb_start + rdev->sb_size/512
|
2007-07-17 18:06:12 +07:00
|
|
|
> rdev->data_offset) {
|
|
|
|
printk("md: %s: metadata overlaps data\n",
|
|
|
|
mdname(mddev));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2008-10-21 09:25:28 +07:00
|
|
|
sysfs_notify_dirent(rdev->sysfs_state);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
md_probe(mddev->unit, NULL, NULL);
|
|
|
|
disk = mddev->gendisk;
|
|
|
|
if (!disk)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock(&pers_lock);
|
2006-01-06 15:20:51 +07:00
|
|
|
pers = find_pers(mddev->level, mddev->clevel);
|
2006-01-06 15:20:36 +07:00
|
|
|
if (!pers || !try_module_get(pers->owner)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&pers_lock);
|
2006-01-06 15:20:51 +07:00
|
|
|
if (mddev->level != LEVEL_NONE)
|
|
|
|
printk(KERN_WARNING "md: personality for level %d is not loaded!\n",
|
|
|
|
mddev->level);
|
|
|
|
else
|
|
|
|
printk(KERN_WARNING "md: personality for level %s is not loaded!\n",
|
|
|
|
mddev->clevel);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2006-01-06 15:20:36 +07:00
|
|
|
mddev->pers = pers;
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&pers_lock);
|
2006-01-06 15:20:51 +07:00
|
|
|
mddev->level = pers->level;
|
|
|
|
strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-03-31 10:27:02 +07:00
|
|
|
if (pers->level >= 4 && pers->level <= 6)
|
|
|
|
/* Cannot support integrity (yet) */
|
|
|
|
blk_integrity_unregister(mddev->gendisk);
|
|
|
|
|
2006-03-27 16:18:11 +07:00
|
|
|
if (mddev->reshape_position != MaxSector &&
|
2006-03-27 16:18:13 +07:00
|
|
|
pers->start_reshape == NULL) {
|
2006-03-27 16:18:11 +07:00
|
|
|
/* This personality cannot handle reshaping... */
|
|
|
|
mddev->pers = NULL;
|
|
|
|
module_put(pers->owner);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-03-01 11:11:35 +07:00
|
|
|
if (pers->sync_request) {
|
|
|
|
/* Warn if this is a potentially silly
|
|
|
|
* configuration.
|
|
|
|
*/
|
|
|
|
char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
|
|
|
|
mdk_rdev_t *rdev2;
|
|
|
|
int warned = 0;
|
2009-01-09 04:31:08 +07:00
|
|
|
|
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
|
|
|
list_for_each_entry(rdev2, &mddev->disks, same_set) {
|
2007-03-01 11:11:35 +07:00
|
|
|
if (rdev < rdev2 &&
|
|
|
|
rdev->bdev->bd_contains ==
|
|
|
|
rdev2->bdev->bd_contains) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"%s: WARNING: %s appears to be"
|
|
|
|
" on the same physical disk as"
|
|
|
|
" %s.\n",
|
|
|
|
mdname(mddev),
|
|
|
|
bdevname(rdev->bdev,b),
|
|
|
|
bdevname(rdev2->bdev,b2));
|
|
|
|
warned = 1;
|
|
|
|
}
|
|
|
|
}
|
2009-01-09 04:31:08 +07:00
|
|
|
|
2007-03-01 11:11:35 +07:00
|
|
|
if (warned)
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"True protection against single-disk"
|
|
|
|
" failure might be compromised.\n");
|
|
|
|
}
|
|
|
|
|
2005-08-27 08:34:16 +07:00
|
|
|
mddev->recovery = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
mddev->barriers_work = 1;
|
2006-01-06 15:20:15 +07:00
|
|
|
mddev->ok_start_degraded = start_dirty_degraded;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
if (start_readonly)
|
|
|
|
mddev->ro = 2; /* read-only, but switch on first write */
|
|
|
|
|
2006-01-06 15:20:16 +07:00
|
|
|
err = mddev->pers->run(mddev);
|
2008-03-26 06:07:03 +07:00
|
|
|
if (err)
|
|
|
|
printk(KERN_ERR "md: pers->run() failed ...\n");
|
|
|
|
else if (mddev->pers->sync_request) {
|
2006-01-06 15:20:16 +07:00
|
|
|
err = bitmap_create(mddev);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: failed to create bitmap (%d)\n",
|
|
|
|
mdname(mddev), err);
|
|
|
|
mddev->pers->stop(mddev);
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err) {
|
|
|
|
module_put(mddev->pers->owner);
|
|
|
|
mddev->pers = NULL;
|
2005-06-22 07:17:14 +07:00
|
|
|
bitmap_destroy(mddev);
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-27 12:32:14 +07:00
|
|
|
if (mddev->pers->sync_request) {
|
|
|
|
if (sysfs_create_group(&mddev->kobj, &md_redundancy_group))
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: cannot register extra attributes for %s\n",
|
|
|
|
mdname(mddev));
|
2009-01-09 04:31:05 +07:00
|
|
|
mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action");
|
2007-03-27 12:32:14 +07:00
|
|
|
} else if (mddev->ro == 2) /* auto-readonly not meaningful */
|
2005-11-09 12:39:42 +07:00
|
|
|
mddev->ro = 0;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
atomic_set(&mddev->writes_pending,0);
|
|
|
|
mddev->safemode = 0;
|
|
|
|
mddev->safemode_timer.function = md_safemode_timeout;
|
|
|
|
mddev->safemode_timer.data = (unsigned long) mddev;
|
2006-06-26 14:27:37 +07:00
|
|
|
mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->in_sync = 1;
|
2005-11-09 12:39:24 +07:00
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2005-11-09 12:39:24 +07:00
|
|
|
if (rdev->raid_disk >= 0) {
|
|
|
|
char nm[20];
|
|
|
|
sprintf(nm, "rd%d", rdev->raid_disk);
|
2007-03-27 12:32:14 +07:00
|
|
|
if (sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
|
|
|
|
printk("md: cannot register %s for %s\n",
|
|
|
|
nm, mdname(mddev));
|
2005-11-09 12:39:24 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
|
2006-10-03 15:15:46 +07:00
|
|
|
if (mddev->flags)
|
|
|
|
md_update_sb(mddev, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-21 14:05:22 +07:00
|
|
|
set_capacity(disk, mddev->array_sectors);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* If we call blk_queue_make_request here, it will
|
|
|
|
* re-initialise max_sectors etc which may have been
|
|
|
|
* refined inside -> run. So just set the bits we need to set.
|
|
|
|
* Most initialisation happended when we called
|
|
|
|
* blk_queue_make_request(..., md_fail_request)
|
|
|
|
* earlier.
|
|
|
|
*/
|
|
|
|
mddev->queue->queuedata = mddev;
|
|
|
|
mddev->queue->make_request_fn = mddev->pers->make_request;
|
|
|
|
|
2006-06-26 14:27:40 +07:00
|
|
|
/* If there is a partially-recovered drive we need to
|
|
|
|
* start recovery here. If we leave it to md_check_recovery,
|
|
|
|
* it will remove the drives and not do the right thing
|
|
|
|
*/
|
2006-07-10 18:44:15 +07:00
|
|
|
if (mddev->degraded && !mddev->sync_thread) {
|
2006-06-26 14:27:40 +07:00
|
|
|
int spares = 0;
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2006-06-26 14:27:40 +07:00
|
|
|
if (rdev->raid_disk >= 0 &&
|
|
|
|
!test_bit(In_sync, &rdev->flags) &&
|
|
|
|
!test_bit(Faulty, &rdev->flags))
|
|
|
|
/* complete an interrupted recovery */
|
|
|
|
spares++;
|
|
|
|
if (spares && mddev->pers->sync_request) {
|
|
|
|
mddev->recovery = 0;
|
|
|
|
set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
|
|
|
|
mddev->sync_thread = md_register_thread(md_do_sync,
|
|
|
|
mddev,
|
|
|
|
"%s_resync");
|
|
|
|
if (!mddev->sync_thread) {
|
|
|
|
printk(KERN_ERR "%s: could not start resync"
|
|
|
|
" thread...\n",
|
|
|
|
mdname(mddev));
|
|
|
|
/* leave the spares where they are, it shouldn't hurt */
|
|
|
|
mddev->recovery = 0;
|
2006-07-10 18:44:15 +07:00
|
|
|
}
|
2006-06-26 14:27:40 +07:00
|
|
|
}
|
|
|
|
}
|
2006-07-10 18:44:15 +07:00
|
|
|
md_wakeup_thread(mddev->thread);
|
|
|
|
md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
|
2006-06-26 14:27:40 +07:00
|
|
|
|
2007-05-10 08:51:36 +07:00
|
|
|
mddev->changed = 1;
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
md_new_event(mddev);
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2009-01-09 04:31:05 +07:00
|
|
|
if (mddev->sysfs_action)
|
|
|
|
sysfs_notify_dirent(mddev->sysfs_action);
|
2008-06-28 05:31:43 +07:00
|
|
|
sysfs_notify(&mddev->kobj, NULL, "degraded");
|
2008-08-25 17:56:05 +07:00
|
|
|
kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int restart_array(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
struct gendisk *disk = mddev->gendisk;
|
|
|
|
|
2008-07-11 19:02:21 +07:00
|
|
|
/* Complain if it has no devices */
|
2005-04-17 05:20:36 +07:00
|
|
|
if (list_empty(&mddev->disks))
|
2008-07-11 19:02:21 +07:00
|
|
|
return -ENXIO;
|
|
|
|
if (!mddev->pers)
|
|
|
|
return -EINVAL;
|
|
|
|
if (!mddev->ro)
|
|
|
|
return -EBUSY;
|
|
|
|
mddev->safemode = 0;
|
|
|
|
mddev->ro = 0;
|
|
|
|
set_disk_ro(disk, 0);
|
|
|
|
printk(KERN_INFO "md: %s switched to read-write mode.\n",
|
|
|
|
mdname(mddev));
|
|
|
|
/* Kick recovery or resync if necessary */
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
md_wakeup_thread(mddev->thread);
|
|
|
|
md_wakeup_thread(mddev->sync_thread);
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2008-07-11 19:02:21 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-26 14:27:47 +07:00
|
|
|
/* similar to deny_write_access, but accounts for our holding a reference
|
|
|
|
* to the file ourselves */
|
|
|
|
static int deny_bitmap_write_access(struct file * file)
|
|
|
|
{
|
|
|
|
struct inode *inode = file->f_mapping->host;
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
if (atomic_read(&inode->i_writecount) > 1) {
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
return -ETXTBSY;
|
|
|
|
}
|
|
|
|
atomic_set(&inode->i_writecount, -1);
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void restore_bitmap_write_access(struct file *file)
|
|
|
|
{
|
|
|
|
struct inode *inode = file->f_mapping->host;
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
atomic_set(&inode->i_writecount, 1);
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
|
|
|
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
/* mode:
|
|
|
|
* 0 - completely stop and dis-assemble array
|
|
|
|
* 1 - switch to readonly
|
|
|
|
* 2 - stop but do not disassemble array
|
|
|
|
*/
|
2008-07-11 19:02:22 +07:00
|
|
|
static int do_md_stop(mddev_t * mddev, int mode, int is_open)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct gendisk *disk = mddev->gendisk;
|
|
|
|
|
2008-07-21 14:05:25 +07:00
|
|
|
if (atomic_read(&mddev->openers) > is_open) {
|
2008-07-11 19:02:22 +07:00
|
|
|
printk("md: %s still in use.\n",mdname(mddev));
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->pers) {
|
|
|
|
|
|
|
|
if (mddev->sync_thread) {
|
2006-06-26 14:27:40 +07:00
|
|
|
set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
|
2005-04-17 05:20:36 +07:00
|
|
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
|
|
|
md_unregister_thread(mddev->sync_thread);
|
|
|
|
mddev->sync_thread = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
del_timer_sync(&mddev->safemode_timer);
|
|
|
|
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
switch(mode) {
|
|
|
|
case 1: /* readonly */
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -ENXIO;
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
if (mddev->ro==1)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
mddev->ro = 1;
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
break;
|
|
|
|
case 0: /* disassemble */
|
|
|
|
case 2: /* stop */
|
2005-08-05 02:53:35 +07:00
|
|
|
bitmap_flush(mddev);
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
md_super_wait(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->ro)
|
|
|
|
set_disk_ro(disk, 0);
|
|
|
|
blk_queue_make_request(mddev->queue, md_fail_request);
|
|
|
|
mddev->pers->stop(mddev);
|
2007-03-01 11:11:42 +07:00
|
|
|
mddev->queue->merge_bvec_fn = NULL;
|
|
|
|
mddev->queue->unplug_fn = NULL;
|
2007-03-27 12:32:14 +07:00
|
|
|
mddev->queue->backing_dev_info.congested_fn = NULL;
|
2009-01-09 04:31:05 +07:00
|
|
|
if (mddev->pers->sync_request) {
|
2005-11-09 12:39:40 +07:00
|
|
|
sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
|
2009-01-09 04:31:05 +07:00
|
|
|
if (mddev->sysfs_action)
|
|
|
|
sysfs_put(mddev->sysfs_action);
|
|
|
|
mddev->sysfs_action = NULL;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
module_put(mddev->pers->owner);
|
|
|
|
mddev->pers = NULL;
|
2008-05-24 03:04:37 +07:00
|
|
|
/* tell userspace to handle 'inactive' */
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2006-12-10 17:20:44 +07:00
|
|
|
|
|
|
|
set_capacity(disk, 0);
|
2007-05-10 08:51:36 +07:00
|
|
|
mddev->changed = 1;
|
2006-12-10 17:20:44 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->ro)
|
|
|
|
mddev->ro = 0;
|
|
|
|
}
|
2006-10-03 15:15:46 +07:00
|
|
|
if (!mddev->in_sync || mddev->flags) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* mark array as shutdown cleanly */
|
|
|
|
mddev->in_sync = 1;
|
2006-10-03 15:15:46 +07:00
|
|
|
md_update_sb(mddev, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
if (mode == 1)
|
2005-04-17 05:20:36 +07:00
|
|
|
set_disk_ro(disk, 1);
|
2006-06-26 14:27:40 +07:00
|
|
|
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Free resources if final stop
|
|
|
|
*/
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
if (mode == 0) {
|
2005-11-09 12:39:24 +07:00
|
|
|
mdk_rdev_t *rdev;
|
2006-12-10 17:20:44 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
|
|
|
|
|
2006-02-03 05:28:05 +07:00
|
|
|
bitmap_destroy(mddev);
|
|
|
|
if (mddev->bitmap_file) {
|
2006-06-26 14:27:47 +07:00
|
|
|
restore_bitmap_write_access(mddev->bitmap_file);
|
2006-02-03 05:28:05 +07:00
|
|
|
fput(mddev->bitmap_file);
|
|
|
|
mddev->bitmap_file = NULL;
|
|
|
|
}
|
|
|
|
mddev->bitmap_offset = 0;
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2005-11-09 12:39:24 +07:00
|
|
|
if (rdev->raid_disk >= 0) {
|
|
|
|
char nm[20];
|
|
|
|
sprintf(nm, "rd%d", rdev->raid_disk);
|
|
|
|
sysfs_remove_link(&mddev->kobj, nm);
|
|
|
|
}
|
|
|
|
|
2008-02-06 16:39:56 +07:00
|
|
|
/* make sure all md_delayed_delete calls have finished */
|
2007-04-05 09:08:18 +07:00
|
|
|
flush_scheduled_work();
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
export_array(mddev);
|
|
|
|
|
2008-07-21 14:05:22 +07:00
|
|
|
mddev->array_sectors = 0;
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
mddev->size = 0;
|
|
|
|
mddev->raid_disks = 0;
|
2006-06-26 14:28:00 +07:00
|
|
|
mddev->recovery_cp = 0;
|
2008-06-28 05:31:24 +07:00
|
|
|
mddev->resync_min = 0;
|
2008-02-06 16:39:52 +07:00
|
|
|
mddev->resync_max = MaxSector;
|
2007-05-09 16:35:38 +07:00
|
|
|
mddev->reshape_position = MaxSector;
|
2008-02-06 16:39:51 +07:00
|
|
|
mddev->external = 0;
|
2008-02-06 16:39:53 +07:00
|
|
|
mddev->persistent = 0;
|
2008-04-30 14:52:29 +07:00
|
|
|
mddev->level = LEVEL_NONE;
|
|
|
|
mddev->clevel[0] = 0;
|
|
|
|
mddev->flags = 0;
|
|
|
|
mddev->ro = 0;
|
|
|
|
mddev->metadata_type[0] = 0;
|
|
|
|
mddev->chunk_size = 0;
|
|
|
|
mddev->ctime = mddev->utime = 0;
|
|
|
|
mddev->layout = 0;
|
|
|
|
mddev->max_disks = 0;
|
|
|
|
mddev->events = 0;
|
|
|
|
mddev->delta_disks = 0;
|
|
|
|
mddev->new_level = LEVEL_NONE;
|
|
|
|
mddev->new_layout = 0;
|
|
|
|
mddev->new_chunk = 0;
|
|
|
|
mddev->curr_resync = 0;
|
|
|
|
mddev->resync_mismatches = 0;
|
|
|
|
mddev->suspend_lo = mddev->suspend_hi = 0;
|
|
|
|
mddev->sync_speed_min = mddev->sync_speed_max = 0;
|
|
|
|
mddev->recovery = 0;
|
|
|
|
mddev->in_sync = 0;
|
|
|
|
mddev->changed = 0;
|
|
|
|
mddev->degraded = 0;
|
|
|
|
mddev->barriers_work = 0;
|
|
|
|
mddev->safemode = 0;
|
2008-10-28 13:01:23 +07:00
|
|
|
kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
|
2009-01-09 04:31:10 +07:00
|
|
|
if (mddev->hold_active == UNTIL_STOP)
|
|
|
|
mddev->hold_active = 0;
|
[PATCH] md: Set/get state of array via sysfs
This allows the state of an md/array to be directly controlled via sysfs and
adds the ability to stop and array without tearing it down.
Array states/settings:
clear
No devices, no size, no level
Equivalent to STOP_ARRAY ioctl
inactive
May have some settings, but array is not active
all IO results in error
When written, doesn't tear down array, but just stops it
suspended (not supported yet)
All IO requests will block. The array can be reconfigured.
Writing this, if accepted, will block until array is quiescent
readonly
no resync can happen. no superblocks get written.
write requests fail
read-auto
like readonly, but behaves like 'clean' on a write request.
clean - no pending writes, but otherwise active.
When written to inactive array, starts without resync
If a write request arrives then
if metadata is known, mark 'dirty' and switch to 'active'.
if not known, block and switch to write-pending
If written to an active array that has pending writes, then fails.
active
fully active: IO and resync can be happening.
When written to inactive array, starts with resync
write-pending (not supported yet)
clean, but writes are blocked waiting for 'active' to be written.
active-idle
like active, but no writes have been seen for a while (100msec).
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 14:27:58 +07:00
|
|
|
|
2006-06-26 14:27:39 +07:00
|
|
|
} else if (mddev->pers)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_INFO "md: %s switched to read-only mode.\n",
|
|
|
|
mdname(mddev));
|
|
|
|
err = 0;
|
2009-03-31 10:27:02 +07:00
|
|
|
blk_integrity_unregister(disk);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
md_new_event(mddev);
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2005-04-17 05:20:36 +07:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-12-10 17:20:50 +07:00
|
|
|
#ifndef MODULE
|
2005-04-17 05:20:36 +07:00
|
|
|
static void autorun_array(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
int err;
|
|
|
|
|
2005-04-17 05:26:42 +07:00
|
|
|
if (list_empty(&mddev->disks))
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
printk(KERN_INFO "md: running: ");
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2005-04-17 05:20:36 +07:00
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
printk("<%s>", bdevname(rdev->bdev,b));
|
|
|
|
}
|
|
|
|
printk("\n");
|
|
|
|
|
2008-10-13 07:55:12 +07:00
|
|
|
err = do_md_run(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err) {
|
|
|
|
printk(KERN_WARNING "md: do_md_run() returned %d\n", err);
|
2008-10-13 07:55:12 +07:00
|
|
|
do_md_stop(mddev, 0, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* lets try to run arrays based on all disks that have arrived
|
|
|
|
* until now. (those are in pending_raid_disks)
|
|
|
|
*
|
|
|
|
* the method: pick the first pending disk, collect all disks with
|
|
|
|
* the same UUID, remove all from the pending list and put them into
|
|
|
|
* the 'same_array' list. Then order this list based on superblock
|
|
|
|
* update time (freshest comes first), kick out 'old' disks and
|
|
|
|
* compare superblocks. If everything's fine then run it.
|
|
|
|
*
|
|
|
|
* If "unit" is allocated, then bump its reference count
|
|
|
|
*/
|
|
|
|
static void autorun_devices(int part)
|
|
|
|
{
|
2009-01-09 04:31:08 +07:00
|
|
|
mdk_rdev_t *rdev0, *rdev, *tmp;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_t *mddev;
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
|
|
|
|
printk(KERN_INFO "md: autorun ...\n");
|
|
|
|
while (!list_empty(&pending_raid_disks)) {
|
2006-10-03 15:15:59 +07:00
|
|
|
int unit;
|
2005-04-17 05:20:36 +07:00
|
|
|
dev_t dev;
|
2006-03-27 16:18:07 +07:00
|
|
|
LIST_HEAD(candidates);
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev0 = list_entry(pending_raid_disks.next,
|
|
|
|
mdk_rdev_t, same_set);
|
|
|
|
|
|
|
|
printk(KERN_INFO "md: considering %s ...\n",
|
|
|
|
bdevname(rdev0->bdev,b));
|
|
|
|
INIT_LIST_HEAD(&candidates);
|
2009-01-09 04:31:08 +07:00
|
|
|
rdev_for_each_list(rdev, tmp, &pending_raid_disks)
|
2005-04-17 05:20:36 +07:00
|
|
|
if (super_90_load(rdev, rdev0, 0) >= 0) {
|
|
|
|
printk(KERN_INFO "md: adding %s ...\n",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
list_move(&rdev->same_set, &candidates);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* now we have a set of devices, with all of them having
|
|
|
|
* mostly sane superblocks. It's time to allocate the
|
|
|
|
* mddev.
|
|
|
|
*/
|
2006-10-03 15:15:59 +07:00
|
|
|
if (part) {
|
|
|
|
dev = MKDEV(mdp_major,
|
|
|
|
rdev0->preferred_minor << MdpMinorShift);
|
|
|
|
unit = MINOR(dev) >> MdpMinorShift;
|
|
|
|
} else {
|
|
|
|
dev = MKDEV(MD_MAJOR, rdev0->preferred_minor);
|
|
|
|
unit = MINOR(dev);
|
|
|
|
}
|
|
|
|
if (rdev0->preferred_minor != unit) {
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_INFO "md: unit number in %s is bad: %d\n",
|
|
|
|
bdevname(rdev0->bdev, b), rdev0->preferred_minor);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
md_probe(dev, NULL, NULL);
|
|
|
|
mddev = mddev_find(dev);
|
2008-06-28 05:31:17 +07:00
|
|
|
if (!mddev || !mddev->gendisk) {
|
|
|
|
if (mddev)
|
|
|
|
mddev_put(mddev);
|
|
|
|
printk(KERN_ERR
|
2005-04-17 05:20:36 +07:00
|
|
|
"md: cannot allocate memory for md drive.\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (mddev_lock(mddev))
|
|
|
|
printk(KERN_WARNING "md: %s locked, cannot run\n",
|
|
|
|
mdname(mddev));
|
|
|
|
else if (mddev->raid_disks || mddev->major_version
|
|
|
|
|| !list_empty(&mddev->disks)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: %s already running, cannot run %s\n",
|
|
|
|
mdname(mddev), bdevname(rdev0->bdev,b));
|
|
|
|
mddev_unlock(mddev);
|
|
|
|
} else {
|
|
|
|
printk(KERN_INFO "md: created %s\n", mdname(mddev));
|
2008-02-06 16:39:53 +07:00
|
|
|
mddev->persistent = 1;
|
2009-01-09 04:31:08 +07:00
|
|
|
rdev_for_each_list(rdev, tmp, &candidates) {
|
2005-04-17 05:20:36 +07:00
|
|
|
list_del_init(&rdev->same_set);
|
|
|
|
if (bind_rdev_to_array(rdev, mddev))
|
|
|
|
export_rdev(rdev);
|
|
|
|
}
|
|
|
|
autorun_array(mddev);
|
|
|
|
mddev_unlock(mddev);
|
|
|
|
}
|
|
|
|
/* on success, candidates will be empty, on error
|
|
|
|
* it won't...
|
|
|
|
*/
|
2009-01-09 04:31:08 +07:00
|
|
|
rdev_for_each_list(rdev, tmp, &candidates) {
|
2008-07-21 14:05:25 +07:00
|
|
|
list_del_init(&rdev->same_set);
|
2005-04-17 05:20:36 +07:00
|
|
|
export_rdev(rdev);
|
2008-07-21 14:05:25 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_put(mddev);
|
|
|
|
}
|
|
|
|
printk(KERN_INFO "md: ... autorun DONE.\n");
|
|
|
|
}
|
2006-12-10 17:20:50 +07:00
|
|
|
#endif /* !MODULE */
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static int get_version(void __user * arg)
|
|
|
|
{
|
|
|
|
mdu_version_t ver;
|
|
|
|
|
|
|
|
ver.major = MD_MAJOR_VERSION;
|
|
|
|
ver.minor = MD_MINOR_VERSION;
|
|
|
|
ver.patchlevel = MD_PATCHLEVEL_VERSION;
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &ver, sizeof(ver)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_array_info(mddev_t * mddev, void __user * arg)
|
|
|
|
{
|
|
|
|
mdu_array_info_t info;
|
|
|
|
int nr,working,active,failed,spare;
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
|
|
|
nr=working=active=failed=spare=0;
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2005-04-17 05:20:36 +07:00
|
|
|
nr++;
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
failed++;
|
|
|
|
else {
|
|
|
|
working++;
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(In_sync, &rdev->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
active++;
|
|
|
|
else
|
|
|
|
spare++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
info.major_version = mddev->major_version;
|
|
|
|
info.minor_version = mddev->minor_version;
|
|
|
|
info.patch_version = MD_PATCHLEVEL_VERSION;
|
|
|
|
info.ctime = mddev->ctime;
|
|
|
|
info.level = mddev->level;
|
|
|
|
info.size = mddev->size;
|
2006-02-03 18:03:40 +07:00
|
|
|
if (info.size != mddev->size) /* overflow */
|
|
|
|
info.size = -1;
|
2005-04-17 05:20:36 +07:00
|
|
|
info.nr_disks = nr;
|
|
|
|
info.raid_disks = mddev->raid_disks;
|
|
|
|
info.md_minor = mddev->md_minor;
|
|
|
|
info.not_persistent= !mddev->persistent;
|
|
|
|
|
|
|
|
info.utime = mddev->utime;
|
|
|
|
info.state = 0;
|
|
|
|
if (mddev->in_sync)
|
|
|
|
info.state = (1<<MD_SB_CLEAN);
|
2005-09-10 06:23:45 +07:00
|
|
|
if (mddev->bitmap && mddev->bitmap_offset)
|
|
|
|
info.state = (1<<MD_SB_BITMAP_PRESENT);
|
2005-04-17 05:20:36 +07:00
|
|
|
info.active_disks = active;
|
|
|
|
info.working_disks = working;
|
|
|
|
info.failed_disks = failed;
|
|
|
|
info.spare_disks = spare;
|
|
|
|
|
|
|
|
info.layout = mddev->layout;
|
|
|
|
info.chunk_size = mddev->chunk_size;
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &info, sizeof(info)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 02:36:43 +07:00
|
|
|
static int get_bitmap_file(mddev_t * mddev, void __user * arg)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
mdu_bitmap_file_t *file = NULL; /* too big for stack allocation */
|
|
|
|
char *ptr, *buf = NULL;
|
|
|
|
int err = -ENOMEM;
|
|
|
|
|
2008-06-28 11:44:04 +07:00
|
|
|
if (md_allow_write(mddev))
|
|
|
|
file = kmalloc(sizeof(*file), GFP_NOIO);
|
|
|
|
else
|
|
|
|
file = kmalloc(sizeof(*file), GFP_KERNEL);
|
2007-01-26 15:57:11 +07:00
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
if (!file)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* bitmap disabled, zero the first byte and copy out */
|
|
|
|
if (!mddev->bitmap || !mddev->bitmap->file) {
|
|
|
|
file->pathname[0] = '\0';
|
|
|
|
goto copy_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = kmalloc(sizeof(file->pathname), GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
goto out;
|
|
|
|
|
2008-05-24 03:04:34 +07:00
|
|
|
ptr = d_path(&mddev->bitmap->file->f_path, buf, sizeof(file->pathname));
|
|
|
|
if (IS_ERR(ptr))
|
2005-06-22 07:17:14 +07:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
strcpy(file->pathname, ptr);
|
|
|
|
|
|
|
|
copy_out:
|
|
|
|
err = 0;
|
|
|
|
if (copy_to_user(arg, file, sizeof(*file)))
|
|
|
|
err = -EFAULT;
|
|
|
|
out:
|
|
|
|
kfree(buf);
|
|
|
|
kfree(file);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int get_disk_info(mddev_t * mddev, void __user * arg)
|
|
|
|
{
|
|
|
|
mdu_disk_info_t info;
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
|
|
|
if (copy_from_user(&info, arg, sizeof(info)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2008-07-11 19:02:21 +07:00
|
|
|
rdev = find_rdev_nr(mddev, info.number);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (rdev) {
|
|
|
|
info.major = MAJOR(rdev->bdev->bd_dev);
|
|
|
|
info.minor = MINOR(rdev->bdev->bd_dev);
|
|
|
|
info.raid_disk = rdev->raid_disk;
|
|
|
|
info.state = 0;
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
info.state |= (1<<MD_DISK_FAULTY);
|
2005-11-09 12:39:31 +07:00
|
|
|
else if (test_bit(In_sync, &rdev->flags)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
info.state |= (1<<MD_DISK_ACTIVE);
|
|
|
|
info.state |= (1<<MD_DISK_SYNC);
|
|
|
|
}
|
2005-09-10 06:23:45 +07:00
|
|
|
if (test_bit(WriteMostly, &rdev->flags))
|
|
|
|
info.state |= (1<<MD_DISK_WRITEMOSTLY);
|
2005-04-17 05:20:36 +07:00
|
|
|
} else {
|
|
|
|
info.major = info.minor = 0;
|
|
|
|
info.raid_disk = -1;
|
|
|
|
info.state = (1<<MD_DISK_REMOVED);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &info, sizeof(info)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
|
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
dev_t dev = MKDEV(info->major,info->minor);
|
|
|
|
|
|
|
|
if (info->major != MAJOR(dev) || info->minor != MINOR(dev))
|
|
|
|
return -EOVERFLOW;
|
|
|
|
|
|
|
|
if (!mddev->raid_disks) {
|
|
|
|
int err;
|
|
|
|
/* expecting a device which has a superblock */
|
|
|
|
rdev = md_import_device(dev, mddev->major_version, mddev->minor_version);
|
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: md_import_device returned %ld\n",
|
|
|
|
PTR_ERR(rdev));
|
|
|
|
return PTR_ERR(rdev);
|
|
|
|
}
|
|
|
|
if (!list_empty(&mddev->disks)) {
|
|
|
|
mdk_rdev_t *rdev0 = list_entry(mddev->disks.next,
|
|
|
|
mdk_rdev_t, same_set);
|
|
|
|
int err = super_types[mddev->major_version]
|
|
|
|
.load_super(rdev, rdev0, mddev->minor_version);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: %s has different UUID to %s\n",
|
|
|
|
bdevname(rdev->bdev,b),
|
|
|
|
bdevname(rdev0->bdev,b2));
|
|
|
|
export_rdev(rdev);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = bind_rdev_to_array(rdev, mddev);
|
|
|
|
if (err)
|
|
|
|
export_rdev(rdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_new_disk can be used once the array is assembled
|
|
|
|
* to add "hot spares". They must already have a superblock
|
|
|
|
* written
|
|
|
|
*/
|
|
|
|
if (mddev->pers) {
|
|
|
|
int err;
|
|
|
|
if (!mddev->pers->hot_add_disk) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"%s: personality does not support diskops!\n",
|
|
|
|
mdname(mddev));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2005-09-10 06:23:50 +07:00
|
|
|
if (mddev->persistent)
|
|
|
|
rdev = md_import_device(dev, mddev->major_version,
|
|
|
|
mddev->minor_version);
|
|
|
|
else
|
|
|
|
rdev = md_import_device(dev, -1, -1);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: md_import_device returned %ld\n",
|
|
|
|
PTR_ERR(rdev));
|
|
|
|
return PTR_ERR(rdev);
|
|
|
|
}
|
2005-06-22 07:17:25 +07:00
|
|
|
/* set save_raid_disk if appropriate */
|
|
|
|
if (!mddev->persistent) {
|
|
|
|
if (info->state & (1<<MD_DISK_SYNC) &&
|
|
|
|
info->raid_disk < mddev->raid_disks)
|
|
|
|
rdev->raid_disk = info->raid_disk;
|
|
|
|
else
|
|
|
|
rdev->raid_disk = -1;
|
|
|
|
} else
|
|
|
|
super_types[mddev->major_version].
|
|
|
|
validate_super(mddev, rdev);
|
|
|
|
rdev->saved_raid_disk = rdev->raid_disk;
|
|
|
|
|
2005-11-09 12:39:31 +07:00
|
|
|
clear_bit(In_sync, &rdev->flags); /* just to be sure */
|
2005-09-10 06:23:45 +07:00
|
|
|
if (info->state & (1<<MD_DISK_WRITEMOSTLY))
|
|
|
|
set_bit(WriteMostly, &rdev->flags);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->raid_disk = -1;
|
|
|
|
err = bind_rdev_to_array(rdev, mddev);
|
2006-06-26 14:27:41 +07:00
|
|
|
if (!err && !mddev->pers->hot_remove_disk) {
|
|
|
|
/* If there is hot_add_disk but no hot_remove_disk
|
|
|
|
* then added disks for geometry changes,
|
|
|
|
* and should be added immediately.
|
|
|
|
*/
|
|
|
|
super_types[mddev->major_version].
|
|
|
|
validate_super(mddev, rdev);
|
|
|
|
err = mddev->pers->hot_add_disk(mddev, rdev);
|
|
|
|
if (err)
|
|
|
|
unbind_rdev_from_array(rdev);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err)
|
|
|
|
export_rdev(rdev);
|
2008-06-28 05:31:44 +07:00
|
|
|
else
|
2008-10-21 09:25:28 +07:00
|
|
|
sysfs_notify_dirent(rdev->sysfs_state);
|
2005-06-22 07:17:10 +07:00
|
|
|
|
2006-12-10 17:20:52 +07:00
|
|
|
md_update_sb(mddev, 1);
|
2008-06-28 05:31:41 +07:00
|
|
|
if (mddev->degraded)
|
|
|
|
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
|
2005-06-22 07:17:10 +07:00
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
2005-08-23 03:11:08 +07:00
|
|
|
md_wakeup_thread(mddev->thread);
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise, add_new_disk is only allowed
|
|
|
|
* for major_version==0 superblocks
|
|
|
|
*/
|
|
|
|
if (mddev->major_version != 0) {
|
|
|
|
printk(KERN_WARNING "%s: ADD_NEW_DISK not supported\n",
|
|
|
|
mdname(mddev));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(info->state & (1<<MD_DISK_FAULTY))) {
|
|
|
|
int err;
|
2008-10-13 07:55:12 +07:00
|
|
|
rdev = md_import_device(dev, -1, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: error, md_import_device() returned %ld\n",
|
|
|
|
PTR_ERR(rdev));
|
|
|
|
return PTR_ERR(rdev);
|
|
|
|
}
|
|
|
|
rdev->desc_nr = info->number;
|
|
|
|
if (info->raid_disk < mddev->raid_disks)
|
|
|
|
rdev->raid_disk = info->raid_disk;
|
|
|
|
else
|
|
|
|
rdev->raid_disk = -1;
|
|
|
|
|
|
|
|
if (rdev->raid_disk < mddev->raid_disks)
|
2005-11-09 12:39:31 +07:00
|
|
|
if (info->state & (1<<MD_DISK_SYNC))
|
|
|
|
set_bit(In_sync, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 06:23:45 +07:00
|
|
|
if (info->state & (1<<MD_DISK_WRITEMOSTLY))
|
|
|
|
set_bit(WriteMostly, &rdev->flags);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!mddev->persistent) {
|
|
|
|
printk(KERN_INFO "md: nonpersistent superblock ...\n");
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = rdev->bdev->bd_inode->i_size / 512;
|
2005-04-17 05:20:36 +07:00
|
|
|
} else
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-06 15:20:55 +07:00
|
|
|
err = bind_rdev_to_array(rdev, mddev);
|
|
|
|
if (err) {
|
|
|
|
export_rdev(rdev);
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hot_remove_disk(mddev_t * mddev, dev_t dev)
|
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
|
|
|
rdev = find_rdev(mddev, dev);
|
|
|
|
if (!rdev)
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
if (rdev->raid_disk >= 0)
|
|
|
|
goto busy;
|
|
|
|
|
|
|
|
kick_rdev_from_array(rdev);
|
2006-10-03 15:15:46 +07:00
|
|
|
md_update_sb(mddev, 1);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
md_new_event(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
busy:
|
2008-04-22 05:42:58 +07:00
|
|
|
printk(KERN_WARNING "md: cannot remove active disk %s from %s ...\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
bdevname(rdev->bdev,b), mdname(mddev));
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hot_add_disk(mddev_t * mddev, dev_t dev)
|
|
|
|
{
|
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
int err;
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
|
|
|
if (!mddev->pers)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (mddev->major_version != 0) {
|
|
|
|
printk(KERN_WARNING "%s: HOT_ADD may only be used with"
|
|
|
|
" version-0 superblocks.\n",
|
|
|
|
mdname(mddev));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (!mddev->pers->hot_add_disk) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"%s: personality does not support diskops!\n",
|
|
|
|
mdname(mddev));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-10-13 07:55:12 +07:00
|
|
|
rdev = md_import_device(dev, -1, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: error, md_import_device() returned %ld\n",
|
|
|
|
PTR_ERR(rdev));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mddev->persistent)
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->sb_start = rdev->bdev->bd_inode->i_size / 512;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-11 19:02:23 +07:00
|
|
|
rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: can not hot-add faulty %s disk to %s!\n",
|
|
|
|
bdevname(rdev->bdev,b), mdname(mddev));
|
|
|
|
err = -EINVAL;
|
|
|
|
goto abort_export;
|
|
|
|
}
|
2005-11-09 12:39:31 +07:00
|
|
|
clear_bit(In_sync, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->desc_nr = -1;
|
2006-10-06 14:44:04 +07:00
|
|
|
rdev->saved_raid_disk = -1;
|
2006-01-06 15:20:55 +07:00
|
|
|
err = bind_rdev_to_array(rdev, mddev);
|
|
|
|
if (err)
|
|
|
|
goto abort_export;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The rest should better be atomic, we can have disk failures
|
|
|
|
* noticed in interrupt contexts ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
rdev->raid_disk = -1;
|
|
|
|
|
2006-10-03 15:15:46 +07:00
|
|
|
md_update_sb(mddev, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Kick recovery, maybe this spare has to be added to the
|
|
|
|
* array immediately.
|
|
|
|
*/
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
md_wakeup_thread(mddev->thread);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
md_new_event(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
abort_export:
|
|
|
|
export_rdev(rdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
static int set_bitmap_file(mddev_t *mddev, int fd)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2005-09-10 06:23:45 +07:00
|
|
|
if (mddev->pers) {
|
|
|
|
if (!mddev->pers->quiesce)
|
|
|
|
return -EBUSY;
|
|
|
|
if (mddev->recovery || mddev->sync_thread)
|
|
|
|
return -EBUSY;
|
|
|
|
/* we should be able to change the bitmap.. */
|
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
|
2005-09-10 06:23:45 +07:00
|
|
|
if (fd >= 0) {
|
|
|
|
if (mddev->bitmap)
|
|
|
|
return -EEXIST; /* cannot add when bitmap is present */
|
|
|
|
mddev->bitmap_file = fget(fd);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2005-09-10 06:23:45 +07:00
|
|
|
if (mddev->bitmap_file == NULL) {
|
|
|
|
printk(KERN_ERR "%s: error: failed to get bitmap file\n",
|
|
|
|
mdname(mddev));
|
|
|
|
return -EBADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = deny_bitmap_write_access(mddev->bitmap_file);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: error: bitmap file is already in use\n",
|
|
|
|
mdname(mddev));
|
|
|
|
fput(mddev->bitmap_file);
|
|
|
|
mddev->bitmap_file = NULL;
|
|
|
|
return err;
|
|
|
|
}
|
2005-06-22 07:17:27 +07:00
|
|
|
mddev->bitmap_offset = 0; /* file overrides offset */
|
2005-09-10 06:23:45 +07:00
|
|
|
} else if (mddev->bitmap == NULL)
|
|
|
|
return -ENOENT; /* cannot remove what isn't there */
|
|
|
|
err = 0;
|
|
|
|
if (mddev->pers) {
|
|
|
|
mddev->pers->quiesce(mddev, 1);
|
|
|
|
if (fd >= 0)
|
|
|
|
err = bitmap_create(mddev);
|
2006-06-26 14:27:43 +07:00
|
|
|
if (fd < 0 || err) {
|
2005-09-10 06:23:45 +07:00
|
|
|
bitmap_destroy(mddev);
|
2006-06-26 14:27:43 +07:00
|
|
|
fd = -1; /* make sure to put the file */
|
|
|
|
}
|
2005-09-10 06:23:45 +07:00
|
|
|
mddev->pers->quiesce(mddev, 0);
|
2006-06-26 14:27:43 +07:00
|
|
|
}
|
|
|
|
if (fd < 0) {
|
2006-06-26 14:27:47 +07:00
|
|
|
if (mddev->bitmap_file) {
|
|
|
|
restore_bitmap_write_access(mddev->bitmap_file);
|
2005-09-10 06:23:45 +07:00
|
|
|
fput(mddev->bitmap_file);
|
2006-06-26 14:27:47 +07:00
|
|
|
}
|
2005-09-10 06:23:45 +07:00
|
|
|
mddev->bitmap_file = NULL;
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* set_array_info is used two different ways
|
|
|
|
* The original usage is when creating a new array.
|
|
|
|
* In this usage, raid_disks is > 0 and it together with
|
|
|
|
* level, size, not_persistent,layout,chunksize determine the
|
|
|
|
* shape of the array.
|
|
|
|
* This will always create an array with a type-0.90.0 superblock.
|
|
|
|
* The newer usage is when assembling an array.
|
|
|
|
* In this case raid_disks will be 0, and the major_version field is
|
|
|
|
* use to determine which style super-blocks are to be found on the devices.
|
|
|
|
* The minor and patch _version numbers are also kept incase the
|
|
|
|
* super_block handler wishes to interpret them.
|
|
|
|
*/
|
|
|
|
static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (info->raid_disks == 0) {
|
|
|
|
/* just setting version number for superblock loading */
|
|
|
|
if (info->major_version < 0 ||
|
2007-05-09 16:35:34 +07:00
|
|
|
info->major_version >= ARRAY_SIZE(super_types) ||
|
2005-04-17 05:20:36 +07:00
|
|
|
super_types[info->major_version].name == NULL) {
|
|
|
|
/* maybe try to auto-load a module? */
|
|
|
|
printk(KERN_INFO
|
|
|
|
"md: superblock version %d not known\n",
|
|
|
|
info->major_version);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
mddev->major_version = info->major_version;
|
|
|
|
mddev->minor_version = info->minor_version;
|
|
|
|
mddev->patch_version = info->patch_version;
|
2006-12-22 16:11:41 +07:00
|
|
|
mddev->persistent = !info->not_persistent;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
mddev->major_version = MD_MAJOR_VERSION;
|
|
|
|
mddev->minor_version = MD_MINOR_VERSION;
|
|
|
|
mddev->patch_version = MD_PATCHLEVEL_VERSION;
|
|
|
|
mddev->ctime = get_seconds();
|
|
|
|
|
|
|
|
mddev->level = info->level;
|
2006-01-17 13:14:57 +07:00
|
|
|
mddev->clevel[0] = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->size = info->size;
|
|
|
|
mddev->raid_disks = info->raid_disks;
|
|
|
|
/* don't set md_minor, it is determined by which /dev/md* was
|
|
|
|
* openned
|
|
|
|
*/
|
|
|
|
if (info->state & (1<<MD_SB_CLEAN))
|
|
|
|
mddev->recovery_cp = MaxSector;
|
|
|
|
else
|
|
|
|
mddev->recovery_cp = 0;
|
|
|
|
mddev->persistent = ! info->not_persistent;
|
2008-02-06 16:39:51 +07:00
|
|
|
mddev->external = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
mddev->layout = info->layout;
|
|
|
|
mddev->chunk_size = info->chunk_size;
|
|
|
|
|
|
|
|
mddev->max_disks = MD_SB_DISKS;
|
|
|
|
|
2008-02-06 16:39:51 +07:00
|
|
|
if (mddev->persistent)
|
|
|
|
mddev->flags = 0;
|
2006-10-03 15:15:46 +07:00
|
|
|
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-29 04:44:12 +07:00
|
|
|
mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
|
|
|
|
mddev->bitmap_offset = 0;
|
|
|
|
|
2006-03-27 16:18:11 +07:00
|
|
|
mddev->reshape_position = MaxSector;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Generate a 128 bit UUID
|
|
|
|
*/
|
|
|
|
get_random_bytes(mddev->uuid, 16);
|
|
|
|
|
2006-03-27 16:18:11 +07:00
|
|
|
mddev->new_level = mddev->level;
|
|
|
|
mddev->new_chunk = mddev->chunk_size;
|
|
|
|
mddev->new_layout = mddev->layout;
|
|
|
|
mddev->delta_disks = 0;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-11 19:02:22 +07:00
|
|
|
static int update_size(mddev_t *mddev, sector_t num_sectors)
|
2006-01-06 15:20:49 +07:00
|
|
|
{
|
2009-01-09 04:31:08 +07:00
|
|
|
mdk_rdev_t *rdev;
|
2006-01-06 15:20:49 +07:00
|
|
|
int rv;
|
2008-07-11 19:02:22 +07:00
|
|
|
int fit = (num_sectors == 0);
|
2006-01-06 15:20:49 +07:00
|
|
|
|
|
|
|
if (mddev->pers->resize == NULL)
|
|
|
|
return -EINVAL;
|
2008-07-11 19:02:22 +07:00
|
|
|
/* The "num_sectors" is the number of sectors of each device that
|
|
|
|
* is used. This can only make sense for arrays with redundancy.
|
|
|
|
* linear and raid0 always use whatever space is available. We can only
|
|
|
|
* consider changing this number if no resync or reconstruction is
|
|
|
|
* happening, and if the new size is acceptable. It must fit before the
|
2008-07-11 19:02:23 +07:00
|
|
|
* sb_start or, if that is <data_offset, it must fit before the size
|
2008-07-11 19:02:22 +07:00
|
|
|
* of each device. If num_sectors is zero, we find the largest size
|
|
|
|
* that fits.
|
|
|
|
|
2006-01-06 15:20:49 +07:00
|
|
|
*/
|
|
|
|
if (mddev->sync_thread)
|
|
|
|
return -EBUSY;
|
2008-08-05 12:54:13 +07:00
|
|
|
if (mddev->bitmap)
|
|
|
|
/* Sorry, cannot grow a bitmap yet, just remove it,
|
|
|
|
* grow, and re-add.
|
|
|
|
*/
|
|
|
|
return -EBUSY;
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2006-01-06 15:20:49 +07:00
|
|
|
sector_t avail;
|
2006-10-29 00:38:30 +07:00
|
|
|
avail = rdev->size * 2;
|
|
|
|
|
2008-07-11 19:02:22 +07:00
|
|
|
if (fit && (num_sectors == 0 || num_sectors > avail))
|
|
|
|
num_sectors = avail;
|
|
|
|
if (avail < num_sectors)
|
2006-01-06 15:20:49 +07:00
|
|
|
return -ENOSPC;
|
|
|
|
}
|
2008-07-11 19:02:22 +07:00
|
|
|
rv = mddev->pers->resize(mddev, num_sectors);
|
2006-01-06 15:20:49 +07:00
|
|
|
if (!rv) {
|
|
|
|
struct block_device *bdev;
|
|
|
|
|
|
|
|
bdev = bdget_disk(mddev->gendisk, 0);
|
|
|
|
if (bdev) {
|
2006-01-10 06:59:24 +07:00
|
|
|
mutex_lock(&bdev->bd_inode->i_mutex);
|
2008-07-21 14:05:22 +07:00
|
|
|
i_size_write(bdev->bd_inode,
|
|
|
|
(loff_t)mddev->array_sectors << 9);
|
2006-01-10 06:59:24 +07:00
|
|
|
mutex_unlock(&bdev->bd_inode->i_mutex);
|
2006-01-06 15:20:49 +07:00
|
|
|
bdput(bdev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:54 +07:00
|
|
|
static int update_raid_disks(mddev_t *mddev, int raid_disks)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
/* change the number of raid disks */
|
2006-03-27 16:18:13 +07:00
|
|
|
if (mddev->pers->check_reshape == NULL)
|
2006-01-06 15:20:54 +07:00
|
|
|
return -EINVAL;
|
|
|
|
if (raid_disks <= 0 ||
|
|
|
|
raid_disks >= mddev->max_disks)
|
|
|
|
return -EINVAL;
|
2006-03-27 16:18:13 +07:00
|
|
|
if (mddev->sync_thread || mddev->reshape_position != MaxSector)
|
2006-01-06 15:20:54 +07:00
|
|
|
return -EBUSY;
|
2006-03-27 16:18:13 +07:00
|
|
|
mddev->delta_disks = raid_disks - mddev->raid_disks;
|
|
|
|
|
|
|
|
rv = mddev->pers->check_reshape(mddev);
|
2006-01-06 15:20:54 +07:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* update_array_info is used to change the configuration of an
|
|
|
|
* on-line array.
|
|
|
|
* The version, ctime,level,size,raid_disks,not_persistent, layout,chunk_size
|
|
|
|
* fields in the info are checked against the array.
|
|
|
|
* Any differences that cannot be handled will cause an error.
|
|
|
|
* Normally, only one change can be managed at a time.
|
|
|
|
*/
|
|
|
|
static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
|
|
|
|
{
|
|
|
|
int rv = 0;
|
|
|
|
int cnt = 0;
|
2005-09-10 06:23:45 +07:00
|
|
|
int state = 0;
|
|
|
|
|
|
|
|
/* calculate expected state,ignoring low bits */
|
|
|
|
if (mddev->bitmap && mddev->bitmap_offset)
|
|
|
|
state |= (1 << MD_SB_BITMAP_PRESENT);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (mddev->major_version != info->major_version ||
|
|
|
|
mddev->minor_version != info->minor_version ||
|
|
|
|
/* mddev->patch_version != info->patch_version || */
|
|
|
|
mddev->ctime != info->ctime ||
|
|
|
|
mddev->level != info->level ||
|
|
|
|
/* mddev->layout != info->layout || */
|
|
|
|
!mddev->persistent != info->not_persistent||
|
2005-09-10 06:23:45 +07:00
|
|
|
mddev->chunk_size != info->chunk_size ||
|
|
|
|
/* ignore bottom 8 bits of state, and allow SB_BITMAP_PRESENT to change */
|
|
|
|
((state^info->state) & 0xfffffe00)
|
|
|
|
)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
/* Check there is only one change */
|
2006-02-03 18:03:40 +07:00
|
|
|
if (info->size >= 0 && mddev->size != info->size) cnt++;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->raid_disks != info->raid_disks) cnt++;
|
|
|
|
if (mddev->layout != info->layout) cnt++;
|
2005-09-10 06:23:45 +07:00
|
|
|
if ((state ^ info->state) & (1<<MD_SB_BITMAP_PRESENT)) cnt++;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (cnt == 0) return 0;
|
|
|
|
if (cnt > 1) return -EINVAL;
|
|
|
|
|
|
|
|
if (mddev->layout != info->layout) {
|
|
|
|
/* Change layout
|
|
|
|
* we don't need to do anything at the md level, the
|
|
|
|
* personality will take care of it all.
|
|
|
|
*/
|
|
|
|
if (mddev->pers->reconfig == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
else
|
|
|
|
return mddev->pers->reconfig(mddev, info->layout, -1);
|
|
|
|
}
|
2006-02-03 18:03:40 +07:00
|
|
|
if (info->size >= 0 && mddev->size != info->size)
|
2008-07-11 19:02:22 +07:00
|
|
|
rv = update_size(mddev, (sector_t)info->size * 2);
|
2006-01-06 15:20:49 +07:00
|
|
|
|
2006-01-06 15:20:54 +07:00
|
|
|
if (mddev->raid_disks != info->raid_disks)
|
|
|
|
rv = update_raid_disks(mddev, info->raid_disks);
|
|
|
|
|
2005-09-10 06:23:45 +07:00
|
|
|
if ((state ^ info->state) & (1<<MD_SB_BITMAP_PRESENT)) {
|
|
|
|
if (mddev->pers->quiesce == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
if (mddev->recovery || mddev->sync_thread)
|
|
|
|
return -EBUSY;
|
|
|
|
if (info->state & (1<<MD_SB_BITMAP_PRESENT)) {
|
|
|
|
/* add the bitmap */
|
|
|
|
if (mddev->bitmap)
|
|
|
|
return -EEXIST;
|
|
|
|
if (mddev->default_bitmap_offset == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
mddev->bitmap_offset = mddev->default_bitmap_offset;
|
|
|
|
mddev->pers->quiesce(mddev, 1);
|
|
|
|
rv = bitmap_create(mddev);
|
|
|
|
if (rv)
|
|
|
|
bitmap_destroy(mddev);
|
|
|
|
mddev->pers->quiesce(mddev, 0);
|
|
|
|
} else {
|
|
|
|
/* remove the bitmap */
|
|
|
|
if (!mddev->bitmap)
|
|
|
|
return -ENOENT;
|
|
|
|
if (mddev->bitmap->file)
|
|
|
|
return -EINVAL;
|
|
|
|
mddev->pers->quiesce(mddev, 1);
|
|
|
|
bitmap_destroy(mddev);
|
|
|
|
mddev->pers->quiesce(mddev, 0);
|
|
|
|
mddev->bitmap_offset = 0;
|
|
|
|
}
|
|
|
|
}
|
2006-10-03 15:15:46 +07:00
|
|
|
md_update_sb(mddev, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_disk_faulty(mddev_t *mddev, dev_t dev)
|
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
|
|
|
if (mddev->pers == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
rdev = find_rdev(mddev, dev);
|
|
|
|
if (!rdev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
md_error(mddev, rdev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-25 23:57:58 +07:00
|
|
|
/*
|
|
|
|
* We have a problem here : there is no easy way to give a CHS
|
|
|
|
* virtual geometry. We currently pretend that we have a 2 heads
|
|
|
|
* 4 sectors (with a BIG number of cylinders...). This drives
|
|
|
|
* dosfs just mad... ;-)
|
|
|
|
*/
|
2006-01-08 16:02:50 +07:00
|
|
|
static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = bdev->bd_disk->private_data;
|
|
|
|
|
|
|
|
geo->heads = 2;
|
|
|
|
geo->sectors = 4;
|
|
|
|
geo->cylinders = get_capacity(mddev->gendisk) / 8;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-03-02 22:31:15 +07:00
|
|
|
static int md_ioctl(struct block_device *bdev, fmode_t mode,
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
mddev_t *mddev = NULL;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Commands dealing with the RAID driver but not any
|
|
|
|
* particular array:
|
|
|
|
*/
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case RAID_VERSION:
|
|
|
|
err = get_version(argp);
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
case PRINT_RAID_DEBUG:
|
|
|
|
err = 0;
|
|
|
|
md_print_devices();
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
#ifndef MODULE
|
|
|
|
case RAID_AUTORUN:
|
|
|
|
err = 0;
|
|
|
|
autostart_arrays(arg);
|
|
|
|
goto done;
|
|
|
|
#endif
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Commands creating/starting a new array:
|
|
|
|
*/
|
|
|
|
|
2008-03-02 22:31:15 +07:00
|
|
|
mddev = bdev->bd_disk->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!mddev) {
|
|
|
|
BUG();
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = mddev_lock(mddev);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_INFO
|
|
|
|
"md: ioctl lock interrupted, reason %d, cmd %d\n",
|
|
|
|
err, cmd);
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case SET_ARRAY_INFO:
|
|
|
|
{
|
|
|
|
mdu_array_info_t info;
|
|
|
|
if (!arg)
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
else if (copy_from_user(&info, argp, sizeof(info))) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
|
|
|
if (mddev->pers) {
|
|
|
|
err = update_array_info(mddev, &info);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_WARNING "md: couldn't update"
|
|
|
|
" array info. %d\n", err);
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
|
|
|
goto done_unlock;
|
|
|
|
}
|
|
|
|
if (!list_empty(&mddev->disks)) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: array %s already has disks!\n",
|
|
|
|
mdname(mddev));
|
|
|
|
err = -EBUSY;
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
|
|
|
if (mddev->raid_disks) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: array %s already initialised!\n",
|
|
|
|
mdname(mddev));
|
|
|
|
err = -EBUSY;
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
|
|
|
err = set_array_info(mddev, &info);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_WARNING "md: couldn't set"
|
|
|
|
" array info. %d\n", err);
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Commands querying/configuring an existing array:
|
|
|
|
*/
|
2005-06-22 07:17:14 +07:00
|
|
|
/* if we are not initialised yet, only ADD_NEW_DISK, STOP_ARRAY,
|
2006-12-22 16:11:41 +07:00
|
|
|
* RUN_ARRAY, and GET_ and SET_BITMAP_FILE are allowed */
|
2008-02-06 16:39:55 +07:00
|
|
|
if ((!mddev->raid_disks && !mddev->external)
|
|
|
|
&& cmd != ADD_NEW_DISK && cmd != STOP_ARRAY
|
|
|
|
&& cmd != RUN_ARRAY && cmd != SET_BITMAP_FILE
|
|
|
|
&& cmd != GET_BITMAP_FILE) {
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -ENODEV;
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Commands even a read-only array can execute:
|
|
|
|
*/
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case GET_ARRAY_INFO:
|
|
|
|
err = get_array_info(mddev, argp);
|
|
|
|
goto done_unlock;
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
case GET_BITMAP_FILE:
|
2005-09-10 02:36:43 +07:00
|
|
|
err = get_bitmap_file(mddev, argp);
|
2005-06-22 07:17:14 +07:00
|
|
|
goto done_unlock;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case GET_DISK_INFO:
|
|
|
|
err = get_disk_info(mddev, argp);
|
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
case RESTART_ARRAY_RW:
|
|
|
|
err = restart_array(mddev);
|
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
case STOP_ARRAY:
|
2008-10-13 07:55:12 +07:00
|
|
|
err = do_md_stop(mddev, 0, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
case STOP_ARRAY_RO:
|
2008-10-13 07:55:12 +07:00
|
|
|
err = do_md_stop(mddev, 1, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The remaining ioctls are changing the state of the
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
* superblock, so we do not allow them on read-only arrays.
|
|
|
|
* However non-MD ioctls (e.g. get-size) will still come through
|
|
|
|
* here and hit the 'default' below, so only disallow
|
|
|
|
* 'md' ioctls, and switch to rw mode if started auto-readonly.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2008-04-26 00:06:35 +07:00
|
|
|
if (_IOC_TYPE(cmd) == MD_MAJOR && mddev->ro && mddev->pers) {
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
if (mddev->ro == 2) {
|
|
|
|
mddev->ro = 0;
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2008-06-28 05:31:36 +07:00
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
md_wakeup_thread(mddev->thread);
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
} else {
|
|
|
|
err = -EROFS;
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case ADD_NEW_DISK:
|
|
|
|
{
|
|
|
|
mdu_disk_info_t info;
|
|
|
|
if (copy_from_user(&info, argp, sizeof(info)))
|
|
|
|
err = -EFAULT;
|
|
|
|
else
|
|
|
|
err = add_new_disk(mddev, &info);
|
|
|
|
goto done_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
case HOT_REMOVE_DISK:
|
|
|
|
err = hot_remove_disk(mddev, new_decode_dev(arg));
|
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
case HOT_ADD_DISK:
|
|
|
|
err = hot_add_disk(mddev, new_decode_dev(arg));
|
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
case SET_DISK_FAULTY:
|
|
|
|
err = set_disk_faulty(mddev, new_decode_dev(arg));
|
|
|
|
goto done_unlock;
|
|
|
|
|
|
|
|
case RUN_ARRAY:
|
2008-10-13 07:55:12 +07:00
|
|
|
err = do_md_run(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto done_unlock;
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
case SET_BITMAP_FILE:
|
|
|
|
err = set_bitmap_file(mddev, (int)arg);
|
|
|
|
goto done_unlock;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
|
|
|
err = -EINVAL;
|
|
|
|
goto abort_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
done_unlock:
|
|
|
|
abort_unlock:
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
if (mddev->hold_active == UNTIL_IOCTL &&
|
|
|
|
err != -EINVAL)
|
|
|
|
mddev->hold_active = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_unlock(mddev);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
done:
|
|
|
|
if (err)
|
|
|
|
MD_BUG();
|
|
|
|
abort:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-03-02 22:31:15 +07:00
|
|
|
static int md_open(struct block_device *bdev, fmode_t mode)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Succeed if we can lock the mddev, which confirms that
|
|
|
|
* it isn't being stopped right now.
|
|
|
|
*/
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
mddev_t *mddev = mddev_find(bdev->bd_dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
if (mddev->gendisk != bdev->bd_disk) {
|
|
|
|
/* we are racing with mddev_put which is discarding this
|
|
|
|
* bd_disk.
|
|
|
|
*/
|
|
|
|
mddev_put(mddev);
|
|
|
|
/* Wait until bdev->bd_disk is definitely gone */
|
|
|
|
flush_scheduled_work();
|
|
|
|
/* Then retry the open from the top */
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
}
|
|
|
|
BUG_ON(mddev != bdev->bd_disk->private_data);
|
|
|
|
|
2006-12-08 17:36:17 +07:00
|
|
|
if ((err = mutex_lock_interruptible_nested(&mddev->reconfig_mutex, 1)))
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = 0;
|
2008-07-21 14:05:25 +07:00
|
|
|
atomic_inc(&mddev->openers);
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_unlock(mddev);
|
|
|
|
|
2008-03-02 22:31:15 +07:00
|
|
|
check_disk_change(bdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-03-02 22:31:15 +07:00
|
|
|
static int md_release(struct gendisk *disk, fmode_t mode)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-03-02 22:31:15 +07:00
|
|
|
mddev_t *mddev = disk->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-10-04 04:33:23 +07:00
|
|
|
BUG_ON(!mddev);
|
2008-07-21 14:05:25 +07:00
|
|
|
atomic_dec(&mddev->openers);
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_put(mddev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-05-10 08:51:36 +07:00
|
|
|
static int md_media_changed(struct gendisk *disk)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = disk->private_data;
|
|
|
|
|
|
|
|
return mddev->changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int md_revalidate(struct gendisk *disk)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = disk->private_data;
|
|
|
|
|
|
|
|
mddev->changed = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
static struct block_device_operations md_fops =
|
|
|
|
{
|
|
|
|
.owner = THIS_MODULE,
|
2008-03-02 22:31:15 +07:00
|
|
|
.open = md_open,
|
|
|
|
.release = md_release,
|
|
|
|
.locked_ioctl = md_ioctl,
|
2006-01-08 16:02:50 +07:00
|
|
|
.getgeo = md_getgeo,
|
2007-05-10 08:51:36 +07:00
|
|
|
.media_changed = md_media_changed,
|
|
|
|
.revalidate_disk= md_revalidate,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2005-05-06 06:16:09 +07:00
|
|
|
static int md_thread(void * arg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
mdk_thread_t *thread = arg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* md_thread is a 'system-thread', it's priority should be very
|
|
|
|
* high. We avoid resource deadlocks individually in each
|
|
|
|
* raid personality. (RAID5 does preallocation) We also use RR and
|
|
|
|
* the very same RT priority as kswapd, thus we will never get
|
|
|
|
* into a priority inversion deadlock.
|
|
|
|
*
|
|
|
|
* we definitely have to have equal or higher priority than
|
|
|
|
* bdflush, otherwise bdflush will deadlock if there are too
|
|
|
|
* many dirty RAID5 blocks.
|
|
|
|
*/
|
|
|
|
|
2005-10-20 11:23:47 +07:00
|
|
|
allow_signal(SIGKILL);
|
2005-09-10 06:23:56 +07:00
|
|
|
while (!kthread_should_stop()) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-15 15:09:12 +07:00
|
|
|
/* We need to wait INTERRUPTIBLE so that
|
|
|
|
* we don't add to the load-average.
|
|
|
|
* That means we need to be sure no signals are
|
|
|
|
* pending
|
|
|
|
*/
|
|
|
|
if (signal_pending(current))
|
|
|
|
flush_signals(current);
|
|
|
|
|
|
|
|
wait_event_interruptible_timeout
|
|
|
|
(thread->wqueue,
|
|
|
|
test_bit(THREAD_WAKEUP, &thread->flags)
|
|
|
|
|| kthread_should_stop(),
|
|
|
|
thread->timeout);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
clear_bit(THREAD_WAKEUP, &thread->flags);
|
|
|
|
|
2005-11-09 12:39:43 +07:00
|
|
|
thread->run(thread->mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-09-10 06:23:56 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void md_wakeup_thread(mdk_thread_t *thread)
|
|
|
|
{
|
|
|
|
if (thread) {
|
|
|
|
dprintk("md: waking up MD thread %s.\n", thread->tsk->comm);
|
|
|
|
set_bit(THREAD_WAKEUP, &thread->flags);
|
|
|
|
wake_up(&thread->wqueue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
mdk_thread_t *thread;
|
|
|
|
|
2006-01-06 15:20:32 +07:00
|
|
|
thread = kzalloc(sizeof(mdk_thread_t), GFP_KERNEL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!thread)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
init_waitqueue_head(&thread->wqueue);
|
|
|
|
|
|
|
|
thread->run = run;
|
|
|
|
thread->mddev = mddev;
|
2005-06-22 07:17:14 +07:00
|
|
|
thread->timeout = MAX_SCHEDULE_TIMEOUT;
|
2005-10-20 11:23:47 +07:00
|
|
|
thread->tsk = kthread_run(md_thread, thread, name, mdname(thread->mddev));
|
2005-09-10 06:23:56 +07:00
|
|
|
if (IS_ERR(thread->tsk)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
kfree(thread);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
void md_unregister_thread(mdk_thread_t *thread)
|
|
|
|
{
|
2007-10-19 13:40:40 +07:00
|
|
|
dprintk("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk));
|
2005-09-10 06:23:56 +07:00
|
|
|
|
|
|
|
kthread_stop(thread->tsk);
|
2005-04-17 05:20:36 +07:00
|
|
|
kfree(thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
|
|
|
|
{
|
|
|
|
if (!mddev) {
|
|
|
|
MD_BUG();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-11-09 12:39:31 +07:00
|
|
|
if (!rdev || test_bit(Faulty, &rdev->flags))
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2008-04-30 14:52:32 +07:00
|
|
|
|
|
|
|
if (mddev->external)
|
|
|
|
set_bit(Blocked, &rdev->flags);
|
2005-06-22 07:17:14 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
dprintk("md_error dev:%s, rdev:(%d:%d), (caller: %p,%p,%p,%p).\n",
|
|
|
|
mdname(mddev),
|
|
|
|
MAJOR(rdev->bdev->bd_dev), MINOR(rdev->bdev->bd_dev),
|
|
|
|
__builtin_return_address(0),__builtin_return_address(1),
|
|
|
|
__builtin_return_address(2),__builtin_return_address(3));
|
2005-06-22 07:17:14 +07:00
|
|
|
*/
|
2006-07-10 18:44:20 +07:00
|
|
|
if (!mddev->pers)
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!mddev->pers->error_handler)
|
|
|
|
return;
|
|
|
|
mddev->pers->error_handler(mddev,rdev);
|
2008-06-28 05:31:41 +07:00
|
|
|
if (mddev->degraded)
|
|
|
|
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
|
2008-06-28 05:31:44 +07:00
|
|
|
set_bit(StateChanged, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
md_wakeup_thread(mddev->thread);
|
2006-05-31 11:27:13 +07:00
|
|
|
md_new_event_inintr(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* seq_file implementation /proc/mdstat */
|
|
|
|
|
|
|
|
static void status_unused(struct seq_file *seq)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
|
|
|
seq_printf(seq, "unused devices: ");
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &pending_raid_disks, same_set) {
|
2005-04-17 05:20:36 +07:00
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
i++;
|
|
|
|
seq_printf(seq, "%s ",
|
|
|
|
bdevname(rdev->bdev,b));
|
|
|
|
}
|
|
|
|
if (!i)
|
|
|
|
seq_printf(seq, "<none>");
|
|
|
|
|
|
|
|
seq_printf(seq, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void status_resync(struct seq_file *seq, mddev_t * mddev)
|
|
|
|
{
|
2006-03-27 16:18:04 +07:00
|
|
|
sector_t max_blocks, resync, res;
|
|
|
|
unsigned long dt, db, rt;
|
|
|
|
int scale;
|
|
|
|
unsigned int per_milli;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2;
|
|
|
|
|
|
|
|
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
|
|
|
|
max_blocks = mddev->resync_max_sectors >> 1;
|
|
|
|
else
|
|
|
|
max_blocks = mddev->size;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Should not happen.
|
|
|
|
*/
|
|
|
|
if (!max_blocks) {
|
|
|
|
MD_BUG();
|
|
|
|
return;
|
|
|
|
}
|
2006-03-27 16:18:04 +07:00
|
|
|
/* Pick 'scale' such that (resync>>scale)*1000 will fit
|
|
|
|
* in a sector_t, and (max_blocks>>scale) will fit in a
|
|
|
|
* u32, as those are the requirements for sector_div.
|
|
|
|
* Thus 'scale' must be at least 10
|
|
|
|
*/
|
|
|
|
scale = 10;
|
|
|
|
if (sizeof(sector_t) > sizeof(unsigned long)) {
|
|
|
|
while ( max_blocks/2 > (1ULL<<(scale+32)))
|
|
|
|
scale++;
|
|
|
|
}
|
|
|
|
res = (resync>>scale)*1000;
|
|
|
|
sector_div(res, (u32)((max_blocks>>scale)+1));
|
|
|
|
|
|
|
|
per_milli = res;
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-03-27 16:18:04 +07:00
|
|
|
int i, x = per_milli/50, y = 20-x;
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(seq, "[");
|
|
|
|
for (i = 0; i < x; i++)
|
|
|
|
seq_printf(seq, "=");
|
|
|
|
seq_printf(seq, ">");
|
|
|
|
for (i = 0; i < y; i++)
|
|
|
|
seq_printf(seq, ".");
|
|
|
|
seq_printf(seq, "] ");
|
|
|
|
}
|
2006-03-27 16:18:04 +07:00
|
|
|
seq_printf(seq, " %s =%3u.%u%% (%llu/%llu)",
|
2006-03-27 16:18:09 +07:00
|
|
|
(test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)?
|
|
|
|
"reshape" :
|
2006-10-03 15:15:57 +07:00
|
|
|
(test_bit(MD_RECOVERY_CHECK, &mddev->recovery)?
|
|
|
|
"check" :
|
|
|
|
(test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ?
|
|
|
|
"resync" : "recovery"))),
|
|
|
|
per_milli/10, per_milli % 10,
|
2006-03-27 16:18:04 +07:00
|
|
|
(unsigned long long) resync,
|
|
|
|
(unsigned long long) max_blocks);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We do not want to overflow, so the order of operands and
|
|
|
|
* the * 100 / 100 trick are important. We do a +1 to be
|
|
|
|
* safe against division by zero. We only estimate anyway.
|
|
|
|
*
|
|
|
|
* dt: time from mark until now
|
|
|
|
* db: blocks written from mark until now
|
|
|
|
* rt: remaining time
|
|
|
|
*/
|
|
|
|
dt = ((jiffies - mddev->resync_mark) / HZ);
|
|
|
|
if (!dt) dt++;
|
2006-07-10 18:44:16 +07:00
|
|
|
db = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active))
|
|
|
|
- mddev->resync_mark_cnt;
|
|
|
|
rt = (dt * ((unsigned long)(max_blocks-resync) / (db/2/100+1)))/100;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
|
|
|
|
|
2006-07-10 18:44:16 +07:00
|
|
|
seq_printf(seq, " speed=%ldK/sec", db/2/dt);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *md_seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
loff_t l = *pos;
|
|
|
|
mddev_t *mddev;
|
|
|
|
|
|
|
|
if (l >= 0x10000)
|
|
|
|
return NULL;
|
|
|
|
if (!l--)
|
|
|
|
/* header */
|
|
|
|
return (void*)1;
|
|
|
|
|
|
|
|
spin_lock(&all_mddevs_lock);
|
|
|
|
list_for_each(tmp,&all_mddevs)
|
|
|
|
if (!l--) {
|
|
|
|
mddev = list_entry(tmp, mddev_t, all_mddevs);
|
|
|
|
mddev_get(mddev);
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
return mddev;
|
|
|
|
}
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
if (!l--)
|
|
|
|
return (void*)2;/* tail */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
mddev_t *next_mddev, *mddev = v;
|
|
|
|
|
|
|
|
++*pos;
|
|
|
|
if (v == (void*)2)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
spin_lock(&all_mddevs_lock);
|
|
|
|
if (v == (void*)1)
|
|
|
|
tmp = all_mddevs.next;
|
|
|
|
else
|
|
|
|
tmp = mddev->all_mddevs.next;
|
|
|
|
if (tmp != &all_mddevs)
|
|
|
|
next_mddev = mddev_get(list_entry(tmp,mddev_t,all_mddevs));
|
|
|
|
else {
|
|
|
|
next_mddev = (void*)2;
|
|
|
|
*pos = 0x10000;
|
|
|
|
}
|
|
|
|
spin_unlock(&all_mddevs_lock);
|
|
|
|
|
|
|
|
if (v != (void*)1)
|
|
|
|
mddev_put(mddev);
|
|
|
|
return next_mddev;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void md_seq_stop(struct seq_file *seq, void *v)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = v;
|
|
|
|
|
|
|
|
if (mddev && v != (void*)1 && v != (void*)2)
|
|
|
|
mddev_put(mddev);
|
|
|
|
}
|
|
|
|
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
struct mdstat_info {
|
|
|
|
int event;
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int md_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
|
|
|
mddev_t *mddev = v;
|
|
|
|
sector_t size;
|
|
|
|
mdk_rdev_t *rdev;
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
struct mdstat_info *mi = seq->private;
|
2005-06-22 07:17:14 +07:00
|
|
|
struct bitmap *bitmap;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (v == (void*)1) {
|
2006-01-06 15:20:36 +07:00
|
|
|
struct mdk_personality *pers;
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(seq, "Personalities : ");
|
|
|
|
spin_lock(&pers_lock);
|
2006-01-06 15:20:36 +07:00
|
|
|
list_for_each_entry(pers, &pers_list, list)
|
|
|
|
seq_printf(seq, "[%s] ", pers->name);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
spin_unlock(&pers_lock);
|
|
|
|
seq_printf(seq, "\n");
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
mi->event = atomic_read(&md_event_count);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (v == (void*)2) {
|
|
|
|
status_unused(seq);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-04-20 16:43:23 +07:00
|
|
|
if (mddev_lock(mddev) < 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINTR;
|
2006-04-20 16:43:23 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) {
|
|
|
|
seq_printf(seq, "%s : %sactive", mdname(mddev),
|
|
|
|
mddev->pers ? "" : "in");
|
|
|
|
if (mddev->pers) {
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
if (mddev->ro==1)
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(seq, " (read-only)");
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
if (mddev->ro==2)
|
2008-03-11 01:43:47 +07:00
|
|
|
seq_printf(seq, " (auto-read-only)");
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(seq, " %s", mddev->pers->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
size = 0;
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
2005-04-17 05:20:36 +07:00
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
seq_printf(seq, " %s[%d]",
|
|
|
|
bdevname(rdev->bdev,b), rdev->desc_nr);
|
2005-09-10 06:23:45 +07:00
|
|
|
if (test_bit(WriteMostly, &rdev->flags))
|
|
|
|
seq_printf(seq, "(W)");
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(seq, "(F)");
|
|
|
|
continue;
|
2005-09-10 06:24:00 +07:00
|
|
|
} else if (rdev->raid_disk < 0)
|
|
|
|
seq_printf(seq, "(S)"); /* spare */
|
2005-04-17 05:20:36 +07:00
|
|
|
size += rdev->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!list_empty(&mddev->disks)) {
|
|
|
|
if (mddev->pers)
|
|
|
|
seq_printf(seq, "\n %llu blocks",
|
2008-07-21 14:05:22 +07:00
|
|
|
(unsigned long long)
|
|
|
|
mddev->array_sectors / 2);
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
|
|
|
seq_printf(seq, "\n %llu blocks",
|
2008-07-21 14:05:22 +07:00
|
|
|
(unsigned long long)size);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-09-10 06:24:00 +07:00
|
|
|
if (mddev->persistent) {
|
|
|
|
if (mddev->major_version != 0 ||
|
|
|
|
mddev->minor_version != 90) {
|
|
|
|
seq_printf(seq," super %d.%d",
|
|
|
|
mddev->major_version,
|
|
|
|
mddev->minor_version);
|
|
|
|
}
|
2008-02-06 16:39:51 +07:00
|
|
|
} else if (mddev->external)
|
|
|
|
seq_printf(seq, " super external:%s",
|
|
|
|
mddev->metadata_type);
|
|
|
|
else
|
2005-09-10 06:24:00 +07:00
|
|
|
seq_printf(seq, " super non-persistent");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (mddev->pers) {
|
2008-10-13 07:55:12 +07:00
|
|
|
mddev->pers->status(seq, mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
seq_printf(seq, "\n ");
|
2005-11-09 12:39:41 +07:00
|
|
|
if (mddev->pers->sync_request) {
|
|
|
|
if (mddev->curr_resync > 2) {
|
2008-10-13 07:55:12 +07:00
|
|
|
status_resync(seq, mddev);
|
2005-11-09 12:39:41 +07:00
|
|
|
seq_printf(seq, "\n ");
|
|
|
|
} else if (mddev->curr_resync == 1 || mddev->curr_resync == 2)
|
|
|
|
seq_printf(seq, "\tresync=DELAYED\n ");
|
|
|
|
else if (mddev->recovery_cp < MaxSector)
|
|
|
|
seq_printf(seq, "\tresync=PENDING\n ");
|
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
} else
|
|
|
|
seq_printf(seq, "\n ");
|
|
|
|
|
|
|
|
if ((bitmap = mddev->bitmap)) {
|
|
|
|
unsigned long chunk_kb;
|
|
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
chunk_kb = bitmap->chunksize >> 10;
|
|
|
|
seq_printf(seq, "bitmap: %lu/%lu pages [%luKB], "
|
|
|
|
"%lu%s chunk",
|
|
|
|
bitmap->pages - bitmap->missing_pages,
|
|
|
|
bitmap->pages,
|
|
|
|
(bitmap->pages - bitmap->missing_pages)
|
|
|
|
<< (PAGE_SHIFT - 10),
|
|
|
|
chunk_kb ? chunk_kb : bitmap->chunksize,
|
|
|
|
chunk_kb ? "KB" : "B");
|
2005-06-22 07:17:15 +07:00
|
|
|
if (bitmap->file) {
|
|
|
|
seq_printf(seq, ", file: ");
|
2008-02-15 10:38:43 +07:00
|
|
|
seq_path(seq, &bitmap->file->f_path, " \t\n");
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
2005-06-22 07:17:15 +07:00
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
seq_printf(seq, "\n");
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
seq_printf(seq, "\n");
|
|
|
|
}
|
|
|
|
mddev_unlock(mddev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct seq_operations md_seq_ops = {
|
|
|
|
.start = md_seq_start,
|
|
|
|
.next = md_seq_next,
|
|
|
|
.stop = md_seq_stop,
|
|
|
|
.show = md_seq_show,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int md_seq_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
int error;
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
struct mdstat_info *mi = kmalloc(sizeof(*mi), GFP_KERNEL);
|
|
|
|
if (mi == NULL)
|
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
error = seq_open(file, &md_seq_ops);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
if (error)
|
|
|
|
kfree(mi);
|
|
|
|
else {
|
|
|
|
struct seq_file *p = file->private_data;
|
|
|
|
p->private = mi;
|
|
|
|
mi->event = atomic_read(&md_event_count);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
static unsigned int mdstat_poll(struct file *filp, poll_table *wait)
|
|
|
|
{
|
|
|
|
struct seq_file *m = filp->private_data;
|
|
|
|
struct mdstat_info *mi = m->private;
|
|
|
|
int mask;
|
|
|
|
|
|
|
|
poll_wait(filp, &md_event_waiters, wait);
|
|
|
|
|
|
|
|
/* always allow read */
|
|
|
|
mask = POLLIN | POLLRDNORM;
|
|
|
|
|
|
|
|
if (mi->event != atomic_read(&md_event_count))
|
|
|
|
mask |= POLLERR | POLLPRI;
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2007-02-12 15:55:33 +07:00
|
|
|
static const struct file_operations md_seq_fops = {
|
2006-10-17 14:09:38 +07:00
|
|
|
.owner = THIS_MODULE,
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = md_seq_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
2007-05-09 16:35:35 +07:00
|
|
|
.release = seq_release_private,
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
.poll = mdstat_poll,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-01-06 15:20:36 +07:00
|
|
|
int register_md_personality(struct mdk_personality *p)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
spin_lock(&pers_lock);
|
2006-01-06 15:20:36 +07:00
|
|
|
list_add_tail(&p->list, &pers_list);
|
|
|
|
printk(KERN_INFO "md: %s personality registered for level %d\n", p->name, p->level);
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&pers_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:36 +07:00
|
|
|
int unregister_md_personality(struct mdk_personality *p)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-06 15:20:36 +07:00
|
|
|
printk(KERN_INFO "md: %s personality unregistered\n", p->name);
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_lock(&pers_lock);
|
2006-01-06 15:20:36 +07:00
|
|
|
list_del_init(&p->list);
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&pers_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-31 10:27:02 +07:00
|
|
|
static int is_mddev_idle(mddev_t *mddev, int init)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
mdk_rdev_t * rdev;
|
|
|
|
int idle;
|
2009-03-31 10:27:02 +07:00
|
|
|
int curr_events;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
idle = 1;
|
2008-07-21 14:05:25 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
rdev_for_each_rcu(rdev, mddev) {
|
2005-04-17 05:20:36 +07:00
|
|
|
struct gendisk *disk = rdev->bdev->bd_contains->bd_disk;
|
2009-03-31 10:27:02 +07:00
|
|
|
curr_events = (int)part_stat_read(&disk->part0, sectors[0]) +
|
|
|
|
(int)part_stat_read(&disk->part0, sectors[1]) -
|
|
|
|
atomic_read(&disk->sync_io);
|
2007-07-17 18:06:12 +07:00
|
|
|
/* sync IO will cause sync_io to increase before the disk_stats
|
|
|
|
* as sync_io is counted when a request starts, and
|
|
|
|
* disk_stats is counted when it completes.
|
|
|
|
* So resync activity will cause curr_events to be smaller than
|
|
|
|
* when there was no such activity.
|
|
|
|
* non-sync IO will cause disk_stat to increase without
|
|
|
|
* increasing sync_io so curr_events will (eventually)
|
|
|
|
* be larger than it was before. Once it becomes
|
|
|
|
* substantially larger, the test below will cause
|
|
|
|
* the array to appear non-idle, and resync will slow
|
|
|
|
* down.
|
|
|
|
* If there is a lot of outstanding resync activity when
|
|
|
|
* we set last_event to curr_events, then all that activity
|
|
|
|
* completing might cause the array to appear non-idle
|
|
|
|
* and resync will be slowed down even though there might
|
|
|
|
* not have been non-resync activity. This will only
|
|
|
|
* happen once though. 'last_events' will soon reflect
|
|
|
|
* the state where there is little or no outstanding
|
|
|
|
* resync requests, and further resync activity will
|
|
|
|
* always make curr_events less than last_events.
|
2005-11-18 16:11:01 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2009-03-31 10:27:02 +07:00
|
|
|
if (init || curr_events - rdev->last_events > 64) {
|
2005-04-17 05:20:36 +07:00
|
|
|
rdev->last_events = curr_events;
|
|
|
|
idle = 0;
|
|
|
|
}
|
|
|
|
}
|
2008-07-21 14:05:25 +07:00
|
|
|
rcu_read_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
return idle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void md_done_sync(mddev_t *mddev, int blocks, int ok)
|
|
|
|
{
|
|
|
|
/* another "blocks" (512byte) blocks have been synced */
|
|
|
|
atomic_sub(blocks, &mddev->recovery_active);
|
|
|
|
wake_up(&mddev->recovery_wait);
|
|
|
|
if (!ok) {
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
2005-04-17 05:20:36 +07:00
|
|
|
md_wakeup_thread(mddev->thread);
|
|
|
|
// stop recovery, signal do_sync ....
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-06-22 07:17:12 +07:00
|
|
|
/* md_write_start(mddev, bi)
|
|
|
|
* If we need to update some array metadata (e.g. 'active' flag
|
2005-06-22 07:17:26 +07:00
|
|
|
* in superblock) before writing, schedule a superblock update
|
|
|
|
* and wait for it to complete.
|
2005-06-22 07:17:12 +07:00
|
|
|
*/
|
2005-06-22 07:17:26 +07:00
|
|
|
void md_write_start(mddev_t *mddev, struct bio *bi)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-06-28 05:31:36 +07:00
|
|
|
int did_change = 0;
|
2005-06-22 07:17:12 +07:00
|
|
|
if (bio_data_dir(bi) != WRITE)
|
2005-06-22 07:17:26 +07:00
|
|
|
return;
|
2005-06-22 07:17:12 +07:00
|
|
|
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
BUG_ON(mddev->ro == 1);
|
|
|
|
if (mddev->ro == 2) {
|
|
|
|
/* need to switch to read/write */
|
|
|
|
mddev->ro = 0;
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
md_wakeup_thread(mddev->thread);
|
2008-03-05 05:29:32 +07:00
|
|
|
md_wakeup_thread(mddev->sync_thread);
|
2008-06-28 05:31:36 +07:00
|
|
|
did_change = 1;
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
}
|
2005-06-22 07:17:12 +07:00
|
|
|
atomic_inc(&mddev->writes_pending);
|
2008-04-30 14:52:30 +07:00
|
|
|
if (mddev->safemode == 1)
|
|
|
|
mddev->safemode = 0;
|
2005-06-22 07:17:12 +07:00
|
|
|
if (mddev->in_sync) {
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_lock_irq(&mddev->write_lock);
|
2005-06-22 07:17:26 +07:00
|
|
|
if (mddev->in_sync) {
|
|
|
|
mddev->in_sync = 0;
|
2006-10-03 15:15:46 +07:00
|
|
|
set_bit(MD_CHANGE_CLEAN, &mddev->flags);
|
2005-06-22 07:17:26 +07:00
|
|
|
md_wakeup_thread(mddev->thread);
|
2008-06-28 05:31:36 +07:00
|
|
|
did_change = 1;
|
2005-06-22 07:17:26 +07:00
|
|
|
}
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
2005-06-22 07:17:12 +07:00
|
|
|
}
|
2008-06-28 05:31:36 +07:00
|
|
|
if (did_change)
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2008-05-24 03:04:36 +07:00
|
|
|
wait_event(mddev->sb_wait,
|
|
|
|
!test_bit(MD_CHANGE_CLEAN, &mddev->flags) &&
|
|
|
|
!test_bit(MD_CHANGE_PENDING, &mddev->flags));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void md_write_end(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
if (atomic_dec_and_test(&mddev->writes_pending)) {
|
|
|
|
if (mddev->safemode == 2)
|
|
|
|
md_wakeup_thread(mddev->thread);
|
2006-06-26 14:27:37 +07:00
|
|
|
else if (mddev->safemode_delay)
|
2005-04-17 05:20:36 +07:00
|
|
|
mod_timer(&mddev->safemode_timer, jiffies + mddev->safemode_delay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-26 15:57:11 +07:00
|
|
|
/* md_allow_write(mddev)
|
|
|
|
* Calling this ensures that the array is marked 'active' so that writes
|
|
|
|
* may proceed without blocking. It is important to call this before
|
|
|
|
* attempting a GFP_KERNEL allocation while holding the mddev lock.
|
|
|
|
* Must be called with mddev_lock held.
|
2008-06-28 11:44:04 +07:00
|
|
|
*
|
|
|
|
* In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock
|
|
|
|
* is dropped, so return -EAGAIN after notifying userspace.
|
2007-01-26 15:57:11 +07:00
|
|
|
*/
|
2008-06-28 11:44:04 +07:00
|
|
|
int md_allow_write(mddev_t *mddev)
|
2007-01-26 15:57:11 +07:00
|
|
|
{
|
|
|
|
if (!mddev->pers)
|
2008-06-28 11:44:04 +07:00
|
|
|
return 0;
|
2007-01-26 15:57:11 +07:00
|
|
|
if (mddev->ro)
|
2008-06-28 11:44:04 +07:00
|
|
|
return 0;
|
2008-06-28 05:31:27 +07:00
|
|
|
if (!mddev->pers->sync_request)
|
2008-06-28 11:44:04 +07:00
|
|
|
return 0;
|
2007-01-26 15:57:11 +07:00
|
|
|
|
|
|
|
spin_lock_irq(&mddev->write_lock);
|
|
|
|
if (mddev->in_sync) {
|
|
|
|
mddev->in_sync = 0;
|
|
|
|
set_bit(MD_CHANGE_CLEAN, &mddev->flags);
|
|
|
|
if (mddev->safemode_delay &&
|
|
|
|
mddev->safemode == 0)
|
|
|
|
mddev->safemode = 1;
|
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
|
|
|
md_update_sb(mddev, 0);
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2007-01-26 15:57:11 +07:00
|
|
|
} else
|
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
2008-06-28 11:44:04 +07:00
|
|
|
|
|
|
|
if (test_bit(MD_CHANGE_CLEAN, &mddev->flags))
|
|
|
|
return -EAGAIN;
|
|
|
|
else
|
|
|
|
return 0;
|
2007-01-26 15:57:11 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(md_allow_write);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#define SYNC_MARKS 10
|
|
|
|
#define SYNC_MARK_STEP (3*HZ)
|
2006-03-27 16:18:10 +07:00
|
|
|
void md_do_sync(mddev_t *mddev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
mddev_t *mddev2;
|
|
|
|
unsigned int currspeed = 0,
|
|
|
|
window;
|
2005-06-22 07:17:13 +07:00
|
|
|
sector_t max_sectors,j, io_sectors;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long mark[SYNC_MARKS];
|
|
|
|
sector_t mark_cnt[SYNC_MARKS];
|
|
|
|
int last_mark,m;
|
|
|
|
struct list_head *tmp;
|
|
|
|
sector_t last_check;
|
2005-06-22 07:17:13 +07:00
|
|
|
int skipped = 0;
|
2006-06-26 14:27:40 +07:00
|
|
|
mdk_rdev_t *rdev;
|
2006-10-03 15:15:57 +07:00
|
|
|
char *desc;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* just incase thread restarts... */
|
|
|
|
if (test_bit(MD_RECOVERY_DONE, &mddev->recovery))
|
|
|
|
return;
|
2006-06-26 14:27:40 +07:00
|
|
|
if (mddev->ro) /* never try to sync a read-only array */
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-10-03 15:15:57 +07:00
|
|
|
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
|
|
|
|
if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
|
|
|
|
desc = "data-check";
|
|
|
|
else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
|
|
|
|
desc = "requested-resync";
|
|
|
|
else
|
|
|
|
desc = "resync";
|
|
|
|
} else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
|
|
|
|
desc = "reshape";
|
|
|
|
else
|
|
|
|
desc = "recovery";
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* we overload curr_resync somewhat here.
|
|
|
|
* 0 == not engaged in resync at all
|
|
|
|
* 2 == checking that there is no conflict with another sync
|
|
|
|
* 1 == like 2, but have yielded to allow conflicting resync to
|
|
|
|
* commense
|
|
|
|
* other == active in resync - this many blocks
|
|
|
|
*
|
|
|
|
* Before starting a resync we must have set curr_resync to
|
|
|
|
* 2, and then checked that every "conflicting" array has curr_resync
|
|
|
|
* less than ours. When we find one that is the same or higher
|
|
|
|
* we wait on resync_wait. To avoid deadlock, we reduce curr_resync
|
|
|
|
* to 1 if we choose to yield (based arbitrarily on address of mddev structure).
|
|
|
|
* This will mean we have to start checking from the beginning again.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
do {
|
|
|
|
mddev->curr_resync = 2;
|
|
|
|
|
|
|
|
try_again:
|
2005-11-09 12:39:43 +07:00
|
|
|
if (kthread_should_stop()) {
|
2005-10-20 11:23:47 +07:00
|
|
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto skip;
|
|
|
|
}
|
2008-02-06 16:39:58 +07:00
|
|
|
for_each_mddev(mddev2, tmp) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev2 == mddev)
|
|
|
|
continue;
|
2008-05-24 03:04:38 +07:00
|
|
|
if (!mddev->parallel_resync
|
|
|
|
&& mddev2->curr_resync
|
|
|
|
&& match_mddev_units(mddev, mddev2)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
DEFINE_WAIT(wq);
|
|
|
|
if (mddev < mddev2 && mddev->curr_resync == 2) {
|
|
|
|
/* arbitrarily yield */
|
|
|
|
mddev->curr_resync = 1;
|
|
|
|
wake_up(&resync_wait);
|
|
|
|
}
|
|
|
|
if (mddev > mddev2 && mddev->curr_resync == 1)
|
|
|
|
/* no need to wait here, we can wait the next
|
|
|
|
* time 'round when curr_resync == 2
|
|
|
|
*/
|
|
|
|
continue;
|
2008-09-19 08:49:54 +07:00
|
|
|
/* We need to wait 'interruptible' so as not to
|
|
|
|
* contribute to the load average, and not to
|
|
|
|
* be caught by 'softlockup'
|
|
|
|
*/
|
|
|
|
prepare_to_wait(&resync_wait, &wq, TASK_INTERRUPTIBLE);
|
2005-11-09 12:39:43 +07:00
|
|
|
if (!kthread_should_stop() &&
|
2005-10-26 15:58:58 +07:00
|
|
|
mddev2->curr_resync >= mddev->curr_resync) {
|
2006-10-03 15:15:57 +07:00
|
|
|
printk(KERN_INFO "md: delaying %s of %s"
|
|
|
|
" until %s has finished (they"
|
2005-04-17 05:20:36 +07:00
|
|
|
" share one or more physical units)\n",
|
2006-10-03 15:15:57 +07:00
|
|
|
desc, mdname(mddev), mdname(mddev2));
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_put(mddev2);
|
2008-09-19 08:49:54 +07:00
|
|
|
if (signal_pending(current))
|
|
|
|
flush_signals(current);
|
2005-04-17 05:20:36 +07:00
|
|
|
schedule();
|
|
|
|
finish_wait(&resync_wait, &wq);
|
|
|
|
goto try_again;
|
|
|
|
}
|
|
|
|
finish_wait(&resync_wait, &wq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (mddev->curr_resync < 2);
|
|
|
|
|
2006-06-26 14:27:40 +07:00
|
|
|
j = 0;
|
2005-11-09 12:39:26 +07:00
|
|
|
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* resync follows the size requested by the personality,
|
2005-06-22 07:17:13 +07:00
|
|
|
* which defaults to physical size, but can be virtual size
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
max_sectors = mddev->resync_max_sectors;
|
2005-11-09 12:39:26 +07:00
|
|
|
mddev->resync_mismatches = 0;
|
2006-06-26 14:27:40 +07:00
|
|
|
/* we don't use the checkpoint if there's a bitmap */
|
2008-06-28 05:31:24 +07:00
|
|
|
if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
|
|
|
|
j = mddev->resync_min;
|
|
|
|
else if (!mddev->bitmap)
|
2006-06-26 14:27:40 +07:00
|
|
|
j = mddev->recovery_cp;
|
2008-06-28 05:31:24 +07:00
|
|
|
|
2006-03-27 16:18:09 +07:00
|
|
|
} else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
|
|
|
|
max_sectors = mddev->size << 1;
|
2006-06-26 14:27:40 +07:00
|
|
|
else {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* recovery follows the physical size of devices */
|
|
|
|
max_sectors = mddev->size << 1;
|
2006-06-26 14:27:40 +07:00
|
|
|
j = MaxSector;
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2006-06-26 14:27:40 +07:00
|
|
|
if (rdev->raid_disk >= 0 &&
|
|
|
|
!test_bit(Faulty, &rdev->flags) &&
|
|
|
|
!test_bit(In_sync, &rdev->flags) &&
|
|
|
|
rdev->recovery_offset < j)
|
|
|
|
j = rdev->recovery_offset;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-10-03 15:15:57 +07:00
|
|
|
printk(KERN_INFO "md: %s of RAID array %s\n", desc, mdname(mddev));
|
|
|
|
printk(KERN_INFO "md: minimum _guaranteed_ speed:"
|
|
|
|
" %d KB/sec/disk.\n", speed_min(mddev));
|
2005-09-10 14:26:54 +07:00
|
|
|
printk(KERN_INFO "md: using maximum available idle IO bandwidth "
|
2006-10-03 15:15:57 +07:00
|
|
|
"(but not more than %d KB/sec) for %s.\n",
|
|
|
|
speed_max(mddev), desc);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-03-31 10:27:02 +07:00
|
|
|
is_mddev_idle(mddev, 1); /* this initializes IO event counters */
|
2006-06-26 14:27:40 +07:00
|
|
|
|
2005-06-22 07:17:13 +07:00
|
|
|
io_sectors = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
for (m = 0; m < SYNC_MARKS; m++) {
|
|
|
|
mark[m] = jiffies;
|
2005-06-22 07:17:13 +07:00
|
|
|
mark_cnt[m] = io_sectors;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
last_mark = 0;
|
|
|
|
mddev->resync_mark = mark[last_mark];
|
|
|
|
mddev->resync_mark_cnt = mark_cnt[last_mark];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tune reconstruction:
|
|
|
|
*/
|
|
|
|
window = 32*(PAGE_SIZE/512);
|
|
|
|
printk(KERN_INFO "md: using %dk window, over a total of %llu blocks.\n",
|
|
|
|
window/2,(unsigned long long) max_sectors/2);
|
|
|
|
|
|
|
|
atomic_set(&mddev->recovery_active, 0);
|
|
|
|
last_check = 0;
|
|
|
|
|
|
|
|
if (j>2) {
|
|
|
|
printk(KERN_INFO
|
2006-10-03 15:15:57 +07:00
|
|
|
"md: resuming %s of %s from checkpoint.\n",
|
|
|
|
desc, mdname(mddev));
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->curr_resync = j;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (j < max_sectors) {
|
2005-06-22 07:17:13 +07:00
|
|
|
sector_t sectors;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-22 07:17:13 +07:00
|
|
|
skipped = 0;
|
2008-02-06 16:39:52 +07:00
|
|
|
if (j >= mddev->resync_max) {
|
|
|
|
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
|
|
|
|
wait_event(mddev->recovery_wait,
|
|
|
|
mddev->resync_max > j
|
|
|
|
|| kthread_should_stop());
|
|
|
|
}
|
|
|
|
if (kthread_should_stop())
|
|
|
|
goto interrupted;
|
2005-06-22 07:17:13 +07:00
|
|
|
sectors = mddev->pers->sync_request(mddev, j, &skipped,
|
2008-02-06 16:39:52 +07:00
|
|
|
currspeed < speed_min(mddev));
|
2005-06-22 07:17:13 +07:00
|
|
|
if (sectors == 0) {
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
}
|
2005-06-22 07:17:13 +07:00
|
|
|
|
|
|
|
if (!skipped) { /* actual IO requested */
|
|
|
|
io_sectors += sectors;
|
|
|
|
atomic_add(sectors, &mddev->recovery_active);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
j += sectors;
|
|
|
|
if (j>1) mddev->curr_resync = j;
|
2006-07-10 18:44:16 +07:00
|
|
|
mddev->curr_mark_cnt = io_sectors;
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
if (last_check == 0)
|
|
|
|
/* this is the earliers that rebuilt will be
|
|
|
|
* visible in /proc/mdstat
|
|
|
|
*/
|
|
|
|
md_new_event(mddev);
|
2005-06-22 07:17:13 +07:00
|
|
|
|
|
|
|
if (last_check + window > io_sectors || j == max_sectors)
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
|
|
|
|
2005-06-22 07:17:13 +07:00
|
|
|
last_check = io_sectors;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
repeat:
|
|
|
|
if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) {
|
|
|
|
/* step marks */
|
|
|
|
int next = (last_mark+1) % SYNC_MARKS;
|
|
|
|
|
|
|
|
mddev->resync_mark = mark[next];
|
|
|
|
mddev->resync_mark_cnt = mark_cnt[next];
|
|
|
|
mark[next] = jiffies;
|
2005-06-22 07:17:13 +07:00
|
|
|
mark_cnt[next] = io_sectors - atomic_read(&mddev->recovery_active);
|
2005-04-17 05:20:36 +07:00
|
|
|
last_mark = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-06 16:39:52 +07:00
|
|
|
if (kthread_should_stop())
|
|
|
|
goto interrupted;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* this loop exits only if either when we are slower than
|
|
|
|
* the 'hard' speed limit, or the system was IO-idle for
|
|
|
|
* a jiffy.
|
|
|
|
* the system might be non-idle CPU-wise, but we only care
|
|
|
|
* about not overloading the IO subsystem. (things like an
|
|
|
|
* e2fsck being done on the RAID array should execute fast)
|
|
|
|
*/
|
2007-11-08 02:26:56 +07:00
|
|
|
blk_unplug(mddev->queue);
|
2005-04-17 05:20:36 +07:00
|
|
|
cond_resched();
|
|
|
|
|
2005-06-22 07:17:13 +07:00
|
|
|
currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2
|
|
|
|
/((jiffies-mddev->resync_mark)/HZ +1) +1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-06 15:21:36 +07:00
|
|
|
if (currspeed > speed_min(mddev)) {
|
|
|
|
if ((currspeed > speed_max(mddev)) ||
|
2009-03-31 10:27:02 +07:00
|
|
|
!is_mddev_idle(mddev, 0)) {
|
2005-11-18 16:11:01 +07:00
|
|
|
msleep(500);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto repeat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-10-03 15:15:57 +07:00
|
|
|
printk(KERN_INFO "md: %s: %s done.\n",mdname(mddev), desc);
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* this also signals 'finished resyncing' to md_stop
|
|
|
|
*/
|
|
|
|
out:
|
2007-11-08 02:26:56 +07:00
|
|
|
blk_unplug(mddev->queue);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));
|
|
|
|
|
|
|
|
/* tell personality that we are finished */
|
2005-06-22 07:17:13 +07:00
|
|
|
mddev->pers->sync_request(mddev, max_sectors, &skipped, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
|
2006-06-26 14:27:40 +07:00
|
|
|
mddev->curr_resync > 2) {
|
|
|
|
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
|
|
|
|
if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
|
|
|
|
if (mddev->curr_resync >= mddev->recovery_cp) {
|
|
|
|
printk(KERN_INFO
|
2006-10-03 15:15:57 +07:00
|
|
|
"md: checkpointing %s of %s.\n",
|
|
|
|
desc, mdname(mddev));
|
2006-06-26 14:27:40 +07:00
|
|
|
mddev->recovery_cp = mddev->curr_resync;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
mddev->recovery_cp = MaxSector;
|
|
|
|
} else {
|
|
|
|
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))
|
|
|
|
mddev->curr_resync = MaxSector;
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2006-06-26 14:27:40 +07:00
|
|
|
if (rdev->raid_disk >= 0 &&
|
|
|
|
!test_bit(Faulty, &rdev->flags) &&
|
|
|
|
!test_bit(In_sync, &rdev->flags) &&
|
|
|
|
rdev->recovery_offset < mddev->curr_resync)
|
|
|
|
rdev->recovery_offset = mddev->curr_resync;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-12-10 17:20:52 +07:00
|
|
|
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
skip:
|
|
|
|
mddev->curr_resync = 0;
|
2008-06-28 05:31:24 +07:00
|
|
|
mddev->resync_min = 0;
|
2008-02-06 16:39:52 +07:00
|
|
|
mddev->resync_max = MaxSector;
|
|
|
|
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
|
2005-04-17 05:20:36 +07:00
|
|
|
wake_up(&resync_wait);
|
|
|
|
set_bit(MD_RECOVERY_DONE, &mddev->recovery);
|
|
|
|
md_wakeup_thread(mddev->thread);
|
2008-02-06 16:39:52 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
interrupted:
|
|
|
|
/*
|
|
|
|
* got a signal, exit.
|
|
|
|
*/
|
|
|
|
printk(KERN_INFO
|
|
|
|
"md: md_do_sync() got signal ... exiting\n");
|
|
|
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
|
|
|
goto out;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-03-27 16:18:10 +07:00
|
|
|
EXPORT_SYMBOL_GPL(md_do_sync);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
|
2007-03-01 11:11:48 +07:00
|
|
|
static int remove_and_add_spares(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
int spares = 0;
|
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2007-03-01 11:11:48 +07:00
|
|
|
if (rdev->raid_disk >= 0 &&
|
2008-04-30 14:52:32 +07:00
|
|
|
!test_bit(Blocked, &rdev->flags) &&
|
2007-03-01 11:11:48 +07:00
|
|
|
(test_bit(Faulty, &rdev->flags) ||
|
|
|
|
! test_bit(In_sync, &rdev->flags)) &&
|
|
|
|
atomic_read(&rdev->nr_pending)==0) {
|
|
|
|
if (mddev->pers->hot_remove_disk(
|
|
|
|
mddev, rdev->raid_disk)==0) {
|
|
|
|
char nm[20];
|
|
|
|
sprintf(nm,"rd%d", rdev->raid_disk);
|
|
|
|
sysfs_remove_link(&mddev->kobj, nm);
|
|
|
|
rdev->raid_disk = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-09 04:31:11 +07:00
|
|
|
if (mddev->degraded && ! mddev->ro && !mddev->recovery_disabled) {
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set) {
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
if (rdev->raid_disk >= 0 &&
|
2008-07-29 13:28:06 +07:00
|
|
|
!test_bit(In_sync, &rdev->flags) &&
|
|
|
|
!test_bit(Blocked, &rdev->flags))
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
spares++;
|
2007-03-01 11:11:48 +07:00
|
|
|
if (rdev->raid_disk < 0
|
|
|
|
&& !test_bit(Faulty, &rdev->flags)) {
|
|
|
|
rdev->recovery_offset = 0;
|
2008-06-28 05:31:33 +07:00
|
|
|
if (mddev->pers->
|
|
|
|
hot_add_disk(mddev, rdev) == 0) {
|
2007-03-01 11:11:48 +07:00
|
|
|
char nm[20];
|
|
|
|
sprintf(nm, "rd%d", rdev->raid_disk);
|
2007-03-27 12:32:14 +07:00
|
|
|
if (sysfs_create_link(&mddev->kobj,
|
|
|
|
&rdev->kobj, nm))
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"md: cannot register "
|
|
|
|
"%s for %s\n",
|
|
|
|
nm, mdname(mddev));
|
2007-03-01 11:11:48 +07:00
|
|
|
spares++;
|
|
|
|
md_new_event(mddev);
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
}
|
2007-03-01 11:11:48 +07:00
|
|
|
}
|
|
|
|
return spares;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* This routine is regularly called by all per-raid-array threads to
|
|
|
|
* deal with generic issues like resync and super-block update.
|
|
|
|
* Raid personalities that don't have a thread (linear/raid0) do not
|
|
|
|
* need this as they never do any recovery or update the superblock.
|
|
|
|
*
|
|
|
|
* It does not do any resync itself, but rather "forks" off other threads
|
|
|
|
* to do that as needed.
|
|
|
|
* When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in
|
|
|
|
* "->recovery" and create a thread at ->sync_thread.
|
md: restart recovery cleanly after device failure.
When we get any IO error during a recovery (rebuilding a spare), we abort
the recovery and restart it.
For RAID6 (and multi-drive RAID1) it may not be best to restart at the
beginning: when multiple failures can be tolerated, the recovery may be
able to continue and re-doing all that has already been done doesn't make
sense.
We already have the infrastructure to record where a recovery is up to
and restart from there, but it is not being used properly.
This is because:
- We sometimes abort with MD_RECOVERY_ERR rather than just MD_RECOVERY_INTR,
which causes the recovery not be be checkpointed.
- We remove spares and then re-added them which loses important state
information.
The distinction between MD_RECOVERY_ERR and MD_RECOVERY_INTR really isn't
needed. If there is an error, the relevant drive will be marked as
Faulty, and that is enough to ensure correct handling of the error. So we
first remove MD_RECOVERY_ERR, changing some of the uses of it to
MD_RECOVERY_INTR.
Then we cause the attempt to remove a non-faulty device from an array to
fail (unless recovery is impossible as the array is too degraded). Then
when remove_and_add_spares attempts to remove the devices on which
recovery can continue, it will fail, they will remain in place, and
recovery will continue on them as desired.
Issue: If we are halfway through rebuilding a spare and another drive
fails, and a new spare is immediately available, do we want to:
1/ complete the current rebuild, then go back and rebuild the new spare or
2/ restart the rebuild from the start and rebuild both devices in
parallel.
Both options can be argued for. The code currently takes option 2 as
a/ this requires least code change
b/ this results in a minimally-degraded array in minimal time.
Cc: "Eivind Sarto" <ivan@kasenna.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-05-24 03:04:39 +07:00
|
|
|
* When the thread finishes it sets MD_RECOVERY_DONE
|
2005-04-17 05:20:36 +07:00
|
|
|
* and wakeups up this thread which will reap the thread and finish up.
|
|
|
|
* This thread also removes any faulty devices (with nr_pending == 0).
|
|
|
|
*
|
|
|
|
* The overall approach is:
|
|
|
|
* 1/ if the superblock needs updating, update it.
|
|
|
|
* 2/ If a recovery thread is running, don't do anything else.
|
|
|
|
* 3/ If recovery has finished, clean up, possibly marking spares active.
|
|
|
|
* 4/ If there are any faulty devices, remove them.
|
|
|
|
* 5/ If array is degraded, try to add spares devices
|
|
|
|
* 6/ If array has spares or is not in-sync, start a resync thread.
|
|
|
|
*/
|
|
|
|
void md_check_recovery(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
|
|
|
|
|
2005-06-22 07:17:16 +07:00
|
|
|
if (mddev->bitmap)
|
|
|
|
bitmap_daemon_work(mddev->bitmap);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (mddev->ro)
|
|
|
|
return;
|
2005-06-22 07:17:11 +07:00
|
|
|
|
|
|
|
if (signal_pending(current)) {
|
2008-04-30 14:52:30 +07:00
|
|
|
if (mddev->pers->sync_request && !mddev->external) {
|
2005-06-22 07:17:11 +07:00
|
|
|
printk(KERN_INFO "md: %s in immediate safe mode\n",
|
|
|
|
mdname(mddev));
|
|
|
|
mddev->safemode = 2;
|
|
|
|
}
|
|
|
|
flush_signals(current);
|
|
|
|
}
|
|
|
|
|
2008-08-05 12:54:13 +07:00
|
|
|
if (mddev->ro && !test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
if ( ! (
|
2008-02-06 16:39:51 +07:00
|
|
|
(mddev->flags && !mddev->external) ||
|
2005-04-17 05:20:36 +07:00
|
|
|
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
|
2005-06-22 07:17:11 +07:00
|
|
|
test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
|
2008-04-30 14:52:30 +07:00
|
|
|
(mddev->external == 0 && mddev->safemode == 1) ||
|
2005-06-22 07:17:11 +07:00
|
|
|
(mddev->safemode == 2 && ! atomic_read(&mddev->writes_pending)
|
|
|
|
&& !mddev->in_sync && mddev->recovery_cp == MaxSector)
|
2005-04-17 05:20:36 +07:00
|
|
|
))
|
|
|
|
return;
|
2005-06-22 07:17:11 +07:00
|
|
|
|
2006-03-27 16:18:20 +07:00
|
|
|
if (mddev_trylock(mddev)) {
|
2007-03-01 11:11:48 +07:00
|
|
|
int spares = 0;
|
2005-06-22 07:17:11 +07:00
|
|
|
|
2008-08-05 12:54:13 +07:00
|
|
|
if (mddev->ro) {
|
|
|
|
/* Only thing we do on a ro array is remove
|
|
|
|
* failed devices.
|
|
|
|
*/
|
|
|
|
remove_and_add_spares(mddev);
|
|
|
|
clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2008-04-30 14:52:30 +07:00
|
|
|
if (!mddev->external) {
|
2008-06-28 05:31:36 +07:00
|
|
|
int did_change = 0;
|
2008-04-30 14:52:30 +07:00
|
|
|
spin_lock_irq(&mddev->write_lock);
|
|
|
|
if (mddev->safemode &&
|
|
|
|
!atomic_read(&mddev->writes_pending) &&
|
|
|
|
!mddev->in_sync &&
|
|
|
|
mddev->recovery_cp == MaxSector) {
|
|
|
|
mddev->in_sync = 1;
|
2008-06-28 05:31:36 +07:00
|
|
|
did_change = 1;
|
2008-04-30 14:52:30 +07:00
|
|
|
if (mddev->persistent)
|
|
|
|
set_bit(MD_CHANGE_CLEAN, &mddev->flags);
|
|
|
|
}
|
|
|
|
if (mddev->safemode == 1)
|
|
|
|
mddev->safemode = 0;
|
|
|
|
spin_unlock_irq(&mddev->write_lock);
|
2008-06-28 05:31:36 +07:00
|
|
|
if (did_change)
|
2008-10-21 09:25:21 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_state);
|
2005-06-22 07:17:11 +07:00
|
|
|
}
|
|
|
|
|
2006-10-03 15:15:46 +07:00
|
|
|
if (mddev->flags)
|
|
|
|
md_update_sb(mddev, 0);
|
2005-06-22 07:17:12 +07:00
|
|
|
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2008-06-28 05:31:44 +07:00
|
|
|
if (test_and_clear_bit(StateChanged, &rdev->flags))
|
2008-10-21 09:25:28 +07:00
|
|
|
sysfs_notify_dirent(rdev->sysfs_state);
|
2008-06-28 05:31:44 +07:00
|
|
|
|
2005-06-22 07:17:12 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
|
|
|
|
!test_bit(MD_RECOVERY_DONE, &mddev->recovery)) {
|
|
|
|
/* resync/recovery still happening */
|
|
|
|
clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
if (mddev->sync_thread) {
|
|
|
|
/* resync has finished, collect result */
|
|
|
|
md_unregister_thread(mddev->sync_thread);
|
|
|
|
mddev->sync_thread = NULL;
|
2008-08-08 00:02:47 +07:00
|
|
|
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
|
|
|
|
!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* success...*/
|
|
|
|
/* activate any spares */
|
2008-06-28 05:31:43 +07:00
|
|
|
if (mddev->pers->spare_active(mddev))
|
|
|
|
sysfs_notify(&mddev->kobj, NULL,
|
|
|
|
"degraded");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-10-03 15:15:46 +07:00
|
|
|
md_update_sb(mddev, 1);
|
2005-06-22 07:17:25 +07:00
|
|
|
|
|
|
|
/* if array is no-longer degraded, then any saved_raid_disk
|
|
|
|
* information must be scrapped
|
|
|
|
*/
|
|
|
|
if (!mddev->degraded)
|
2009-01-09 04:31:08 +07:00
|
|
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
2005-06-22 07:17:25 +07:00
|
|
|
rdev->saved_raid_disk = -1;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->recovery = 0;
|
|
|
|
/* flag recovery needed just to double check */
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
2009-01-09 04:31:05 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_action);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
md_new_event(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto unlock;
|
|
|
|
}
|
2008-06-28 05:31:41 +07:00
|
|
|
/* Set RUNNING before clearing NEEDED to avoid
|
|
|
|
* any transients in the value of "sync_action".
|
|
|
|
*/
|
|
|
|
set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
|
|
|
|
clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
2005-11-09 12:39:26 +07:00
|
|
|
/* Clear some bits that don't mean anything, but
|
|
|
|
* might be left set
|
|
|
|
*/
|
|
|
|
clear_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
|
|
|
clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-26 14:27:40 +07:00
|
|
|
if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery))
|
|
|
|
goto unlock;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* no recovery is running.
|
|
|
|
* remove any failed drives, then
|
|
|
|
* add spares if possible.
|
|
|
|
* Spare are also removed and re-added, to allow
|
|
|
|
* the personality to fail the re-add.
|
|
|
|
*/
|
|
|
|
|
2007-03-01 11:11:48 +07:00
|
|
|
if (mddev->reshape_position != MaxSector) {
|
|
|
|
if (mddev->pers->check_reshape(mddev) != 0)
|
|
|
|
/* Cannot proceed */
|
|
|
|
goto unlock;
|
|
|
|
set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
|
2008-06-28 05:31:41 +07:00
|
|
|
clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
|
2007-03-01 11:11:48 +07:00
|
|
|
} else if ((spares = remove_and_add_spares(mddev))) {
|
2005-11-09 12:39:26 +07:00
|
|
|
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
|
|
|
|
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
|
2008-08-08 00:02:47 +07:00
|
|
|
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
|
2008-06-28 05:31:41 +07:00
|
|
|
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
|
2005-11-09 12:39:26 +07:00
|
|
|
} else if (mddev->recovery_cp < MaxSector) {
|
|
|
|
set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
|
2008-06-28 05:31:41 +07:00
|
|
|
clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
|
2005-11-09 12:39:26 +07:00
|
|
|
} else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
|
|
|
|
/* nothing to be done ... */
|
2005-04-17 05:20:36 +07:00
|
|
|
goto unlock;
|
2005-11-09 12:39:26 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mddev->pers->sync_request) {
|
2005-06-22 07:17:27 +07:00
|
|
|
if (spares && mddev->bitmap && ! mddev->bitmap->file) {
|
|
|
|
/* We are adding a device or devices to an array
|
|
|
|
* which has the bitmap stored on all devices.
|
|
|
|
* So make sure all bitmap pages get written
|
|
|
|
*/
|
|
|
|
bitmap_write_all(mddev->bitmap);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev->sync_thread = md_register_thread(md_do_sync,
|
|
|
|
mddev,
|
|
|
|
"%s_resync");
|
|
|
|
if (!mddev->sync_thread) {
|
|
|
|
printk(KERN_ERR "%s: could not start resync"
|
|
|
|
" thread...\n",
|
|
|
|
mdname(mddev));
|
|
|
|
/* leave the spares where they are, it shouldn't hurt */
|
|
|
|
mddev->recovery = 0;
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
} else
|
2005-04-17 05:20:36 +07:00
|
|
|
md_wakeup_thread(mddev->sync_thread);
|
2009-01-09 04:31:05 +07:00
|
|
|
sysfs_notify_dirent(mddev->sysfs_action);
|
[PATCH] md: make /proc/mdstat pollable
With this patch it is possible to poll /proc/mdstat to detect arrays appearing
or disappearing, to detect failures, recovery starting, recovery completing,
and devices being added and removed.
It is similar to the poll-ability of /proc/mounts, though different in that:
We always report that the file is readable (because face it, it is, even if
only for EOF).
We report POLLPRI when there is a change so that select() can detect
it as an exceptional event. Not only are these exceptional events, but
that is the mechanism that the current 'mdadm' uses to watch for events
(It also polls after a timeout).
(We also report POLLERR like /proc/mounts).
Finally, we only reset the per-file event counter when the start of the file
is read, rather than when poll() returns an event. This is more robust as it
means that an fd will continue to report activity to poll/select until the
program clearly responds to that activity.
md_new_event takes an 'mddev' which isn't currently used, but it will be soon.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-06 15:20:30 +07:00
|
|
|
md_new_event(mddev);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
unlock:
|
2008-06-28 05:31:41 +07:00
|
|
|
if (!mddev->sync_thread) {
|
|
|
|
clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
|
|
|
|
if (test_and_clear_bit(MD_RECOVERY_RECOVER,
|
|
|
|
&mddev->recovery))
|
2009-01-09 04:31:05 +07:00
|
|
|
if (mddev->sysfs_action)
|
|
|
|
sysfs_notify_dirent(mddev->sysfs_action);
|
2008-06-28 05:31:41 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
mddev_unlock(mddev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-30 14:52:32 +07:00
|
|
|
void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
|
|
|
|
{
|
2008-10-21 09:25:28 +07:00
|
|
|
sysfs_notify_dirent(rdev->sysfs_state);
|
2008-04-30 14:52:32 +07:00
|
|
|
wait_event_timeout(rdev->blocked_wait,
|
|
|
|
!test_bit(Blocked, &rdev->flags),
|
|
|
|
msecs_to_jiffies(5000));
|
|
|
|
rdev_dec_pending(rdev, mddev);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(md_wait_for_blocked_rdev);
|
|
|
|
|
2005-05-06 06:16:09 +07:00
|
|
|
static int md_notify_reboot(struct notifier_block *this,
|
|
|
|
unsigned long code, void *x)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
mddev_t *mddev;
|
|
|
|
|
|
|
|
if ((code == SYS_DOWN) || (code == SYS_HALT) || (code == SYS_POWER_OFF)) {
|
|
|
|
|
|
|
|
printk(KERN_INFO "md: stopping all md devices.\n");
|
|
|
|
|
2008-02-06 16:39:58 +07:00
|
|
|
for_each_mddev(mddev, tmp)
|
2006-05-26 07:39:25 +07:00
|
|
|
if (mddev_trylock(mddev)) {
|
2008-08-05 12:54:13 +07:00
|
|
|
/* Force a switch to readonly even array
|
|
|
|
* appears to still be in use. Hence
|
|
|
|
* the '100'.
|
|
|
|
*/
|
2008-10-13 07:55:12 +07:00
|
|
|
do_md_stop(mddev, 1, 100);
|
2006-05-26 07:39:25 +07:00
|
|
|
mddev_unlock(mddev);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* certain more exotic SCSI devices are known to be
|
|
|
|
* volatile wrt too early system reboots. While the
|
|
|
|
* right place to handle this issue is the given
|
|
|
|
* driver, we do want to have a safe RAID driver ...
|
|
|
|
*/
|
|
|
|
mdelay(1000*1);
|
|
|
|
}
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
2005-05-06 06:16:09 +07:00
|
|
|
static struct notifier_block md_notifier = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.notifier_call = md_notify_reboot,
|
|
|
|
.next = NULL,
|
|
|
|
.priority = INT_MAX, /* before any real devices */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void md_geninit(void)
|
|
|
|
{
|
|
|
|
dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t));
|
|
|
|
|
2008-04-29 15:02:35 +07:00
|
|
|
proc_create("mdstat", S_IRUGO, NULL, &md_seq_fops);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-05-06 06:16:09 +07:00
|
|
|
static int __init md_init(void)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-03-31 10:27:02 +07:00
|
|
|
if (register_blkdev(MD_MAJOR, "md"))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -1;
|
|
|
|
if ((mdp_major=register_blkdev(0, "mdp"))<=0) {
|
2009-03-31 10:27:02 +07:00
|
|
|
unregister_blkdev(MD_MAJOR, "md");
|
2005-04-17 05:20:36 +07:00
|
|
|
return -1;
|
|
|
|
}
|
2009-03-31 10:27:02 +07:00
|
|
|
blk_register_region(MKDEV(MD_MAJOR, 0), 1UL<<MINORBITS, THIS_MODULE,
|
2006-10-03 15:15:59 +07:00
|
|
|
md_probe, NULL, NULL);
|
|
|
|
blk_register_region(MKDEV(mdp_major, 0), 1UL<<MINORBITS, THIS_MODULE,
|
2005-04-17 05:20:36 +07:00
|
|
|
md_probe, NULL, NULL);
|
|
|
|
|
|
|
|
register_reboot_notifier(&md_notifier);
|
2007-02-14 15:34:09 +07:00
|
|
|
raid_table_header = register_sysctl_table(raid_root_table);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
md_geninit();
|
2008-10-13 07:55:12 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef MODULE
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Searches all registered partitions for autorun RAID arrays
|
|
|
|
* at boot time.
|
|
|
|
*/
|
2007-10-17 13:30:52 +07:00
|
|
|
|
|
|
|
static LIST_HEAD(all_detected_devices);
|
|
|
|
struct detected_devices_node {
|
|
|
|
struct list_head list;
|
|
|
|
dev_t dev;
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
void md_autodetect_dev(dev_t dev)
|
|
|
|
{
|
2007-10-17 13:30:52 +07:00
|
|
|
struct detected_devices_node *node_detected_dev;
|
|
|
|
|
|
|
|
node_detected_dev = kzalloc(sizeof(*node_detected_dev), GFP_KERNEL);
|
|
|
|
if (node_detected_dev) {
|
|
|
|
node_detected_dev->dev = dev;
|
|
|
|
list_add_tail(&node_detected_dev->list, &all_detected_devices);
|
|
|
|
} else {
|
|
|
|
printk(KERN_CRIT "md: md_autodetect_dev: kzalloc failed"
|
|
|
|
", skipping dev(%d,%d)\n", MAJOR(dev), MINOR(dev));
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void autostart_arrays(int part)
|
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
2007-10-17 13:30:52 +07:00
|
|
|
struct detected_devices_node *node_detected_dev;
|
|
|
|
dev_t dev;
|
|
|
|
int i_scanned, i_passed;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-17 13:30:52 +07:00
|
|
|
i_scanned = 0;
|
|
|
|
i_passed = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-17 13:30:52 +07:00
|
|
|
printk(KERN_INFO "md: Autodetecting RAID arrays.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-17 13:30:52 +07:00
|
|
|
while (!list_empty(&all_detected_devices) && i_scanned < INT_MAX) {
|
|
|
|
i_scanned++;
|
|
|
|
node_detected_dev = list_entry(all_detected_devices.next,
|
|
|
|
struct detected_devices_node, list);
|
|
|
|
list_del(&node_detected_dev->list);
|
|
|
|
dev = node_detected_dev->dev;
|
|
|
|
kfree(node_detected_dev);
|
2007-07-17 18:06:11 +07:00
|
|
|
rdev = md_import_device(dev,0, 90);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (IS_ERR(rdev))
|
|
|
|
continue;
|
|
|
|
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(Faulty, &rdev->flags)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
MD_BUG();
|
|
|
|
continue;
|
|
|
|
}
|
2008-03-05 05:29:31 +07:00
|
|
|
set_bit(AutoDetected, &rdev->flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
list_add(&rdev->same_set, &pending_raid_disks);
|
2007-10-17 13:30:52 +07:00
|
|
|
i_passed++;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-10-17 13:30:52 +07:00
|
|
|
|
|
|
|
printk(KERN_INFO "md: Scanned %d and added %d devices.\n",
|
|
|
|
i_scanned, i_passed);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
autorun_devices(part);
|
|
|
|
}
|
|
|
|
|
2006-12-10 17:20:50 +07:00
|
|
|
#endif /* !MODULE */
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static __exit void md_exit(void)
|
|
|
|
{
|
|
|
|
mddev_t *mddev;
|
|
|
|
struct list_head *tmp;
|
2005-06-21 11:15:16 +07:00
|
|
|
|
2009-03-31 10:27:02 +07:00
|
|
|
blk_unregister_region(MKDEV(MD_MAJOR,0), 1U << MINORBITS);
|
2006-10-03 15:15:59 +07:00
|
|
|
blk_unregister_region(MKDEV(mdp_major,0), 1U << MINORBITS);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-03-31 10:27:02 +07:00
|
|
|
unregister_blkdev(MD_MAJOR,"md");
|
2005-04-17 05:20:36 +07:00
|
|
|
unregister_blkdev(mdp_major, "mdp");
|
|
|
|
unregister_reboot_notifier(&md_notifier);
|
|
|
|
unregister_sysctl_table(raid_table_header);
|
|
|
|
remove_proc_entry("mdstat", NULL);
|
2008-02-06 16:39:58 +07:00
|
|
|
for_each_mddev(mddev, tmp) {
|
2005-04-17 05:20:36 +07:00
|
|
|
export_array(mddev);
|
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 if it
is no longer needed.
This ensures 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.
Some udev configurations will always open an md device when it first
appears. If we allow an md device that was just created by an open
to disappear on an immediate close, then this can race with such udev
configurations and result in an infinite loop the device being opened
and closed, then re-open due to the 'ADD' even from the first open,
and then close and so on.
So we make sure an md device, once created by an open, remains active
at least until some md 'ioctl' has been made on it. This means that
all normal usage of md devices will allow them to disappear promptly
when not needed, but the worst that an incorrect usage will do it
cause an inactive md device to be left in existence (it can easily be
removed).
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>
2009-01-09 04:31:10 +07:00
|
|
|
mddev->hold_active = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-10 01:56:42 +07:00
|
|
|
subsys_initcall(md_init);
|
2005-04-17 05:20:36 +07:00
|
|
|
module_exit(md_exit)
|
|
|
|
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
static int get_ro(char *buffer, struct kernel_param *kp)
|
|
|
|
{
|
|
|
|
return sprintf(buffer, "%d", start_readonly);
|
|
|
|
}
|
|
|
|
static int set_ro(const char *val, struct kernel_param *kp)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
int num = simple_strtoul(val, &e, 10);
|
|
|
|
if (*val && (*e == '\0' || *e == '\n')) {
|
|
|
|
start_readonly = num;
|
2006-01-06 15:20:52 +07:00
|
|
|
return 0;
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2006-07-10 18:44:18 +07:00
|
|
|
module_param_call(start_ro, set_ro, get_ro, NULL, S_IRUSR|S_IWUSR);
|
|
|
|
module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR);
|
2006-01-06 15:20:15 +07:00
|
|
|
|
2009-01-09 04:31:10 +07:00
|
|
|
module_param_call(new_array, add_named_array, NULL, NULL, S_IWUSR);
|
[PATCH] md: allow md arrays to be started read-only (module parameter).
When an md array is started, the superblock will be written, and resync may
commense. This is not good if you want to be completely read-only as, for
example, when preparing to resume from a suspend-to-disk image.
So introduce a module parameter "start_ro" which can be set
to '1' at boot, at module load, or via
/sys/module/md_mod/parameters/start_ro
When this is set, new arrays get an 'auto-ro' mode, which disables all
internal io (superblock updates, resync, recovery) and is automatically
switched to 'rw' when the first write request arrives.
The array can be set to true 'ro' mode using 'mdadm -r' before the first
write request, or resync can be started without a write using 'mdadm -w'.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:36 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
EXPORT_SYMBOL(register_md_personality);
|
|
|
|
EXPORT_SYMBOL(unregister_md_personality);
|
|
|
|
EXPORT_SYMBOL(md_error);
|
|
|
|
EXPORT_SYMBOL(md_done_sync);
|
|
|
|
EXPORT_SYMBOL(md_write_start);
|
|
|
|
EXPORT_SYMBOL(md_write_end);
|
|
|
|
EXPORT_SYMBOL(md_register_thread);
|
|
|
|
EXPORT_SYMBOL(md_unregister_thread);
|
|
|
|
EXPORT_SYMBOL(md_wakeup_thread);
|
|
|
|
EXPORT_SYMBOL(md_check_recovery);
|
|
|
|
MODULE_LICENSE("GPL");
|
2005-08-05 02:53:32 +07:00
|
|
|
MODULE_ALIAS("md");
|
2005-08-27 08:34:15 +07:00
|
|
|
MODULE_ALIAS_BLOCKDEV_MAJOR(MD_MAJOR);
|