Major GEOM MULTIPATH class rewrite:
- Improved locking and destruction process to fix crashes. - Improved "automatic" configuration method to make it consistent and safe by reading metadata back from all specified paths after writing to one. - Added provider size check to reduce chance of ordering conflict with other GEOM classes. - Added "manual" configuration method without using on-disk metadata. - Added "add" and "remove" commands to allow manage paths manually. - Failed paths are no longer dropped from geom, but only marked as FAIL and excluded from I/O operations. - Automatically restore failed paths when all others paths are marked as failed, for example, because of device-caused (not transport) errors. - Added "fail" and "restore" commands to manually control FAIL flag. - geom is now destroyed on last path disconnection. - Added optional Active/Active mode support. Unlike Active/Passive mode, load evenly distributed between all working paths. If supported by the device, it allows to significantly improve performance, utilizing bandwidth of all paths. It is controlled by -A option during creation. Disabled by default now. - Improved `status` and `list` commands output. Sponsored by: iXsystems, inc. MFC after: 1 month
This commit is contained in:
parent
09979ece0f
commit
ec5f778a89
@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <strings.h>
|
||||
#include <assert.h>
|
||||
#include <libgeom.h>
|
||||
#include <unistd.h>
|
||||
#include <uuid.h>
|
||||
#include <geom/multipath/g_multipath.h>
|
||||
|
||||
@ -48,31 +49,58 @@ uint32_t version = G_MULTIPATH_VERSION;
|
||||
static void mp_main(struct gctl_req *, unsigned int);
|
||||
static void mp_label(struct gctl_req *);
|
||||
static void mp_clear(struct gctl_req *);
|
||||
static void mp_add(struct gctl_req *);
|
||||
|
||||
struct g_command class_commands[] = {
|
||||
{
|
||||
"label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, mp_main, G_NULL_OPTS,
|
||||
"[-v] name prov ..."
|
||||
"create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
|
||||
{
|
||||
{ 'A', "active_active", NULL, G_TYPE_BOOL },
|
||||
G_OPT_SENTINEL
|
||||
},
|
||||
"[-vA] name prov ..."
|
||||
},
|
||||
{
|
||||
"add", G_FLAG_VERBOSE | G_FLAG_LOADKLD, mp_main, G_NULL_OPTS,
|
||||
"[-v] name prov ..."
|
||||
"label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, mp_main,
|
||||
{
|
||||
{ 'A', "active_active", NULL, G_TYPE_BOOL },
|
||||
G_OPT_SENTINEL
|
||||
},
|
||||
"[-vA] name prov ..."
|
||||
},
|
||||
{
|
||||
"destroy", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] prov ..."
|
||||
"add", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] name prov"
|
||||
},
|
||||
{
|
||||
"clear", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS,
|
||||
"[-v] prov ..."
|
||||
"remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] name prov"
|
||||
},
|
||||
{
|
||||
"fail", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] name prov"
|
||||
},
|
||||
{
|
||||
"restore", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] name prov"
|
||||
},
|
||||
{
|
||||
"rotate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] prov ..."
|
||||
"[-v] name"
|
||||
},
|
||||
{
|
||||
"getactive", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] name"
|
||||
},
|
||||
{
|
||||
"destroy", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] name"
|
||||
},
|
||||
{
|
||||
"stop", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||
"[-v] name"
|
||||
},
|
||||
{
|
||||
"clear", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS,
|
||||
"[-v] prov ..."
|
||||
},
|
||||
G_CMD_SENTINEL
|
||||
@ -90,8 +118,6 @@ mp_main(struct gctl_req *req, unsigned int flags __unused)
|
||||
}
|
||||
if (strcmp(name, "label") == 0) {
|
||||
mp_label(req);
|
||||
} else if (strcmp(name, "add") == 0) {
|
||||
mp_add(req);
|
||||
} else if (strcmp(name, "clear") == 0) {
|
||||
mp_clear(req);
|
||||
} else {
|
||||
@ -103,13 +129,13 @@ static void
|
||||
mp_label(struct gctl_req *req)
|
||||
{
|
||||
struct g_multipath_metadata md;
|
||||
off_t disksiz = 0, msize;
|
||||
uint8_t *sector;
|
||||
off_t disksize = 0, msize;
|
||||
uint8_t *sector, *rsector;
|
||||
char *ptr;
|
||||
uuid_t uuid;
|
||||
uint32_t secsize = 0, ssize, status;
|
||||
const char *name, *mpname;
|
||||
int error, i, nargs;
|
||||
const char *name, *name2, *mpname;
|
||||
int error, i, nargs, fd;
|
||||
|
||||
nargs = gctl_get_int(req, "nargs");
|
||||
if (nargs < 2) {
|
||||
@ -132,14 +158,14 @@ mp_label(struct gctl_req *req)
|
||||
}
|
||||
if (i == 1) {
|
||||
secsize = ssize;
|
||||
disksiz = msize;
|
||||
disksize = msize;
|
||||
} else {
|
||||
if (secsize != ssize) {
|
||||
gctl_error(req, "%s sector size %u different.",
|
||||
name, ssize);
|
||||
return;
|
||||
}
|
||||
if (disksiz != msize) {
|
||||
if (disksize != msize) {
|
||||
gctl_error(req, "%s media size %ju different.",
|
||||
name, (intmax_t)msize);
|
||||
return;
|
||||
@ -155,7 +181,7 @@ mp_label(struct gctl_req *req)
|
||||
md.md_version = G_MULTIPATH_VERSION;
|
||||
mpname = gctl_get_ascii(req, "arg0");
|
||||
strlcpy(md.md_name, mpname, sizeof(md.md_name));
|
||||
md.md_size = disksiz;
|
||||
md.md_size = disksize;
|
||||
md.md_sectorsize = secsize;
|
||||
uuid_create(&uuid, &status);
|
||||
if (status != uuid_s_ok) {
|
||||
@ -168,18 +194,9 @@ mp_label(struct gctl_req *req)
|
||||
return;
|
||||
}
|
||||
strlcpy(md.md_uuid, ptr, sizeof (md.md_uuid));
|
||||
md.md_active_active = gctl_get_int(req, "active_active");
|
||||
free(ptr);
|
||||
|
||||
/*
|
||||
* Clear metadata on initial provider first.
|
||||
*/
|
||||
name = gctl_get_ascii(req, "arg1");
|
||||
error = g_metadata_clear(name, NULL);
|
||||
if (error != 0) {
|
||||
gctl_error(req, "cannot clear metadata on %s: %s.", name, strerror(error));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a sector to write as metadata.
|
||||
*/
|
||||
@ -189,6 +206,12 @@ mp_label(struct gctl_req *req)
|
||||
return;
|
||||
}
|
||||
memset(sector, 0, secsize);
|
||||
rsector = malloc(secsize);
|
||||
if (rsector == NULL) {
|
||||
free(sector);
|
||||
gctl_error(req, "unable to allocate metadata buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* encode the metadata
|
||||
@ -198,6 +221,7 @@ mp_label(struct gctl_req *req)
|
||||
/*
|
||||
* Store metadata on the initial provider.
|
||||
*/
|
||||
name = gctl_get_ascii(req, "arg1");
|
||||
error = g_metadata_store(name, sector, secsize);
|
||||
if (error != 0) {
|
||||
gctl_error(req, "cannot store metadata on %s: %s.", name, strerror(error));
|
||||
@ -205,20 +229,29 @@ mp_label(struct gctl_req *req)
|
||||
}
|
||||
|
||||
/*
|
||||
* Now add the rest of the providers.
|
||||
* Now touch the rest of the providers to hint retaste.
|
||||
*/
|
||||
error = gctl_change_param(req, "verb", -1, "add");
|
||||
if (error) {
|
||||
gctl_error(req, "unable to change verb to \"add\": %s.", strerror(error));
|
||||
return;
|
||||
}
|
||||
for (i = 2; i < nargs; i++) {
|
||||
error = gctl_change_param(req, "arg1", -1, gctl_get_ascii(req, "arg%d", i));
|
||||
if (error) {
|
||||
gctl_error(req, "unable to add %s to %s: %s.", gctl_get_ascii(req, "arg%d", i), mpname, strerror(error));
|
||||
name2 = gctl_get_ascii(req, "arg%d", i);
|
||||
fd = g_open(name2, 1);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Unable to open %s: %s.\n",
|
||||
name2, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
mp_add(req);
|
||||
if (pread(fd, rsector, secsize, disksize - secsize) !=
|
||||
secsize) {
|
||||
fprintf(stderr, "Unable to read metadata from %s: %s.\n",
|
||||
name2, strerror(errno));
|
||||
g_close(fd);
|
||||
continue;
|
||||
}
|
||||
g_close(fd);
|
||||
if (memcmp(sector, rsector, secsize)) {
|
||||
fprintf(stderr, "No metadata found on %s."
|
||||
" It is not a path of %s.\n",
|
||||
name2, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,13 +280,3 @@ mp_clear(struct gctl_req *req)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mp_add(struct gctl_req *req)
|
||||
{
|
||||
const char *errstr;
|
||||
|
||||
errstr = gctl_issue(req);
|
||||
if (errstr != NULL && errstr[0] != '\0') {
|
||||
gctl_error(req, "%s", errstr);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 26, 2007
|
||||
.Dd October 31, 2011
|
||||
.Dt GMULTIPATH 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -32,11 +32,48 @@
|
||||
.Nd "disk multipath control utility"
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Cm label
|
||||
.Op Fl hv
|
||||
.Cm create
|
||||
.Op Fl Av
|
||||
.Ar name
|
||||
.Ar prov ...
|
||||
.Nm
|
||||
.Cm label
|
||||
.Op Fl Av
|
||||
.Ar name
|
||||
.Ar prov ...
|
||||
.Nm
|
||||
.Cm add
|
||||
.Op Fl v
|
||||
.Ar name prov
|
||||
.Nm
|
||||
.Cm remove
|
||||
.Op Fl v
|
||||
.Ar name prov
|
||||
.Nm
|
||||
.Cm fail
|
||||
.Op Fl v
|
||||
.Ar name prov
|
||||
.Nm
|
||||
.Cm restore
|
||||
.Op Fl v
|
||||
.Ar name prov
|
||||
.Nm
|
||||
.Cm rotate
|
||||
.Op Fl v
|
||||
.Ar name
|
||||
.Nm
|
||||
.Cm getactive
|
||||
.Op Fl v
|
||||
.Ar name
|
||||
.Nm
|
||||
.Cm destroy
|
||||
.Op Fl v
|
||||
.Ar name
|
||||
.Nm
|
||||
.Cm stop
|
||||
.Op Fl v
|
||||
.Ar name
|
||||
.Nm
|
||||
.Cm clear
|
||||
.Op Fl v
|
||||
.Ar prov ...
|
||||
@ -53,27 +90,79 @@ The
|
||||
.Nm
|
||||
utility is used for device multipath configuration.
|
||||
.Pp
|
||||
Only automatic configuration is supported at the present time via the
|
||||
.Cm label
|
||||
command.
|
||||
This operation writes a label on the last sector of the underlying
|
||||
disk device with a contained name and UUID.
|
||||
The UUID guarantees uniqueness
|
||||
in a shared storage environment but is in general too cumbersome to use.
|
||||
The multipath device can be configured using two different methods:
|
||||
.Dq manual
|
||||
or
|
||||
.Dq automatic .
|
||||
When using the
|
||||
.Dq manual
|
||||
method, no metadata are stored on the devices, so the multipath
|
||||
device has to be configured by hand every time it is needed.
|
||||
Additional device paths also won't be detected automatically.
|
||||
The
|
||||
.Dq automatic
|
||||
method uses on-disk metadata to detect device and all it's paths.
|
||||
Metadata use the last sector of the underlying disk device and
|
||||
include device name and UUID.
|
||||
The UUID guarantees uniqueness in a shared storage environment
|
||||
but is in general too cumbersome to use.
|
||||
The name is what is exported via the device interface.
|
||||
.Pp
|
||||
The first argument to
|
||||
.Nm
|
||||
indicates an action to be performed:
|
||||
.Bl -tag -width ".Cm destroy"
|
||||
.It Cm create
|
||||
Create multipath device with
|
||||
.Dq manual
|
||||
method without writing any on-disk metadata.
|
||||
It is up to administrator, how to properly identify device paths.
|
||||
Kernel will only check that all given providers have same media and
|
||||
sector sizes.
|
||||
.Pp
|
||||
.Fl A
|
||||
option enables Active/Active mode, otherwise Active/Passive mode is used
|
||||
by default.
|
||||
.It Cm label
|
||||
Label the given underlying device with the specified
|
||||
Create multipath device with
|
||||
.Dq automatic
|
||||
method.
|
||||
Label the first given provider with on-disk metadata using the specified
|
||||
.Ar name .
|
||||
The kernel module
|
||||
.Pa geom_multipath.ko
|
||||
will be loaded if it is not loaded already.
|
||||
The rest of given providers will be retasted to detect these metadata.
|
||||
It reliably protects against specifying unrelated providers.
|
||||
Providers with no matching metadata detected will not be added to the device.
|
||||
.Pp
|
||||
.Fl A
|
||||
option enables Active/Active mode, otherwise Active/Passive mode is used
|
||||
by default.
|
||||
.It Cm add
|
||||
Add the given provider as a path to the given multipath device.
|
||||
Should normally be used only for devices created with
|
||||
.Dq manual
|
||||
method, unless you know what you are doing (you are sure that it is another
|
||||
device path, but tasting its metadata in regular
|
||||
.Dq automatic
|
||||
way is not possible).
|
||||
.It Cm remove
|
||||
Remove the given provider as a path from the given multipath device.
|
||||
If the last path removed, the multipath device will be destroyed.
|
||||
.It Cm fail
|
||||
Mark specified provider as a path of the specified multipath device as failed.
|
||||
If there are other paths present, new requests will be forwarded there.
|
||||
.It Cm restore
|
||||
Mark specified provider as a path of the specified multipath device as
|
||||
operational, allowing it to handle requests.
|
||||
.It Cm rotate
|
||||
Change the active provider/path in Active/Passive mode.
|
||||
.It Cm getactive
|
||||
Get the currently active provider(s)/path(s).
|
||||
.It Cm destroy
|
||||
Destroy the given multipath device clearing metadata.
|
||||
.It Cm stop
|
||||
Stop the given multipath device without clearing metadata.
|
||||
.It Cm clear
|
||||
Clear metadata on the given device.
|
||||
Clear metadata on the given provider.
|
||||
.It Cm list
|
||||
See
|
||||
.Xr geom 8 .
|
||||
@ -101,14 +190,15 @@ Debug level of the
|
||||
GEOM class.
|
||||
This can be set to 0 (default) or 1 to disable or enable various
|
||||
forms of chattiness.
|
||||
.It Va kern.geom.multipath.exclusive : No 1
|
||||
Open underlying providers exclusively, preventing individual paths access.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
Exit status is 0 on success, and 1 if the command fails.
|
||||
.Sh MULTIPATH ARCHITECTURE
|
||||
.Pp
|
||||
This is an active/passive
|
||||
multiple path architecture with no device knowledge or presumptions other
|
||||
than size matching built in.
|
||||
This is a multiple path architecture with no device knowledge or
|
||||
presumptions other than size matching built in.
|
||||
Therefore the user must exercise some care
|
||||
in selecting providers that do indeed represent multiple paths to the
|
||||
same underlying disk device.
|
||||
@ -133,15 +223,16 @@ of multiple pathnames refer to the same device should be left to the
|
||||
system operator who will use tools and knowledge of their own storage
|
||||
subsystem to make the correct configuration selection.
|
||||
.Pp
|
||||
As an active/passive architecture, only one path has I/O moving on it
|
||||
There are Active/Passive and Active/Active operation modes supported.
|
||||
In Active/Passive mode only one path has I/O moving on it
|
||||
at any point in time.
|
||||
This I/O continues until an I/O is returned with
|
||||
a generic I/O error or a "Nonexistent Device" error.
|
||||
When this occurs,
|
||||
the active device is kicked out of the
|
||||
.Nm MULTIPATH
|
||||
GEOM class and the next in a list is selected, the failed I/O reissued
|
||||
and the system proceeds.
|
||||
When this occurs, that path is marked FAIL, the next path
|
||||
in a list is selected as active and the failed I/O reissued.
|
||||
In Active/Active mode all paths not marked FAIL may handle I/O same time.
|
||||
Requests are distributed between paths to equalize load.
|
||||
For capable devices it allows to utilize bandwidth of all paths.
|
||||
.Pp
|
||||
When new devices are added to the system the
|
||||
.Nm MULTIPATH
|
||||
@ -149,9 +240,9 @@ GEOM class is given an opportunity to taste these new devices.
|
||||
If a new
|
||||
device has a
|
||||
.Nm MULTIPATH
|
||||
label, the device is used to either create a new
|
||||
on-disk metadata label, the device is used to either create a new
|
||||
.Nm MULTIPATH
|
||||
GEOM, or to attach to the end of the list of devices for an existing
|
||||
GEOM, or been added the list of paths for an existing
|
||||
.Nm MULTIPATH
|
||||
GEOM.
|
||||
.Pp
|
||||
@ -176,7 +267,7 @@ of an RSCN event from the Fabric Domain Controller), they can cause
|
||||
a rescan to occur and cause the attachment and configuration of any
|
||||
(now) new devices to occur, causing the taste event described above.
|
||||
.Pp
|
||||
This means that this active/passive architecture is not a one-shot path
|
||||
This means that this multipath architecture is not a one-shot path
|
||||
failover, but can be considered to be steady state as long as failed
|
||||
paths are repaired (automatically or otherwise).
|
||||
.Pp
|
||||
@ -184,7 +275,7 @@ Automatic rescanning is not a requirement.
|
||||
Nor is Fibre Channel.
|
||||
The
|
||||
same failover mechanisms work equally well for traditional "Parallel"
|
||||
SCSI but require manual intervention with
|
||||
SCSI but may require manual intervention with
|
||||
.Xr camcontrol 8
|
||||
to cause the reattachment of repaired device links.
|
||||
.Sh EXAMPLES
|
||||
@ -226,9 +317,9 @@ mount /dev/multipath/FREDa /mnt....
|
||||
.Pp
|
||||
The resultant console output looks something like:
|
||||
.Bd -literal -offset indent
|
||||
GEOM_MULTIPATH: adding da0 to Fred/b631385f-c61c-11db-b884-0011116ae789
|
||||
GEOM_MULTIPATH: da0 now active path in Fred
|
||||
GEOM_MULTIPATH: adding da2 to Fred/b631385f-c61c-11db-b884-0011116ae789
|
||||
GEOM_MULTIPATH: da0 added to FRED
|
||||
GEOM_MULTIPATH: da0 is now active path in FRED
|
||||
GEOM_MULTIPATH: da2 added to FRED
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr geom 4 ,
|
||||
@ -240,24 +331,6 @@ GEOM_MULTIPATH: adding da2 to Fred/b631385f-c61c-11db-b884-0011116ae789
|
||||
.Xr mount 8 ,
|
||||
.Xr newfs 8 ,
|
||||
.Xr sysctl 8
|
||||
.Sh BUGS
|
||||
The
|
||||
.Nm
|
||||
should allow for a manual method of pairing disks.
|
||||
.Pp
|
||||
There is currently no way for
|
||||
.Pa geom_multipath.ko
|
||||
to distinguish between various label instances of the same provider.
|
||||
That
|
||||
is devices such as
|
||||
.Ar da0
|
||||
and
|
||||
.Ar da0c
|
||||
can be tasted and instantiated as multiple paths for the same device.
|
||||
Technically, this is correct, but pretty useless.
|
||||
This will be fixed soon
|
||||
(I hope), but to avoid this it is a good idea to destroy any label on
|
||||
the disk object prior to labelling it with
|
||||
.Nm .
|
||||
.Sh AUTHOR
|
||||
.An Matthew Jacob Aq mjacob@FreeBSD.org
|
||||
.An Alexander Motin Aq mav@FreeBSD.org
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 Alexander Motin <mav@FreeBSD.org>
|
||||
* Copyright (c) 2006-2007 Matthew Jacob <mjacob@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -53,6 +54,9 @@ static SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0,
|
||||
static u_int g_multipath_debug = 0;
|
||||
SYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW,
|
||||
&g_multipath_debug, 0, "Debug level");
|
||||
static u_int g_multipath_exclusive = 1;
|
||||
SYSCTL_UINT(_kern_geom_multipath, OID_AUTO, exclusive, CTLFLAG_RW,
|
||||
&g_multipath_exclusive, 0, "Exclusively open providers");
|
||||
|
||||
static enum {
|
||||
GKT_NIL,
|
||||
@ -79,6 +83,7 @@ static g_taste_t g_multipath_taste;
|
||||
static g_ctl_req_t g_multipath_config;
|
||||
static g_init_t g_multipath_init;
|
||||
static g_fini_t g_multipath_fini;
|
||||
static g_dumpconf_t g_multipath_dumpconf;
|
||||
|
||||
struct g_class g_multipath_class = {
|
||||
.name = G_MULTIPATH_CLASS_NAME,
|
||||
@ -90,35 +95,144 @@ struct g_class g_multipath_class = {
|
||||
.fini = g_multipath_fini
|
||||
};
|
||||
|
||||
#define MP_BAD 0x1
|
||||
#define MP_POSTED 0x2
|
||||
#define MP_FAIL 0x00000001
|
||||
#define MP_LOST 0x00000002
|
||||
#define MP_NEW 0x00000004
|
||||
#define MP_POSTED 0x00000008
|
||||
#define MP_BAD (MP_FAIL | MP_LOST | MP_NEW)
|
||||
#define MP_IDLE 0x00000010
|
||||
#define MP_IDLE_MASK 0xfffffff0
|
||||
|
||||
static int
|
||||
g_multipath_good(struct g_geom *gp)
|
||||
{
|
||||
struct g_consumer *cp;
|
||||
int n = 0;
|
||||
|
||||
LIST_FOREACH(cp, &gp->consumer, consumer) {
|
||||
if ((cp->index & MP_BAD) == 0)
|
||||
n++;
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_fault(struct g_consumer *cp, int cause)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *lcp;
|
||||
struct g_geom *gp;
|
||||
|
||||
gp = cp->geom;
|
||||
sc = gp->softc;
|
||||
cp->index |= cause;
|
||||
if (g_multipath_good(gp) == 0 && sc->sc_ndisks > 0) {
|
||||
LIST_FOREACH(lcp, &gp->consumer, consumer) {
|
||||
if (lcp->provider == NULL ||
|
||||
(lcp->index & (MP_LOST | MP_NEW)))
|
||||
continue;
|
||||
if (sc->sc_ndisks > 1 && lcp == cp)
|
||||
continue;
|
||||
printf("GEOM_MULTIPATH: "
|
||||
"all paths in %s were marked FAIL, restore %s\n",
|
||||
sc->sc_name, lcp->provider->name);
|
||||
lcp->index &= ~MP_FAIL;
|
||||
}
|
||||
}
|
||||
if (cp != sc->sc_active)
|
||||
return;
|
||||
sc->sc_active = NULL;
|
||||
LIST_FOREACH(lcp, &gp->consumer, consumer) {
|
||||
if ((lcp->index & MP_BAD) == 0) {
|
||||
sc->sc_active = lcp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sc->sc_active == NULL) {
|
||||
printf("GEOM_MULTIPATH: out of providers for %s\n",
|
||||
sc->sc_name);
|
||||
} else if (!sc->sc_active_active) {
|
||||
printf("GEOM_MULTIPATH: %s is now active path in %s\n",
|
||||
sc->sc_active->provider->name, sc->sc_name);
|
||||
}
|
||||
}
|
||||
|
||||
static struct g_consumer *
|
||||
g_multipath_choose(struct g_geom *gp)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *best, *cp;
|
||||
|
||||
sc = gp->softc;
|
||||
if (!sc->sc_active_active)
|
||||
return (sc->sc_active);
|
||||
best = NULL;
|
||||
LIST_FOREACH(cp, &gp->consumer, consumer) {
|
||||
if (cp->index & MP_BAD)
|
||||
continue;
|
||||
cp->index += MP_IDLE;
|
||||
if (best == NULL || cp->private < best->private ||
|
||||
(cp->private == best->private && cp->index > best->index))
|
||||
best = cp;
|
||||
}
|
||||
if (best != NULL)
|
||||
best->index &= ~MP_IDLE_MASK;
|
||||
return (best);
|
||||
}
|
||||
|
||||
static void
|
||||
g_mpd(void *arg, int flags __unused)
|
||||
{
|
||||
struct g_geom *gp;
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *cp;
|
||||
int w;
|
||||
|
||||
g_topology_assert();
|
||||
cp = arg;
|
||||
if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
|
||||
gp = cp->geom;
|
||||
if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) {
|
||||
w = cp->acw;
|
||||
g_access(cp, -cp->acr, -cp->acw, -cp->ace);
|
||||
if (w > 0 && cp->provider != NULL &&
|
||||
(cp->provider->geom->flags & G_GEOM_WITHER) == 0) {
|
||||
g_post_event(g_mpd, cp, M_WAITOK, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
sc = gp->softc;
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
if (cp->provider) {
|
||||
printf("GEOM_MULTIPATH: %s removed from %s\n",
|
||||
cp->provider->name, cp->geom->name);
|
||||
cp->provider->name, gp->name);
|
||||
g_detach(cp);
|
||||
}
|
||||
g_destroy_consumer(cp);
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
if (LIST_EMPTY(&gp->consumer))
|
||||
g_multipath_destroy(gp);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_orphan(struct g_consumer *cp)
|
||||
{
|
||||
if ((cp->index & MP_POSTED) == 0) {
|
||||
struct g_multipath_softc *sc;
|
||||
uintptr_t *cnt;
|
||||
|
||||
g_topology_assert();
|
||||
printf("GEOM_MULTIPATH: %s in %s was disconnected\n",
|
||||
cp->provider->name, cp->geom->name);
|
||||
sc = cp->geom->softc;
|
||||
cnt = (uintptr_t *)&cp->private;
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
sc->sc_ndisks--;
|
||||
g_multipath_fault(cp, MP_LOST);
|
||||
if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
|
||||
cp->index |= MP_POSTED;
|
||||
printf("GEOM_MULTIPATH: %s orphaned in %s\n",
|
||||
cp->provider->name, cp->geom->name);
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
g_mpd(cp, 0);
|
||||
}
|
||||
} else
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -128,20 +242,29 @@ g_multipath_start(struct bio *bp)
|
||||
struct g_geom *gp;
|
||||
struct g_consumer *cp;
|
||||
struct bio *cbp;
|
||||
uintptr_t *cnt;
|
||||
|
||||
gp = bp->bio_to->geom;
|
||||
sc = gp->softc;
|
||||
KASSERT(sc != NULL, ("NULL sc"));
|
||||
cp = sc->cp_active;
|
||||
if (cp == NULL) {
|
||||
g_io_deliver(bp, ENXIO);
|
||||
return;
|
||||
}
|
||||
cbp = g_clone_bio(bp);
|
||||
if (cbp == NULL) {
|
||||
g_io_deliver(bp, ENOMEM);
|
||||
return;
|
||||
}
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
cp = g_multipath_choose(gp);
|
||||
if (cp == NULL) {
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
g_destroy_bio(cbp);
|
||||
g_io_deliver(bp, ENXIO);
|
||||
return;
|
||||
}
|
||||
if ((uintptr_t)bp->bio_driver1 < sc->sc_ndisks)
|
||||
bp->bio_driver1 = (void *)(uintptr_t)sc->sc_ndisks;
|
||||
cnt = (uintptr_t *)&cp->private;
|
||||
(*cnt)++;
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
cbp->bio_done = g_multipath_done;
|
||||
g_io_request(cbp, cp);
|
||||
}
|
||||
@ -149,12 +272,27 @@ g_multipath_start(struct bio *bp)
|
||||
static void
|
||||
g_multipath_done(struct bio *bp)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *cp;
|
||||
uintptr_t *cnt;
|
||||
|
||||
if (bp->bio_error == ENXIO || bp->bio_error == EIO) {
|
||||
mtx_lock(&gmtbq_mtx);
|
||||
bioq_insert_tail(&gmtbq, bp);
|
||||
wakeup(&g_multipath_kt_state);
|
||||
mtx_unlock(&gmtbq_mtx);
|
||||
wakeup(&g_multipath_kt_state);
|
||||
} else {
|
||||
cp = bp->bio_from;
|
||||
sc = cp->geom->softc;
|
||||
cnt = (uintptr_t *)&cp->private;
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
(*cnt)--;
|
||||
if (*cnt == 0 && (cp->index & MP_LOST)) {
|
||||
cp->index |= MP_POSTED;
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
g_post_event(g_mpd, cp, M_WAITOK, NULL);
|
||||
} else
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
g_std_done(bp);
|
||||
}
|
||||
}
|
||||
@ -167,6 +305,7 @@ g_multipath_done_error(struct bio *bp)
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *cp;
|
||||
struct g_provider *pp;
|
||||
uintptr_t *cnt;
|
||||
|
||||
/*
|
||||
* If we had a failure, we have to check first to see
|
||||
@ -176,47 +315,31 @@ g_multipath_done_error(struct bio *bp)
|
||||
* to the next available consumer.
|
||||
*/
|
||||
|
||||
g_topology_lock();
|
||||
pbp = bp->bio_parent;
|
||||
gp = pbp->bio_to->geom;
|
||||
sc = gp->softc;
|
||||
cp = bp->bio_from;
|
||||
pp = cp->provider;
|
||||
cnt = (uintptr_t *)&cp->private;
|
||||
|
||||
cp->index |= MP_BAD;
|
||||
if (cp->nend == cp->nstart && pp->nend == pp->nstart) {
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
printf("GEOM_MULTIPATH: Error %d, %s in %s marked FAIL\n",
|
||||
bp->bio_error, pp->name, sc->sc_name);
|
||||
g_multipath_fault(cp, MP_FAIL);
|
||||
(*cnt)--;
|
||||
if (*cnt == 0 && (cp->index & (MP_LOST | MP_POSTED)) == MP_LOST) {
|
||||
cp->index |= MP_POSTED;
|
||||
g_post_event(g_mpd, cp, M_NOWAIT, NULL);
|
||||
}
|
||||
if (cp == sc->cp_active) {
|
||||
struct g_consumer *lcp;
|
||||
printf("GEOM_MULTIPATH: %s failed in %s\n",
|
||||
pp->name, sc->sc_name);
|
||||
sc->cp_active = NULL;
|
||||
LIST_FOREACH(lcp, &gp->consumer, consumer) {
|
||||
if ((lcp->index & MP_BAD) == 0) {
|
||||
sc->cp_active = lcp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sc->cp_active == NULL || sc->cp_active->provider == NULL) {
|
||||
printf("GEOM_MULTIPATH: out of providers for %s\n",
|
||||
sc->sc_name);
|
||||
g_topology_unlock();
|
||||
return;
|
||||
} else {
|
||||
printf("GEOM_MULTIPATH: %s now active path in %s\n",
|
||||
sc->cp_active->provider->name, sc->sc_name);
|
||||
}
|
||||
}
|
||||
g_topology_unlock();
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
g_post_event(g_mpd, cp, M_WAITOK, NULL);
|
||||
} else
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
|
||||
/*
|
||||
* If we can fruitfully restart the I/O, do so.
|
||||
*/
|
||||
if (sc->cp_active) {
|
||||
if (pbp->bio_children < (uintptr_t)pbp->bio_driver1) {
|
||||
pbp->bio_inbed++;
|
||||
g_destroy_bio(bp);
|
||||
pbp->bio_children--;
|
||||
g_multipath_start(pbp);
|
||||
} else {
|
||||
g_std_done(bp);
|
||||
@ -254,6 +377,7 @@ g_multipath_access(struct g_provider *pp, int dr, int dw, int de)
|
||||
{
|
||||
struct g_geom *gp;
|
||||
struct g_consumer *cp, *badcp = NULL;
|
||||
struct g_multipath_softc *sc;
|
||||
int error;
|
||||
|
||||
gp = pp->geom;
|
||||
@ -265,6 +389,10 @@ g_multipath_access(struct g_provider *pp, int dr, int dw, int de)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
sc = gp->softc;
|
||||
sc->sc_opened += dr + dw + de;
|
||||
if (sc->sc_stopping && sc->sc_opened == 0)
|
||||
g_multipath_destroy(gp);
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
@ -286,6 +414,9 @@ g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md)
|
||||
g_topology_assert();
|
||||
|
||||
LIST_FOREACH(gp, &mp->geom, geom) {
|
||||
sc = gp->softc;
|
||||
if (sc == NULL || sc->sc_stopping)
|
||||
continue;
|
||||
if (strcmp(gp->name, md->md_name) == 0) {
|
||||
printf("GEOM_MULTIPATH: name %s already exists\n",
|
||||
md->md_name);
|
||||
@ -295,19 +426,25 @@ g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md)
|
||||
|
||||
gp = g_new_geomf(mp, md->md_name);
|
||||
sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
|
||||
mtx_init(&sc->sc_mtx, "multipath", NULL, MTX_DEF);
|
||||
memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid));
|
||||
memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name));
|
||||
sc->sc_active_active = md->md_active_active;
|
||||
gp->softc = sc;
|
||||
gp->start = g_multipath_start;
|
||||
gp->orphan = g_multipath_orphan;
|
||||
gp->access = g_multipath_access;
|
||||
memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid));
|
||||
memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name));
|
||||
gp->dumpconf = g_multipath_dumpconf;
|
||||
|
||||
pp = g_new_providerf(gp, "multipath/%s", md->md_name);
|
||||
/* limit the provider to not have it stomp on metadata */
|
||||
pp->mediasize = md->md_size - md->md_sectorsize;
|
||||
pp->sectorsize = md->md_sectorsize;
|
||||
sc->pp = pp;
|
||||
if (md->md_size != 0) {
|
||||
pp->mediasize = md->md_size -
|
||||
((md->md_uuid[0] != 0) ? md->md_sectorsize : 0);
|
||||
pp->sectorsize = md->md_sectorsize;
|
||||
}
|
||||
sc->sc_pp = pp;
|
||||
g_error_provider(pp, 0);
|
||||
printf("GEOM_MULTIPATH: %s created\n", gp->name);
|
||||
return (gp);
|
||||
}
|
||||
|
||||
@ -316,7 +453,7 @@ g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *cp, *nxtcp;
|
||||
int error;
|
||||
int error, acr, acw, ace;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
@ -337,6 +474,8 @@ g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
|
||||
}
|
||||
nxtcp = LIST_FIRST(&gp->consumer);
|
||||
cp = g_new_consumer(gp);
|
||||
cp->private = NULL;
|
||||
cp->index = MP_NEW;
|
||||
error = g_attach(cp, pp);
|
||||
if (error != 0) {
|
||||
printf("GEOM_MULTIPATH: cannot attach %s to %s",
|
||||
@ -344,29 +483,51 @@ g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
|
||||
g_destroy_consumer(cp);
|
||||
return (error);
|
||||
}
|
||||
cp->private = sc;
|
||||
cp->index = 0;
|
||||
|
||||
/*
|
||||
* Set access permissions on new consumer to match other consumers
|
||||
*/
|
||||
if (nxtcp && (nxtcp->acr + nxtcp->acw + nxtcp->ace)) {
|
||||
error = g_access(cp, nxtcp->acr, nxtcp->acw, nxtcp->ace);
|
||||
if (error) {
|
||||
printf("GEOM_MULTIPATH: cannot set access in "
|
||||
"attaching %s to %s/%s (%d)\n",
|
||||
pp->name, sc->sc_name, sc->sc_uuid, error);
|
||||
g_detach(cp);
|
||||
g_destroy_consumer(cp);
|
||||
return (error);
|
||||
}
|
||||
if (sc->sc_pp) {
|
||||
acr = sc->sc_pp->acr;
|
||||
acw = sc->sc_pp->acw;
|
||||
ace = sc->sc_pp->ace;
|
||||
} else
|
||||
acr = acw = ace = 0;
|
||||
if (g_multipath_exclusive) {
|
||||
acr++;
|
||||
acw++;
|
||||
ace++;
|
||||
}
|
||||
printf("GEOM_MULTIPATH: adding %s to %s/%s\n",
|
||||
pp->name, sc->sc_name, sc->sc_uuid);
|
||||
if (sc->cp_active == NULL) {
|
||||
sc->cp_active = cp;
|
||||
printf("GEOM_MULTIPATH: %s now active path in %s\n",
|
||||
pp->name, sc->sc_name);
|
||||
error = g_access(cp, acr, acw, ace);
|
||||
if (error) {
|
||||
printf("GEOM_MULTIPATH: cannot set access in "
|
||||
"attaching %s to %s (%d)\n",
|
||||
pp->name, sc->sc_name, error);
|
||||
g_detach(cp);
|
||||
g_destroy_consumer(cp);
|
||||
return (error);
|
||||
}
|
||||
if (sc->sc_pp != NULL && sc->sc_pp->mediasize == 0) {
|
||||
sc->sc_pp->mediasize = pp->mediasize -
|
||||
((sc->sc_uuid[0] != 0) ? pp->sectorsize : 0);
|
||||
sc->sc_pp->sectorsize = pp->sectorsize;
|
||||
}
|
||||
if (sc->sc_pp != NULL &&
|
||||
sc->sc_pp->stripesize == 0 && sc->sc_pp->stripeoffset == 0) {
|
||||
sc->sc_pp->stripesize = pp->stripesize;
|
||||
sc->sc_pp->stripeoffset = pp->stripeoffset;
|
||||
}
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
cp->index = 0;
|
||||
sc->sc_ndisks++;
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
printf("GEOM_MULTIPATH: %s added to %s\n",
|
||||
pp->name, sc->sc_name);
|
||||
if (sc->sc_active == NULL) {
|
||||
sc->sc_active = cp;
|
||||
if (!sc->sc_active_active)
|
||||
printf("GEOM_MULTIPATH: %s is now active path in %s\n",
|
||||
pp->name, sc->sc_name);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@ -374,17 +535,41 @@ g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
|
||||
static int
|
||||
g_multipath_destroy(struct g_geom *gp)
|
||||
{
|
||||
struct g_provider *pp;
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *cp, *cp1;
|
||||
|
||||
g_topology_assert();
|
||||
if (gp->softc == NULL)
|
||||
return (ENXIO);
|
||||
pp = LIST_FIRST(&gp->provider);
|
||||
if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0))
|
||||
return (EBUSY);
|
||||
printf("GEOM_MULTIPATH: destroying %s\n", gp->name);
|
||||
sc = gp->softc;
|
||||
if (!sc->sc_stopping) {
|
||||
printf("GEOM_MULTIPATH: destroying %s\n", gp->name);
|
||||
sc->sc_stopping = 1;
|
||||
}
|
||||
if (sc->sc_opened != 0) {
|
||||
if (sc->sc_pp != NULL) {
|
||||
g_wither_provider(sc->sc_pp, ENXIO);
|
||||
sc->sc_pp = NULL;
|
||||
}
|
||||
return (EINPROGRESS);
|
||||
}
|
||||
LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
if ((cp->index & MP_POSTED) == 0) {
|
||||
cp->index |= MP_POSTED;
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
g_mpd(cp, 0);
|
||||
if (cp1 == NULL)
|
||||
return(0); /* Recursion happened. */
|
||||
} else
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
}
|
||||
if (!LIST_EMPTY(&gp->consumer))
|
||||
return (EINPROGRESS);
|
||||
mtx_destroy(&sc->sc_mtx);
|
||||
g_free(gp->softc);
|
||||
gp->softc = NULL;
|
||||
printf("GEOM_MULTIPATH: %s destroyed\n", gp->name);
|
||||
g_wither_geom(gp, ENXIO);
|
||||
return (0);
|
||||
}
|
||||
@ -408,15 +593,15 @@ g_multipath_rotate(struct g_geom *gp)
|
||||
return (ENXIO);
|
||||
LIST_FOREACH(lcp, &gp->consumer, consumer) {
|
||||
if ((lcp->index & MP_BAD) == 0) {
|
||||
if (sc->cp_active != lcp) {
|
||||
if (sc->sc_active != lcp)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lcp) {
|
||||
sc->cp_active = lcp;
|
||||
printf("GEOM_MULTIPATH: %s now active path in %s\n",
|
||||
lcp->provider->name, sc->sc_name);
|
||||
sc->sc_active = lcp;
|
||||
if (!sc->sc_active_active)
|
||||
printf("GEOM_MULTIPATH: %s is now active path in %s\n",
|
||||
lcp->provider->name, sc->sc_name);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@ -504,6 +689,10 @@ g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
|
||||
G_MULTIPATH_VERSION);
|
||||
return (NULL);
|
||||
}
|
||||
if (md.md_size != 0 && md.md_size != pp->mediasize)
|
||||
return (NULL);
|
||||
if (md.md_sectorsize != 0 && md.md_sectorsize != pp->sectorsize)
|
||||
return (NULL);
|
||||
if (g_multipath_debug)
|
||||
printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid);
|
||||
|
||||
@ -521,7 +710,7 @@ g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
|
||||
sc = NULL;
|
||||
LIST_FOREACH(gp, &mp->geom, geom) {
|
||||
sc = gp->softc;
|
||||
if (sc == NULL)
|
||||
if (sc == NULL || sc->sc_stopping)
|
||||
continue;
|
||||
if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0)
|
||||
break;
|
||||
@ -531,7 +720,7 @@ g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
|
||||
if (gp1 == gp)
|
||||
continue;
|
||||
sc = gp1->softc;
|
||||
if (sc == NULL)
|
||||
if (sc == NULL || sc->sc_stopping)
|
||||
continue;
|
||||
if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0)
|
||||
break;
|
||||
@ -591,12 +780,14 @@ g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
|
||||
g_multipath_ctl_add_name(struct gctl_req *req, struct g_class *mp,
|
||||
const char *name)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_geom *gp;
|
||||
struct g_consumer *cp;
|
||||
struct g_provider *pp, *pp0;
|
||||
const char *name, *mpname;
|
||||
struct g_provider *pp;
|
||||
const char *mpname;
|
||||
static const char devpf[6] = "/dev/";
|
||||
|
||||
g_topology_assert();
|
||||
@ -611,12 +802,8 @@ g_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
|
||||
gctl_error(req, "Device %s is invalid", mpname);
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
|
||||
name = gctl_get_asciiparam(req, "arg1");
|
||||
if (name == NULL) {
|
||||
gctl_error(req, "No 'arg1' argument");
|
||||
return;
|
||||
}
|
||||
if (strncmp(name, devpf, 5) == 0)
|
||||
name += 5;
|
||||
pp = g_provider_by_name(name);
|
||||
@ -626,32 +813,29 @@ g_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to make sure parameters match, if we already have one.
|
||||
* Check to make sure parameters match.
|
||||
*/
|
||||
cp = LIST_FIRST(&gp->consumer);
|
||||
if (cp) {
|
||||
pp0 = cp->provider;
|
||||
} else {
|
||||
pp0 = NULL;
|
||||
LIST_FOREACH(cp, &gp->consumer, consumer) {
|
||||
if (cp->provider == pp) {
|
||||
gctl_error(req, "provider %s is already there",
|
||||
pp->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pp0) {
|
||||
if (pp0 == pp) {
|
||||
gctl_error(req, "providers %s and %s are the same",
|
||||
pp0->name, pp->name);
|
||||
return;
|
||||
}
|
||||
if (pp0->mediasize != pp->mediasize) {
|
||||
gctl_error(req, "Provider %s is %jd; Provider %s is %jd",
|
||||
pp0->name, (intmax_t) pp0->mediasize,
|
||||
pp->name, (intmax_t) pp->mediasize);
|
||||
return;
|
||||
}
|
||||
if (pp0->sectorsize != pp->sectorsize) {
|
||||
gctl_error(req, "Provider %s has sectorsize %u; Provider %s "
|
||||
"has sectorsize %u", pp0->name, pp0->sectorsize,
|
||||
pp->name, pp->sectorsize);
|
||||
return;
|
||||
}
|
||||
if (sc->sc_pp != NULL && sc->sc_pp->mediasize != 0 &&
|
||||
sc->sc_pp->mediasize + (sc->sc_uuid[0] != 0 ? pp->sectorsize : 0)
|
||||
!= pp->mediasize) {
|
||||
gctl_error(req, "Providers size mismatch %jd != %jd",
|
||||
(intmax_t) sc->sc_pp->mediasize +
|
||||
(sc->sc_uuid[0] != 0 ? pp->sectorsize : 0),
|
||||
(intmax_t) pp->mediasize);
|
||||
return;
|
||||
}
|
||||
if (sc->sc_pp != NULL && sc->sc_pp->sectorsize != 0 &&
|
||||
sc->sc_pp->sectorsize != pp->sectorsize) {
|
||||
gctl_error(req, "Providers sectorsize mismatch %u != %u",
|
||||
sc->sc_pp->sectorsize, pp->sectorsize);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -660,21 +844,213 @@ g_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
|
||||
(void) g_multipath_add_disk(gp, pp);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_geom *gp;
|
||||
const char *mpname, *name;
|
||||
|
||||
mpname = gctl_get_asciiparam(req, "arg0");
|
||||
if (mpname == NULL) {
|
||||
gctl_error(req, "No 'arg0' argument");
|
||||
return;
|
||||
}
|
||||
gp = g_multipath_find_geom(mp, mpname);
|
||||
if (gp == NULL) {
|
||||
gctl_error(req, "Device %s not found", mpname);
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
|
||||
name = gctl_get_asciiparam(req, "arg1");
|
||||
if (name == NULL) {
|
||||
gctl_error(req, "No 'arg1' argument");
|
||||
return;
|
||||
}
|
||||
g_multipath_ctl_add_name(req, mp, name);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_ctl_create(struct gctl_req *req, struct g_class *mp)
|
||||
{
|
||||
struct g_multipath_metadata md;
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_geom *gp;
|
||||
const char *mpname, *name;
|
||||
char param[16];
|
||||
int *nargs, i, *active_active;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
|
||||
if (*nargs < 2) {
|
||||
gctl_error(req, "wrong number of arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
mpname = gctl_get_asciiparam(req, "arg0");
|
||||
if (mpname == NULL) {
|
||||
gctl_error(req, "No 'arg0' argument");
|
||||
return;
|
||||
}
|
||||
gp = g_multipath_find_geom(mp, mpname);
|
||||
if (gp != NULL) {
|
||||
gctl_error(req, "Device %s already exist", mpname);
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
|
||||
memset(&md, 0, sizeof(md));
|
||||
strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
|
||||
md.md_version = G_MULTIPATH_VERSION;
|
||||
strlcpy(md.md_name, mpname, sizeof(md.md_name));
|
||||
md.md_size = 0;
|
||||
md.md_sectorsize = 0;
|
||||
md.md_uuid[0] = 0;
|
||||
active_active = gctl_get_paraml(req, "active_active",
|
||||
sizeof(*active_active));
|
||||
md.md_active_active =
|
||||
(active_active == NULL || *active_active == 0) ? 0 : 1;
|
||||
gp = g_multipath_create(mp, &md);
|
||||
if (gp == NULL) {
|
||||
gctl_error(req, "GEOM_MULTIPATH: cannot create geom %s/%s\n",
|
||||
md.md_name, md.md_uuid);
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
|
||||
for (i = 1; i < *nargs; i++) {
|
||||
snprintf(param, sizeof(param), "arg%d", i);
|
||||
name = gctl_get_asciiparam(req, param);
|
||||
g_multipath_ctl_add_name(req, mp, name);
|
||||
}
|
||||
|
||||
if (sc->sc_ndisks != (*nargs - 1))
|
||||
g_multipath_destroy(gp);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_ctl_fail(struct gctl_req *req, struct g_class *mp, int fail)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_geom *gp;
|
||||
struct g_consumer *cp;
|
||||
const char *mpname, *name;
|
||||
int found;
|
||||
|
||||
mpname = gctl_get_asciiparam(req, "arg0");
|
||||
if (mpname == NULL) {
|
||||
gctl_error(req, "No 'arg0' argument");
|
||||
return;
|
||||
}
|
||||
gp = g_multipath_find_geom(mp, mpname);
|
||||
if (gp == NULL) {
|
||||
gctl_error(req, "Device %s not found", mpname);
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
|
||||
name = gctl_get_asciiparam(req, "arg1");
|
||||
if (name == NULL) {
|
||||
gctl_error(req, "No 'arg1' argument");
|
||||
return;
|
||||
}
|
||||
|
||||
found = 0;
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
LIST_FOREACH(cp, &gp->consumer, consumer) {
|
||||
if (cp->provider != NULL &&
|
||||
strcmp(cp->provider->name, name) == 0 &&
|
||||
(cp->index & MP_LOST) == 0) {
|
||||
found = 1;
|
||||
printf("GEOM_MULTIPATH: %s in %s is marked %s.\n",
|
||||
name, sc->sc_name, fail ? "FAIL" : "OK");
|
||||
if (fail) {
|
||||
g_multipath_fault(cp, MP_FAIL);
|
||||
} else {
|
||||
cp->index &= ~MP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
if (found == 0)
|
||||
gctl_error(req, "Provider %s not found", name);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_ctl_remove(struct gctl_req *req, struct g_class *mp)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_geom *gp;
|
||||
struct g_consumer *cp, *cp1;
|
||||
const char *mpname, *name;
|
||||
uintptr_t *cnt;
|
||||
int found;
|
||||
|
||||
mpname = gctl_get_asciiparam(req, "arg0");
|
||||
if (mpname == NULL) {
|
||||
gctl_error(req, "No 'arg0' argument");
|
||||
return;
|
||||
}
|
||||
gp = g_multipath_find_geom(mp, mpname);
|
||||
if (gp == NULL) {
|
||||
gctl_error(req, "Device %s not found", mpname);
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
|
||||
name = gctl_get_asciiparam(req, "arg1");
|
||||
if (name == NULL) {
|
||||
gctl_error(req, "No 'arg1' argument");
|
||||
return;
|
||||
}
|
||||
|
||||
found = 0;
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
|
||||
if (cp->provider != NULL &&
|
||||
strcmp(cp->provider->name, name) == 0 &&
|
||||
(cp->index & MP_LOST) == 0) {
|
||||
found = 1;
|
||||
printf("GEOM_MULTIPATH: removing %s from %s\n",
|
||||
cp->provider->name, cp->geom->name);
|
||||
sc->sc_ndisks--;
|
||||
g_multipath_fault(cp, MP_LOST);
|
||||
cnt = (uintptr_t *)&cp->private;
|
||||
if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
|
||||
cp->index |= MP_POSTED;
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
g_mpd(cp, 0);
|
||||
if (cp1 == NULL)
|
||||
return; /* Recursion happened. */
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
if (found == 0)
|
||||
gctl_error(req, "Provider %s not found", name);
|
||||
}
|
||||
|
||||
static struct g_geom *
|
||||
g_multipath_find_geom(struct g_class *mp, const char *name)
|
||||
{
|
||||
struct g_geom *gp;
|
||||
struct g_multipath_softc *sc;
|
||||
|
||||
LIST_FOREACH(gp, &mp->geom, geom) {
|
||||
if (strcmp(gp->name, name) == 0) {
|
||||
sc = gp->softc;
|
||||
if (sc == NULL || sc->sc_stopping)
|
||||
continue;
|
||||
if (strcmp(gp->name, name) == 0)
|
||||
return (gp);
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp)
|
||||
g_multipath_ctl_stop(struct gctl_req *req, struct g_class *mp)
|
||||
{
|
||||
struct g_geom *gp;
|
||||
const char *name;
|
||||
@ -693,9 +1069,58 @@ g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp)
|
||||
return;
|
||||
}
|
||||
error = g_multipath_destroy(gp);
|
||||
if (error != 0) {
|
||||
gctl_error(req, "failed to destroy %s (err=%d)", name, error);
|
||||
if (error != 0 && error != EINPROGRESS)
|
||||
gctl_error(req, "failed to stop %s (err=%d)", name, error);
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp)
|
||||
{
|
||||
struct g_geom *gp;
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *cp;
|
||||
struct g_provider *pp;
|
||||
const char *name;
|
||||
uint8_t *buf;
|
||||
int error;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
name = gctl_get_asciiparam(req, "arg0");
|
||||
if (name == NULL) {
|
||||
gctl_error(req, "No 'arg0' argument");
|
||||
return;
|
||||
}
|
||||
gp = g_multipath_find_geom(mp, name);
|
||||
if (gp == NULL) {
|
||||
gctl_error(req, "Device %s is invalid", name);
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
|
||||
if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) {
|
||||
cp = sc->sc_active;
|
||||
pp = cp->provider;
|
||||
error = g_access(cp, 1, 1, 1);
|
||||
if (error != 0) {
|
||||
gctl_error(req, "Can't open %s (%d)", pp->name, error);
|
||||
goto destroy;
|
||||
}
|
||||
g_topology_unlock();
|
||||
buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
|
||||
error = g_write_data(cp, pp->mediasize - pp->sectorsize,
|
||||
buf, pp->sectorsize);
|
||||
g_topology_lock();
|
||||
g_access(cp, -1, -1, -1);
|
||||
if (error != 0)
|
||||
gctl_error(req, "Can't erase metadata on %s (%d)",
|
||||
pp->name, error);
|
||||
}
|
||||
|
||||
destroy:
|
||||
error = g_multipath_destroy(gp);
|
||||
if (error != 0 && error != EINPROGRESS)
|
||||
gctl_error(req, "failed to destroy %s (err=%d)", name, error);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -729,7 +1154,9 @@ g_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp)
|
||||
struct sbuf *sb;
|
||||
struct g_geom *gp;
|
||||
struct g_multipath_softc *sc;
|
||||
struct g_consumer *cp;
|
||||
const char *name;
|
||||
int empty;
|
||||
|
||||
sb = sbuf_new_auto();
|
||||
|
||||
@ -745,8 +1172,21 @@ g_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp)
|
||||
return;
|
||||
}
|
||||
sc = gp->softc;
|
||||
if (sc->cp_active && sc->cp_active->provider) {
|
||||
sbuf_printf(sb, "%s\n", sc->cp_active->provider->name);
|
||||
if (sc->sc_active_active) {
|
||||
empty = 1;
|
||||
LIST_FOREACH(cp, &gp->consumer, consumer) {
|
||||
if (cp->index & MP_BAD)
|
||||
continue;
|
||||
if (!empty)
|
||||
sbuf_cat(sb, " ");
|
||||
sbuf_cat(sb, cp->provider->name);
|
||||
empty = 0;
|
||||
}
|
||||
if (empty)
|
||||
sbuf_cat(sb, "none");
|
||||
sbuf_cat(sb, "\n");
|
||||
} else if (sc->sc_active && sc->sc_active->provider) {
|
||||
sbuf_printf(sb, "%s\n", sc->sc_active->provider->name);
|
||||
} else {
|
||||
sbuf_printf(sb, "none\n");
|
||||
}
|
||||
@ -767,8 +1207,18 @@ g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb)
|
||||
gctl_error(req, "Userland and kernel parts are out of sync");
|
||||
} else if (strcmp(verb, "add") == 0) {
|
||||
g_multipath_ctl_add(req, mp);
|
||||
} else if (strcmp(verb, "create") == 0) {
|
||||
g_multipath_ctl_create(req, mp);
|
||||
} else if (strcmp(verb, "stop") == 0) {
|
||||
g_multipath_ctl_stop(req, mp);
|
||||
} else if (strcmp(verb, "destroy") == 0) {
|
||||
g_multipath_ctl_destroy(req, mp);
|
||||
} else if (strcmp(verb, "fail") == 0) {
|
||||
g_multipath_ctl_fail(req, mp, 1);
|
||||
} else if (strcmp(verb, "restore") == 0) {
|
||||
g_multipath_ctl_fail(req, mp, 0);
|
||||
} else if (strcmp(verb, "remove") == 0) {
|
||||
g_multipath_ctl_remove(req, mp);
|
||||
} else if (strcmp(verb, "rotate") == 0) {
|
||||
g_multipath_ctl_rotate(req, mp);
|
||||
} else if (strcmp(verb, "getactive") == 0) {
|
||||
@ -777,4 +1227,40 @@ g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb)
|
||||
gctl_error(req, "Unknown verb %s", verb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_multipath_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
|
||||
struct g_consumer *cp, struct g_provider *pp)
|
||||
{
|
||||
struct g_multipath_softc *sc;
|
||||
int good;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
sc = gp->softc;
|
||||
if (sc == NULL)
|
||||
return;
|
||||
if (cp != NULL) {
|
||||
sbuf_printf(sb, "%s<State>%s</State>", indent,
|
||||
(cp->index & MP_NEW) ? "NEW" :
|
||||
(cp->index & MP_LOST) ? "LOST" :
|
||||
(cp->index & MP_FAIL) ? "FAIL" :
|
||||
(sc->sc_active_active || sc->sc_active == cp) ?
|
||||
"ACTIVE" : "PASSIVE");
|
||||
} else {
|
||||
good = g_multipath_good(gp);
|
||||
sbuf_printf(sb, "%s<State>%s</State>", indent,
|
||||
good == 0 ? "BROKEN" :
|
||||
(good != sc->sc_ndisks || sc->sc_ndisks == 1) ?
|
||||
"DEGRADED" : "OPTIMAL");
|
||||
}
|
||||
if (cp == NULL && pp == NULL) {
|
||||
sbuf_printf(sb, "%s<UUID>%s</UUID>", indent, sc->sc_uuid);
|
||||
sbuf_printf(sb, "%s<Mode>Active/%s</Mode>", indent,
|
||||
sc->sc_active_active ? "Active" : "Passive");
|
||||
sbuf_printf(sb, "%s<Type>%s</Type>", indent,
|
||||
sc->sc_uuid[0] == 0 ? "MANUAL" : "AUTOMATIC");
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_GEOM_CLASS(g_multipath_class, g_multipath);
|
||||
|
@ -43,10 +43,15 @@
|
||||
#ifdef _KERNEL
|
||||
|
||||
struct g_multipath_softc {
|
||||
struct g_provider * pp;
|
||||
struct g_consumer * cp_active;
|
||||
struct g_provider * sc_pp;
|
||||
struct g_consumer * sc_active;
|
||||
struct mtx sc_mtx;
|
||||
char sc_name[16];
|
||||
char sc_uuid[40];
|
||||
int sc_opened;
|
||||
int sc_stopping;
|
||||
int sc_ndisks;
|
||||
int sc_active_active; /* Active/Active mode */
|
||||
};
|
||||
#endif /* _KERNEL */
|
||||
|
||||
@ -57,6 +62,7 @@ struct g_multipath_metadata {
|
||||
uint32_t md_version; /* version */
|
||||
uint32_t md_sectorsize; /* sectorsize of provider */
|
||||
uint64_t md_size; /* absolute size of provider */
|
||||
uint8_t md_active_active; /* Active/Active mode */
|
||||
};
|
||||
|
||||
static __inline void
|
||||
@ -79,6 +85,8 @@ multipath_metadata_encode(const struct g_multipath_metadata *md, u_char *data)
|
||||
le32enc(data, md->md_sectorsize);
|
||||
data += sizeof(md->md_sectorsize);
|
||||
le64enc(data, md->md_size);
|
||||
data += sizeof(md->md_size);
|
||||
*data = md->md_active_active;
|
||||
}
|
||||
|
||||
static __inline void
|
||||
@ -95,5 +103,7 @@ multipath_metadata_decode(u_char *data, struct g_multipath_metadata *md)
|
||||
md->md_sectorsize = le32dec(data);
|
||||
data += sizeof(md->md_sectorsize);
|
||||
md->md_size = le64dec(data);
|
||||
data += sizeof(md->md_size);
|
||||
md->md_active_active = *data;
|
||||
}
|
||||
#endif /* _G_MULTIPATH_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user