master - dmeventd: convert union to struct members
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=5ea466673af377...
Commit: 5ea466673af377328a7ed50d6fb24e66a478b44b
Parent: 45f396f2a075054f4e047abdef5eafffca72a281
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Apr 19 20:44:20 2013 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sun Apr 21 23:01:35 2013 +0200
dmeventd: convert union to struct members
Sharing char* with field has a problem in error path,
when we allocate event, but fail to allocate timeout string.
Instead of creating complicated error paths to resolve
it individually stop using unions, and let the resource
to be released in a simple _free_message().
---
WHATS_NEW_DM | 1 +
daemons/dmeventd/dmeventd.c | 54 ++++++++++++++++---------------------------
2 files changed, 21 insertions(+), 34 deletions(-)
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index eff6bd6..6e45a4c 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,5 +1,6 @@
Version 1.02.78 -
===================================
+ Update dmevent structure message_data to simplify/fix error path handling.
Validate passed params to dm_get_status_raid/thin/thin_pool().
Fix 'dmsetup splitname -o' to not fail if used without '-c' switch (1.02.68).
Add dm_config_write_{node_out/one_node_out} for enhanced config output.
diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c
index 06c0c92..35a2b25 100644
--- a/daemons/dmeventd/dmeventd.c
+++ b/daemons/dmeventd/dmeventd.c
@@ -167,14 +167,10 @@ struct message_data {
char *id;
char *dso_name; /* Name of DSO. */
char *device_uuid; /* Mapped device path. */
- union {
- char *str; /* Events string as fetched from message. */
- enum dm_event_mask field; /* Events bitfield. */
- } events;
- union {
- char *str;
- uint32_t secs;
- } timeout;
+ char *events_str; /* Events string as fetched from message. */
+ enum dm_event_mask events_field; /* Events bitfield. */
+ char *timeout_str;
+ uint32_t timeout_secs;
struct dm_event_daemon_message *msg; /* Pointer to message buffer. */
};
@@ -235,8 +231,8 @@ static struct thread_status *_alloc_thread_status(struct message_data *data,
ret->device.name = NULL;
ret->device.major = ret->device.minor = 0;
ret->dso_data = dso_data;
- ret->events = data->events.field;
- ret->timeout = data->timeout.secs;
+ ret->events = data->events_field;
+ ret->timeout = data->timeout_secs;
dm_list_init(&ret->timeout_list);
return ret;
@@ -325,9 +321,9 @@ static void _free_message(struct message_data *message_data)
{
dm_free(message_data->id);
dm_free(message_data->dso_name);
-
dm_free(message_data->device_uuid);
-
+ dm_free(message_data->events_str);
+ dm_free(message_data->timeout_str);
}
/* Parse a register message from the client. */
@@ -347,25 +343,15 @@ static int _parse_message(struct message_data *message_data)
if (_fetch_string(&message_data->id, &p, ' ') &&
_fetch_string(&message_data->dso_name, &p, ' ') &&
_fetch_string(&message_data->device_uuid, &p, ' ') &&
- _fetch_string(&message_data->events.str, &p, ' ') &&
- _fetch_string(&message_data->timeout.str, &p, ' ')) {
- if (message_data->events.str) {
- enum dm_event_mask i = atoi(message_data->events.str);
-
- /*
- * Free string representaion of events.
- * Not needed an more.
- */
- dm_free(message_data->events.str);
- message_data->events.field = i;
- }
- if (message_data->timeout.str) {
- uint32_t secs = atoi(message_data->timeout.str);
- dm_free(message_data->timeout.str);
- message_data->timeout.secs = secs ? secs :
- DM_EVENT_DEFAULT_TIMEOUT;
- }
-
+ _fetch_string(&message_data->events_str, &p, ' ') &&
+ _fetch_string(&message_data->timeout_str, &p, ' ')) {
+ if (message_data->events_str)
+ message_data->events_field =
+ atoi(message_data->events_str);
+ if (message_data->timeout_str)
+ message_data->timeout_secs =
+ atoi(message_data->timeout_str)
+ ? : DM_EVENT_DEFAULT_TIMEOUT;
ret = 1;
}
@@ -1025,7 +1011,7 @@ static int _register_for_event(struct message_data *message_data)
}
/* Or event # into events bitfield. */
- thread->events |= message_data->events.field;
+ thread->events |= message_data->events_field;
outth:
_unlock_mutex();
@@ -1070,7 +1056,7 @@ static int _unregister_for_event(struct message_data *message_data)
return 0;
}
- thread->events &= ~message_data->events.field;
+ thread->events &= ~message_data->events_field;
if (!(thread->events & DM_EVENT_TIMEOUT))
_unregister_for_timeout(thread);
@@ -1208,7 +1194,7 @@ static int _set_timeout(struct message_data *message_data)
_lock_mutex();
if ((thread = _lookup_thread_status(message_data)))
- thread->timeout = message_data->timeout.secs;
+ thread->timeout = message_data->timeout_secs;
_unlock_mutex();
return thread ? 0 : -ENODEV;
11 years
master - dmeventd: check for msg.data nonnull
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=45f396f2a07505...
Commit: 45f396f2a075054f4e047abdef5eafffca72a281
Parent: ba3cee36300d56efcdcb0af883141b1b0a6129a1
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Apr 19 17:03:50 2013 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sun Apr 21 22:56:59 2013 +0200
dmeventd: check for msg.data nonnull
Ensure we will not use msg.data as NULL for strchr.
---
daemons/dmeventd/libdevmapper-event.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c
index 1f8fbef..f7b586e 100644
--- a/daemons/dmeventd/libdevmapper-event.c
+++ b/daemons/dmeventd/libdevmapper-event.c
@@ -815,13 +815,13 @@ int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
p = msg.data;
*version = 0;
- p = strchr(p, ' '); /* Message ID */
- if (!p) return 0;
- p = strchr(p + 1, ' '); /* HELLO */
- if (!p) return 0;
- p = strchr(p + 1, ' '); /* HELLO, once more */
- if (p)
+ if (!p || !(p = strchr(p, ' '))) /* Message ID */
+ return 0;
+ if (!(p = strchr(p + 1, ' '))) /* HELLO */
+ return 0;
+ if ((p = strchr(p + 1, ' '))) /* HELLO, once more */
*version = atoi(p);
+
return 1;
}
11 years
master - config: check for hash success
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=ba3cee36300d56...
Commit: ba3cee36300d56efcdcb0af883141b1b0a6129a1
Parent: 5070ffbca7b16e4139b3da72bdff0c9add517351
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Sun Apr 21 13:12:58 2013 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sun Apr 21 22:56:59 2013 +0200
config: check for hash success
Hash insert may fail.
---
lib/config/config.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/lib/config/config.c b/lib/config/config.c
index 2c77f30..d1cf590 100644
--- a/lib/config/config.c
+++ b/lib/config/config.c
@@ -577,7 +577,11 @@ int config_def_check(struct cmd_context *cmd, int force, int skip, int suppress_
cmd->cft_def_hash = NULL;
r = 0; goto out;
}
- dm_hash_insert(cmd->cft_def_hash, vp, def);
+ if (!dm_hash_insert(cmd->cft_def_hash, vp, def)) {
+ log_error("Failed to insert configuration to hash.");
+ r = 0;
+ goto out;
+ }
}
}
11 years
master - clvmd: avoid logging in signal handler
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=5070ffbca7b16e...
Commit: 5070ffbca7b16e4139b3da72bdff0c9add517351
Parent: 61d0ae7019aa14d78678a36f1eeb43b29a113e80
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Apr 17 23:16:30 2013 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sun Apr 21 22:56:59 2013 +0200
clvmd: avoid logging in signal handler
debuglog in the main thread.
---
daemons/clvmd/clvmd.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/daemons/clvmd/clvmd.c b/daemons/clvmd/clvmd.c
index c649bbe..a64bd54 100644
--- a/daemons/clvmd/clvmd.c
+++ b/daemons/clvmd/clvmd.c
@@ -874,6 +874,7 @@ static void main_loop(int local_sock, int cmd_timeout)
int saved_errno = errno;
reread_config = 0;
+ DEBUGLOG("got SIGHUP\n");
if (clops->reread_config)
clops->reread_config();
errno = saved_errno;
@@ -982,6 +983,8 @@ static void main_loop(int local_sock, int cmd_timeout)
closedown:
clops->cluster_closedown();
+ if (quit)
+ DEBUGLOG("SIGTERM received\n");
}
static __attribute__ ((noreturn)) void wait_for_child(int c_pipe, int timeout)
@@ -2253,14 +2256,12 @@ static void sigusr2_handler(int sig)
static void sigterm_handler(int sig)
{
- DEBUGLOG("SIGTERM received\n");
quit = 1;
return;
}
static void sighup_handler(int sig)
{
- DEBUGLOG("got SIGHUP\n");
reread_config = 1;
}
11 years
master - clvmd: use pclose
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=61d0ae7019aa14...
Commit: 61d0ae7019aa14d78678a36f1eeb43b29a113e80
Parent: 0125518d6d0a0fce63998fc898c203255ad73700
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Sun Apr 21 11:30:50 2013 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sun Apr 21 22:56:59 2013 +0200
clvmd: use pclose
For popened FILE use pclose.
---
daemons/clvmd/lvm-functions.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c
index 416d4f0..6d78090 100644
--- a/daemons/clvmd/lvm-functions.c
+++ b/daemons/clvmd/lvm-functions.c
@@ -772,8 +772,8 @@ static int get_initial_state(struct dm_hash_table *excl_uuid)
}
}
}
- if (fclose(lvs))
- DEBUGLOG("lvs fclose failed: %s\n", strerror(errno));
+ if (pclose(lvs))
+ DEBUGLOG("lvs pclose failed: %s\n", strerror(errno));
dm_free(lvs_cmd);
11 years
master - clvmd: preserve foreground mode across restart
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=0125518d6d0a0f...
Commit: 0125518d6d0a0fce63998fc898c203255ad73700
Parent: c9d8d22224f5516f364bd66ce41aa506a39801a6
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Sat Apr 20 22:23:36 2013 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sun Apr 21 22:56:48 2013 +0200
clvmd: preserve foreground mode across restart
Keep clvmd in foreground when restarted.
Useful for testing.
---
daemons/clvmd/clvmd-command.c | 8 ++++++--
daemons/clvmd/clvmd.c | 5 +++++
daemons/clvmd/clvmd.h | 1 +
3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/daemons/clvmd/clvmd-command.c b/daemons/clvmd/clvmd-command.c
index c5cd461..5d93f38 100644
--- a/daemons/clvmd/clvmd-command.c
+++ b/daemons/clvmd/clvmd-command.c
@@ -383,7 +383,7 @@ static int restart_clvmd(void)
} while (hn);
/* clvmd + locks (-E uuid) + debug (-d X) + NULL */
- if (!(argv = malloc((max_locks * 2 + 5) * sizeof(*argv))))
+ if (!(argv = malloc((max_locks * 2 + 6) * sizeof(*argv))))
goto_out;
/*
@@ -391,13 +391,17 @@ static int restart_clvmd(void)
*/
argv[argc++] = "clvmd";
- /* Propogate debug options */
+ /* Propagate debug options */
if (clvmd_get_debug()) {
if (dm_snprintf(debug_arg, sizeof(debug_arg), "-d%u", clvmd_get_debug()) < 0)
goto_out;
argv[argc++] = debug_arg;
}
+ /* Propagate foreground options */
+ if (clvmd_get_foreground())
+ argv[argc++] = "-f";
+
argv[argc++] = "-I";
argv[argc++] = clops->name;
diff --git a/daemons/clvmd/clvmd.c b/daemons/clvmd/clvmd.c
index 54cf1ac..c649bbe 100644
--- a/daemons/clvmd/clvmd.c
+++ b/daemons/clvmd/clvmd.c
@@ -249,6 +249,11 @@ debug_t clvmd_get_debug(void)
return debug;
}
+int clvmd_get_foreground(void)
+{
+ return foreground_mode;
+}
+
static const char *decode_cmd(unsigned char cmdl)
{
static char buf[128];
diff --git a/daemons/clvmd/clvmd.h b/daemons/clvmd/clvmd.h
index 5bad43a..67ea41e 100644
--- a/daemons/clvmd/clvmd.h
+++ b/daemons/clvmd/clvmd.h
@@ -119,6 +119,7 @@ extern void debuglog(const char *fmt, ... )
void clvmd_set_debug(debug_t new_de);
debug_t clvmd_get_debug(void);
+int clvmd_get_foreground(void);
int sync_lock(const char *resource, int mode, int flags, int *lockid);
int sync_unlock(const char *resource, int lockid);
11 years
master - clmvd: fix responce status
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=c9d8d22224f551...
Commit: c9d8d22224f5516f364bd66ce41aa506a39801a6
Parent: 764195207d4773cf6f1674a2fb16e9a0acda304a
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Apr 19 21:11:32 2013 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sun Apr 21 22:54:42 2013 +0200
clmvd: fix responce status
Failing status code is expected to be 0.
Also do not return '*response' as pointer which has been already free().
---
WHATS_NEW | 1 +
daemons/clvmd/refresh_clvmd.c | 15 ++++++---------
lib/locking/cluster_locking.c | 10 ++++------
3 files changed, 11 insertions(+), 15 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 76347e9..95b6e13 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.99 -
===================================
+ Fix clvmd _cluster_request() return code in memory fail path.
Add writemostly/writebehind support for RAID1
Add lv_change_activate() for common activation code in vg/lvchange.
Revert change that allowed identical table reload for RAID.
diff --git a/daemons/clvmd/refresh_clvmd.c b/daemons/clvmd/refresh_clvmd.c
index 28b5625..42c3958 100644
--- a/daemons/clvmd/refresh_clvmd.c
+++ b/daemons/clvmd/refresh_clvmd.c
@@ -225,16 +225,14 @@ static int _cluster_request(char cmd, const char *node, void *data, int len,
* With an extra pair of INTs on the front to sanity
* check the pointer when we are given it back to free
*/
- *response = dm_malloc(sizeof(lvm_response_t) * num_responses +
- sizeof(int) * 2);
- if (!*response) {
+ *response = NULL;
+ if (!(rarray = dm_malloc(sizeof(lvm_response_t) * num_responses +
+ sizeof(int) * 2))) {
errno = ENOMEM;
status = 0;
goto out;
}
- rarray = *response;
-
/* Unpack the response into an lvm_response_t array */
inptr = head->args;
i = 0;
@@ -251,9 +249,9 @@ static int _cluster_request(char cmd, const char *node, void *data, int len,
int j;
for (j = 0; j < i; j++)
dm_free(rarray[i].response);
- free(*response);
+ dm_free(rarray);
errno = ENOMEM;
- status = -1;
+ status = 0;
goto out;
}
@@ -266,8 +264,7 @@ static int _cluster_request(char cmd, const char *node, void *data, int len,
*response = rarray;
out:
- if (retbuf)
- dm_free(retbuf);
+ dm_free(retbuf);
return status;
}
diff --git a/lib/locking/cluster_locking.c b/lib/locking/cluster_locking.c
index 01a47b0..3169f0d 100644
--- a/lib/locking/cluster_locking.c
+++ b/lib/locking/cluster_locking.c
@@ -241,15 +241,13 @@ static int _cluster_request(char clvmd_cmd, const char *node, void *data, int le
* With an extra pair of INTs on the front to sanity
* check the pointer when we are given it back to free
*/
- *response = dm_malloc(sizeof(lvm_response_t) * num_responses);
- if (!*response) {
+ *response = NULL;
+ if (!(rarray = dm_malloc(sizeof(lvm_response_t) * num_responses))) {
errno = ENOMEM;
status = 0;
goto out;
}
- rarray = *response;
-
/* Unpack the response into an lvm_response_t array */
inptr = head->args;
i = 0;
@@ -266,9 +264,9 @@ static int _cluster_request(char clvmd_cmd, const char *node, void *data, int le
int j;
for (j = 0; j < i; j++)
dm_free(rarray[i].response);
- free(*response);
+ dm_free(rarray);
errno = ENOMEM;
- status = -1;
+ status = 0;
goto out;
}
11 years
master - udev: also autoactivate on coldplug
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=764195207d4773...
Commit: 764195207d4773cf6f1674a2fb16e9a0acda304a
Parent: 2ccb9eb861c3b65ffc1aac024425e2cc10950077
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Fri Apr 19 12:17:53 2013 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Fri Apr 19 12:43:36 2013 +0200
udev: also autoactivate on coldplug
Commit 756bcabbfe297688ba240a880bc2b55265ad33f0 fixed autoactivation
to not trigger on each uevent for a PV that appeared in the system
most notably the events that are triggered artificially (udevadm
trigger or as the result of the WATCH udev rule being applied that
consequently generates CHANGE uevents). This fixed a situation in
which VGs/LVs were activated when they should not.
BUT we still need to care about the coldplug used at boot to
retrigger the ADD events - the "udevadm trigger --action=add"!
For non-DM-based PVs, this is already covered as for these we
run the autoactivation on ADD event only.
However, for DM-based PVs, we still need to run the
autoactivation even for the artificial ADD event, reusing
the udev DB content from previous proper CHANGE event that
came with the DM device activation.
Simply, this patch fixes a situation in which we run extra
"udevadm trigger --action=add" (or echo add > /sys/block/<dev>/uevent)
for DM-based PVs (cryptsetup devices, multipath devices, any
other DM devices...).
Without this patch, while using lvmetad + autoactivation,
any VG/LV that has a DM-based PV and for which we do not
call the activation directly, the VG/LV is not activated.
For example a VG with an LV with root FS on it which is directly
activated in initrd and then missing activation of the rest
of the LVs in the VG because of unhandled uevent retrigger on
boot after switching to root FS (the "coldplug").
(No WHATS_NEW here as this fixes the commit mentioned
above and which was not released yet.)
---
udev/10-dm.rules.in | 5 ++++-
udev/69-dm-lvm-metad.rules.in | 1 +
2 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/udev/10-dm.rules.in b/udev/10-dm.rules.in
index cfee145..512d156 100644
--- a/udev/10-dm.rules.in
+++ b/udev/10-dm.rules.in
@@ -77,7 +77,10 @@ LABEL="dm_flags_done"
# before (e.g. in initrd). If udev is used in initrd, we require the udev init
# script to not remove the existing udev database so we can reuse the information
# stored at the time of device activation in the initrd.
-ACTION=="add", ENV{DM_UDEV_RULES_VSN}!="1", ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", GOTO="dm_disable"
+ACTION!="add", GOTO="dm_no_coldplug"
+ENV{DM_UDEV_RULES_VSN}!="1", ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", GOTO="dm_disable"
+ENV{DM_ACTIVATION}="1"
+LABEL="dm_no_coldplug"
# "dm" sysfs subdirectory is available in newer versions of DM
# only (kernels >= 2.6.29). We have to check for its existence
diff --git a/udev/69-dm-lvm-metad.rules.in b/udev/69-dm-lvm-metad.rules.in
index b16a27a..66c58b3 100644
--- a/udev/69-dm-lvm-metad.rules.in
+++ b/udev/69-dm-lvm-metad.rules.in
@@ -26,6 +26,7 @@ ACTION=="remove", GOTO="lvm_scan"
KERNEL!="dm-[0-9]*", ACTION!="add", GOTO="lvm_end"
# If the PV is a dm device, scan only after proper mapping activation (CHANGE event + DM_ACTIVATION=1)
+# or after a coldplug (event retrigger) with "add" event (ADD event + DM_ACTIVATION=1)
KERNEL=="dm-[0-9]*", ENV{DM_ACTIVATION}!="1", GOTO="lvm_end"
LABEL="lvm_scan"
11 years
master - config_def_check: fix memory leak
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=2ccb9eb861c3b6...
Commit: 2ccb9eb861c3b65ffc1aac024425e2cc10950077
Parent: 2e0740f7ef420c7b981bc67cb606d180701b8c1d
Author: Tony Asleson <tasleson(a)redhat.com>
AuthorDate: Tue Apr 16 10:42:03 2013 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Apr 16 10:45:37 2013 +0200
config_def_check: fix memory leak
There is no need to strdup a key when inserting into
the hash table as the table allocates memory and copies
the string. This was causing memory to be lost.
---
lib/config/config.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/config/config.c b/lib/config/config.c
index 009eb5f..2c77f30 100644
--- a/lib/config/config.c
+++ b/lib/config/config.c
@@ -577,7 +577,7 @@ int config_def_check(struct cmd_context *cmd, int force, int skip, int suppress_
cmd->cft_def_hash = NULL;
r = 0; goto out;
}
- dm_hash_insert(cmd->cft_def_hash, dm_strdup(vp), def);
+ dm_hash_insert(cmd->cft_def_hash, vp, def);
}
}
11 years
master - RAID: Add writemostly/writebehind support for RAID1
by Jonathan Brassow
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=2e0740f7ef420c...
Commit: 2e0740f7ef420c7b981bc67cb606d180701b8c1d
Parent: dce8d06af7e5a14a1507bea0e64e6cc27556cbc7
Author: Jonathan Brassow <jbrassow(a)redhat.com>
AuthorDate: Mon Apr 15 13:59:46 2013 -0500
Committer: Jonathan Brassow <jbrassow(a)redhat.com>
CommitterDate: Mon Apr 15 13:59:46 2013 -0500
RAID: Add writemostly/writebehind support for RAID1
'lvchange' is used to alter a RAID 1 logical volume's write-mostly and
write-behind characteristics. The '--writemostly' parameter takes a
PV as an argument with an optional trailing character to specify whether
to set ('y'), unset ('n'), or toggle ('t') the value. If no trailing
character is given, it will set the flag.
Synopsis:
lvchange [--writemostly <PV>:{t|y|n}] [--writebehind <count>] vg/lv
Example:
lvchange --writemostly /dev/sdb1:y --writebehind 512 vg/raid1_lv
The last character in the 'lv_attr' field is used to show whether a device
has the WriteMostly flag set. It is signified with a 'w'. If the device
has failed, the 'p'artial flag has priority.
Example ("nosync" raid1 with mismatch_cnt and writemostly):
[~]# lvs -a --segment vg
LV VG Attr #Str Type SSize
raid1 vg Rwi---r-m 2 raid1 500.00m
[raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m
[raid1_rimage_1] vg Iwi---r-w 1 linear 500.00m
[raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m
[raid1_rmeta_1] vg ewi---r-- 1 linear 4.00m
Example (raid1 with mismatch_cnt, writemostly - but failed drive):
[~]# lvs -a --segment vg
LV VG Attr #Str Type SSize
raid1 vg rwi---r-p 2 raid1 500.00m
[raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m
[raid1_rimage_1] vg Iwi---r-p 1 linear 500.00m
[raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m
[raid1_rmeta_1] vg ewi---r-p 1 linear 4.00m
A new reportable field has been added for writebehind as well. If
write-behind has not been set or the LV is not RAID1, the field will
be blank.
Example (writebehind is set):
[~]# lvs -a -o name,attr,writebehind vg
LV Attr WBehind
lv rwi-a-r-- 512
[lv_rimage_0] iwi-aor-w
[lv_rimage_1] iwi-aor--
[lv_rmeta_0] ewi-aor--
[lv_rmeta_1] ewi-aor--
Example (writebehind is not set):
[~]# lvs -a -o name,attr,writebehind vg
LV Attr WBehind
lv rwi-a-r--
[lv_rimage_0] iwi-aor-w
[lv_rimage_1] iwi-aor--
[lv_rmeta_0] ewi-aor--
[lv_rmeta_1] ewi-aor--
---
WHATS_NEW | 1 +
lib/format_text/flags.c | 1 +
lib/metadata/lv.c | 8 ++-
lib/metadata/lv_manip.c | 85 ++++++++++++++++++++++++
lib/metadata/metadata-exported.h | 8 ++
lib/metadata/raid_manip.c | 94 +++------------------------
lib/raid/raid.c | 43 ++++++++++++-
lib/report/columns.h | 1 +
lib/report/properties.c | 6 ++
lib/report/report.c | 18 +++++-
libdm/libdevmapper.h | 30 +++++++++
libdm/libdm-deptree.c | 58 +++++++++++++----
man/lvchange.8.in | 21 ++++++
man/lvs.8.in | 6 +-
scripts/gdbinit | 5 ++
test/shell/lvchange-raid.sh | 99 ++++++++++++++++++++++++++++-
tools/args.h | 2 +
tools/commands.h | 4 +-
tools/lvchange.c | 132 ++++++++++++++++++++++++++++++++++++++
19 files changed, 514 insertions(+), 108 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 31c33fe..76347e9 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.99 -
===================================
+ Add writemostly/writebehind support for RAID1
Add lv_change_activate() for common activation code in vg/lvchange.
Revert change that allowed identical table reload for RAID.
New lvchange arg, '--syncaction' allows scrubbing of RAID LVs.
diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c
index 2a6b7a5..28a7dc4 100644
--- a/lib/format_text/flags.c
+++ b/lib/format_text/flags.c
@@ -58,6 +58,7 @@ static const struct flag _lv_flags[] = {
{LOCKED, "LOCKED", STATUS_FLAG},
{LV_NOTSYNCED, "NOTSYNCED", STATUS_FLAG},
{LV_REBUILD, "REBUILD", STATUS_FLAG},
+ {LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG},
{RAID, NULL, 0},
{RAID_META, NULL, 0},
{RAID_IMAGE, NULL, 0},
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index 5cb87c3..6afa468 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -604,9 +604,11 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
uint64_t n;
if (!_lv_raid_healthy(lv))
repstr[8] = 'r'; /* RAID needs 'r'efresh */
- else if ((lv->status & RAID) &&
- lv_raid_mismatch_count(lv, &n) && n)
- repstr[8] = 'm'; /* RAID contains 'm'ismatches */
+ else if (lv->status & RAID) {
+ if (lv_raid_mismatch_count(lv, &n) && n)
+ repstr[8] = 'm'; /* RAID has 'm'ismatches */
+ } else if (lv->status & LV_WRITEMOSTLY)
+ repstr[8] = 'w'; /* sub-LV has 'w'ritemostly */
}
out:
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 10331c3..fc316e8 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -73,6 +73,91 @@ struct lv_names {
};
/*
+ * lv_is_on_pv
+ * @lv:
+ * @pv:
+ *
+ * If any of the component devices of the LV are on the given PV, 1
+ * is returned; otherwise 0. For example if one of the images of a RAID
+ * (or its metadata device) is on the PV, 1 would be returned for the
+ * top-level LV.
+ * If you wish to check the images themselves, you should pass them.
+ *
+ * FIXME: This should be made more generic, possibly use 'for_each_sub_lv'.
+ * 'for_each_sub_lv' does not yet allow us to short-circuit execution or
+ * pass back the values we need yet though...
+ *
+ * Returns: 1 if LV (or part of LV) is on PV, 0 otherwise
+ */
+int lv_is_on_pv(struct logical_volume *lv, struct physical_volume *pv)
+{
+ uint32_t s;
+ struct physical_volume *pv2;
+ struct lv_segment *seg;
+
+ if (!lv)
+ return 0;
+
+ seg = first_seg(lv);
+ if (!seg)
+ return 0;
+
+ /* Check mirror log */
+ if (lv_is_on_pv(seg->log_lv, pv))
+ return 1;
+
+ /* Check stack of LVs */
+ dm_list_iterate_items(seg, &lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) == AREA_PV) {
+ pv2 = seg_pv(seg, s);
+ if (id_equal(&pv->id, &pv2->id))
+ return 1;
+ if (pv->dev && pv2->dev &&
+ (pv->dev->dev == pv2->dev->dev))
+ return 1;
+ }
+
+ if ((seg_type(seg, s) == AREA_LV) &&
+ lv_is_on_pv(seg_lv(seg, s), pv))
+ return 1;
+
+ if (!seg_is_raid(seg))
+ continue;
+
+ /* This is RAID, so we know the meta_area is AREA_LV */
+ if (lv_is_on_pv(seg_metalv(seg, s), pv))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * lv_is_on_pvs
+ * @lv
+ * @pvs
+ *
+ * Returns 1 if the LV (or part of the LV) is on any of the pvs
+ * in the list, 0 otherwise.
+ */
+int lv_is_on_pvs(struct logical_volume *lv, struct dm_list *pvs)
+{
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, pvs)
+ if (lv_is_on_pv(lv, pvl->pv)) {
+ log_debug_metadata("%s is on %s", lv->name,
+ pv_dev_name(pvl->pv));
+ return 1;
+ } else
+ log_debug_metadata("%s is not on %s", lv->name,
+ pv_dev_name(pvl->pv));
+ return 0;
+}
+
+/*
* get_default_region_size
* @cmd
*
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index df21759..a4ffe26 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -90,6 +90,8 @@
#define THIN_POOL_DATA UINT64_C(0x0000004000000000) /* LV */
#define THIN_POOL_METADATA UINT64_C(0x0000008000000000) /* LV */
+#define LV_WRITEMOSTLY UINT64_C(0x0000010000000000) /* LV (RAID1) */
+
#define LVM_READ UINT64_C(0x00000100) /* LV, VG */
#define LVM_WRITE UINT64_C(0x00000200) /* LV, VG */
@@ -334,6 +336,7 @@ struct lv_segment {
/* FIXME Fields depend on segment type */
uint32_t stripe_size; /* For stripe and RAID - in sectors */
+ uint32_t writebehind; /* For RAID (RAID1 only) */
uint32_t area_count;
uint32_t area_len;
uint32_t chunk_size; /* For snapshots/thin_pool. In sectors. */
@@ -696,6 +699,11 @@ const char *find_vgname_from_pvname(struct cmd_context *cmd,
const char *pvname);
const char *find_vgname_from_pvid(struct cmd_context *cmd,
const char *pvid);
+
+int lv_is_on_pv(struct logical_volume *lv, struct physical_volume *pv);
+int lv_is_on_pvs(struct logical_volume *lv, struct dm_list *pvs);
+
+
/* Find LV segment containing given LE */
struct lv_segment *first_seg(const struct logical_volume *lv);
struct lv_segment *last_seg(const struct logical_volume *lv);
diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c
index 43cf14e..38ea2c2 100644
--- a/lib/metadata/raid_manip.c
+++ b/lib/metadata/raid_manip.c
@@ -93,81 +93,6 @@ static int _activate_sublv_preserving_excl(struct logical_volume *top_lv,
return 1;
}
-/*
- * _lv_is_on_pv
- * @lv:
- * @pv:
- *
- * If any of the component devices of the LV are on the given PV, 1
- * is returned; otherwise 0. For example if one of the images of a RAID
- * (or its metadata device) is on the PV, 1 would be returned for the
- * top-level LV.
- * If you wish to check the images themselves, you should pass them.
- *
- * FIXME: This should be made more generic, possibly use 'for_each_sub_lv',
- * and be put in lv_manip.c. 'for_each_sub_lv' does not yet allow us to
- * short-circuit execution or pass back the values we need yet though...
- */
-static int _lv_is_on_pv(struct logical_volume *lv, struct physical_volume *pv)
-{
- uint32_t s;
- struct physical_volume *pv2;
- struct lv_segment *seg;
-
- if (!lv)
- return 0;
-
- seg = first_seg(lv);
- if (!seg)
- return 0;
-
- /* Check mirror log */
- if (_lv_is_on_pv(seg->log_lv, pv))
- return 1;
-
- /* Check stack of LVs */
- dm_list_iterate_items(seg, &lv->segments) {
- for (s = 0; s < seg->area_count; s++) {
- if (seg_type(seg, s) == AREA_PV) {
- pv2 = seg_pv(seg, s);
- if (id_equal(&pv->id, &pv2->id))
- return 1;
- if (pv->dev && pv2->dev &&
- (pv->dev->dev == pv2->dev->dev))
- return 1;
- }
-
- if ((seg_type(seg, s) == AREA_LV) &&
- _lv_is_on_pv(seg_lv(seg, s), pv))
- return 1;
-
- if (!seg_is_raid(seg))
- continue;
-
- /* This is RAID, so we know the meta_area is AREA_LV */
- if (_lv_is_on_pv(seg_metalv(seg, s), pv))
- return 1;
- }
- }
-
- return 0;
-}
-
-static int _lv_is_on_pvs(struct logical_volume *lv, struct dm_list *pvs)
-{
- struct pv_list *pvl;
-
- dm_list_iterate_items(pvl, pvs)
- if (_lv_is_on_pv(lv, pvl->pv)) {
- log_debug_metadata("%s is on %s", lv->name,
- pv_dev_name(pvl->pv));
- return 1;
- } else
- log_debug_metadata("%s is not on %s", lv->name,
- pv_dev_name(pvl->pv));
- return 0;
-}
-
static int _get_pv_list_for_lv(struct logical_volume *lv, struct dm_list *pvs)
{
uint32_t s;
@@ -1009,8 +934,8 @@ static int _raid_extract_images(struct logical_volume *lv, uint32_t new_count,
seg_metalv(seg, s)->name, seg_lv(seg, s)->name);
} else {
/* Conditions for second pass */
- if (!_lv_is_on_pvs(seg_lv(seg, s), target_pvs) ||
- !_lv_is_on_pvs(seg_metalv(seg, s), target_pvs))
+ if (!lv_is_on_pvs(seg_lv(seg, s), target_pvs) ||
+ !lv_is_on_pvs(seg_metalv(seg, s), target_pvs))
continue;
if (!_raid_in_sync(lv) &&
@@ -1069,7 +994,8 @@ static int _raid_remove_images(struct logical_volume *lv,
" after linear conversion");
return 0;
}
- lv->status &= ~LV_NOTSYNCED;
+ lv->status &= ~(LV_NOTSYNCED | LV_WRITEMOSTLY);
+ first_seg(lv)->writebehind = 0;
}
if (!vg_write(lv->vg)) {
@@ -1211,7 +1137,7 @@ int lv_raid_split(struct logical_volume *lv, const char *split_name,
* complete the split of the tracking sub-LV
*/
if (_lv_is_raid_with_tracking(lv, &tracking)) {
- if (!_lv_is_on_pvs(tracking, splittable_pvs)) {
+ if (!lv_is_on_pvs(tracking, splittable_pvs)) {
log_error("Unable to split additional image from %s "
"while tracking changes for %s",
lv->name, tracking->name);
@@ -1344,7 +1270,7 @@ int lv_raid_split_and_track(struct logical_volume *lv,
}
for (s = seg->area_count - 1; s >= 0; s--) {
- if (!_lv_is_on_pvs(seg_lv(seg, s), splittable_pvs))
+ if (!lv_is_on_pvs(seg_lv(seg, s), splittable_pvs))
continue;
lv_set_visible(seg_lv(seg, s));
seg_lv(seg, s)->status &= ~LVM_WRITE;
@@ -1677,8 +1603,8 @@ int lv_raid_replace(struct logical_volume *lv,
if (lv_is_virtual(seg_lv(raid_seg, s)) ||
lv_is_virtual(seg_metalv(raid_seg, s)) ||
- _lv_is_on_pvs(seg_lv(raid_seg, s), remove_pvs) ||
- _lv_is_on_pvs(seg_metalv(raid_seg, s), remove_pvs))
+ lv_is_on_pvs(seg_lv(raid_seg, s), remove_pvs) ||
+ lv_is_on_pvs(seg_metalv(raid_seg, s), remove_pvs))
match_count++;
}
@@ -1706,8 +1632,8 @@ int lv_raid_replace(struct logical_volume *lv,
s = i % raid_seg->area_count;
if (!(i % copies))
rebuilds_per_group = 0;
- if (_lv_is_on_pvs(seg_lv(raid_seg, s), remove_pvs) ||
- _lv_is_on_pvs(seg_metalv(raid_seg, s), remove_pvs) ||
+ if (lv_is_on_pvs(seg_lv(raid_seg, s), remove_pvs) ||
+ lv_is_on_pvs(seg_metalv(raid_seg, s), remove_pvs) ||
lv_is_virtual(seg_lv(raid_seg, s)) ||
lv_is_virtual(seg_metalv(raid_seg, s)))
rebuilds_per_group++;
diff --git a/lib/raid/raid.c b/lib/raid/raid.c
index 1e28c73..7f453f9 100644
--- a/lib/raid/raid.c
+++ b/lib/raid/raid.c
@@ -121,6 +121,14 @@ static int _raid_text_import(struct lv_segment *seg,
return 0;
}
}
+ if (dm_config_has_node(sn, "writebehind")) {
+ if (!dm_config_get_uint32(sn, "writebehind", &seg->writebehind)) {
+ log_error("Couldn't read 'writebehind' for "
+ "segment %s of logical volume %s.",
+ dm_config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+ }
if (!dm_config_get_list(sn, "raids", &cv)) {
log_error("Couldn't find RAID array for "
"segment %s of logical volume %s.",
@@ -145,6 +153,8 @@ static int _raid_text_export(const struct lv_segment *seg, struct formatter *f)
outf(f, "region_size = %" PRIu32, seg->region_size);
if (seg->stripe_size)
outf(f, "stripe_size = %" PRIu32, seg->stripe_size);
+ if (seg->writebehind)
+ outf(f, "writebehind = %" PRIu32, seg->writebehind);
return out_areas(f, seg, "raid");
}
@@ -161,6 +171,10 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
uint32_t s;
uint64_t flags = 0;
uint64_t rebuilds = 0;
+ uint64_t writemostly = 0;
+ struct dm_tree_node_raid_params params;
+
+ memset(¶ms, 0, sizeof(params));
if (!seg->area_count) {
log_error(INTERNAL_ERROR "_raid_add_target_line called "
@@ -187,12 +201,35 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
if (seg_lv(seg, s)->status & LV_REBUILD)
rebuilds |= 1 << s;
+ for (s = 0; s < seg->area_count; s++)
+ if (seg_lv(seg, s)->status & LV_WRITEMOSTLY)
+ writemostly |= 1 << s;
+
if (mirror_in_sync())
flags = DM_NOSYNC;
- if (!dm_tree_node_add_raid_target(node, len, _raid_name(seg),
- seg->region_size, seg->stripe_size,
- rebuilds, flags))
+ params.raid_type = _raid_name(seg);
+ if (seg->segtype->parity_devs) {
+ /* RAID 4/5/6 */
+ params.mirrors = 1;
+ params.stripes = seg->area_count - seg->segtype->parity_devs;
+ } else if (strcmp(seg->segtype->name, "raid10")) {
+ /* RAID 10 only supports 2 mirrors now */
+ params.mirrors = 2;
+ params.stripes = seg->area_count / 2;
+ } else {
+ /* RAID 1 */
+ params.mirrors = seg->area_count;
+ params.stripes = 1;
+ params.writebehind = seg->writebehind;
+ }
+ params.region_size = seg->region_size;
+ params.stripe_size = seg->stripe_size;
+ params.rebuilds = rebuilds;
+ params.writemostly = writemostly;
+ params.flags = flags;
+
+ if (!dm_tree_node_add_raid_target_with_params(node, len, ¶ms))
return_0;
return add_areas_line(dm, seg, node, 0u, seg->area_count);
diff --git a/lib/report/columns.h b/lib/report/columns.h
index b6dc483..42858f1 100644
--- a/lib/report/columns.h
+++ b/lib/report/columns.h
@@ -82,6 +82,7 @@ FIELD(LVS, lv, NUM, "Cpy%Sync", lvid, 8, copypercent, copy_percent, "For RAID, m
FIELD(LVS, lv, NUM, "Cpy%Sync", lvid, 8, copypercent, sync_percent, "For RAID, mirrors and pvmove, current percentage in-sync.", 0)
FIELD(LVS, lv, NUM, "Mismatches", lvid, 10, mismatch_count, mismatches, "For RAID, number of mismatches found or repaired.", 0)
FIELD(LVS, lv, STR, "SyncAction", lvid, 10, sync_action, syncaction, "For RAID, the current synchronization action being performed.", 0)
+FIELD(LVS, lv, NUM, "WBehind", lvid, 7, write_behind, writebehind, "For RAID1, the number of outstanding writes allowed to writemostly devices.", 0)
FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, move_pv, "For pvmove, Source PV of temporary LV created by pvmove.", 0)
FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, convert_lv, "For lvconvert, Name of temporary LV created by lvconvert.", 0)
FIELD(LVS, lv, STR, "Log", lvid, 3, loglv, mirror_log, "For mirrors, the LV holding the synchronisation log.", 0)
diff --git a/lib/report/properties.c b/lib/report/properties.c
index c04a6a1..71b3cb1 100644
--- a/lib/report/properties.c
+++ b/lib/report/properties.c
@@ -109,6 +109,10 @@ static char *_sync_action(const struct logical_volume *lv) {
return action;
}
+static uint32_t _writebehind(const struct logical_volume *lv) {
+ return first_seg(lv)->writebehind;
+}
+
static percent_t _snap_percent(const struct logical_volume *lv) {
percent_t perc;
@@ -213,6 +217,8 @@ GET_LV_NUM_PROPERTY_FN(sync_percent, _copy_percent(lv))
#define _sync_percent_set _not_implemented_set
GET_LV_NUM_PROPERTY_FN(mismatches, _mismatches(lv))
#define _mismatches_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(writebehind, _writebehind(lv))
+#define _writebehind_set _not_implemented_set
GET_LV_STR_PROPERTY_FN(syncaction, _sync_action(lv))
#define _syncaction_set _not_implemented_set
GET_LV_STR_PROPERTY_FN(move_pv, lv_move_pv_dup(lv->vg->vgmem, lv))
diff --git a/lib/report/report.c b/lib/report/report.c
index 4b0ebef..8578ff7 100644
--- a/lib/report/report.c
+++ b/lib/report/report.c
@@ -969,7 +969,23 @@ static int _mismatch_count_disp(struct dm_report *rh __attribute__((unused)),
return 1;
}
- return dm_report_field_uint64(rh, field, &mismatch_count);
+ return dm_report_field_uint64(rh, field, &mismatch_count);
+}
+
+static int _write_behind_disp(struct dm_report *rh __attribute__((unused)),
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+
+ if (!lv_is_raid_type(lv) || !first_seg(lv)->writebehind) {
+ dm_report_field_set_value(field, "", NULL);
+ return 1;
+ }
+
+ return dm_report_field_uint32(rh, field, &first_seg(lv)->writebehind);
}
static int _dtpercent_disp(int metadata, struct dm_report *rh,
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index b3ba24d..b729846 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -643,6 +643,36 @@ int dm_tree_node_add_raid_target(struct dm_tree_node *node,
uint64_t rebuilds,
uint64_t flags);
+struct dm_tree_node_raid_params {
+ const char *raid_type;
+
+ uint32_t stripes;
+ uint32_t mirrors;
+ uint32_t region_size;
+ uint32_t stripe_size;
+
+ /*
+ * 'rebuilds' and 'writemostly' are bitfields that signify
+ * which devices in the array are to be rebuilt or marked
+ * writemostly. By choosing a 'uint64_t', we limit ourself
+ * to RAID arrays with 64 devices.
+ */
+ uint64_t rebuilds;
+ uint64_t writemostly;
+ uint32_t writebehind; /* I/Os (kernel default COUNTER_MAX / 2) */
+ uint32_t sync_daemon_sleep; /* ms (kernel default = 5sec) */
+ uint32_t max_recovery_rate; /* kB/sec/disk */
+ uint32_t min_recovery_rate; /* kB/sec/disk */
+ uint32_t stripe_cache; /* sectors */
+
+ uint64_t flags; /* [no]sync */
+ uint64_t reserved2;
+};
+
+int dm_tree_node_add_raid_target_with_params(struct dm_tree_node *node,
+ uint64_t size,
+ struct dm_tree_node_raid_params *p);
+
/*
* Replicator operation mode
* Note: API for Replicator is not yet stable
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index 9f82174..b084bc0 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -184,6 +184,8 @@ struct load_segment {
uint64_t rdevice_index; /* Replicator-dev */
uint64_t rebuilds; /* raid */
+ uint64_t writemostly; /* raid */
+ uint32_t writebehind; /* raid */
struct dm_tree_node *metadata; /* Thin_pool */
struct dm_tree_node *pool; /* Thin_pool, Thin */
@@ -2128,10 +2130,17 @@ static int _raid_emit_segment_line(struct dm_task *dmt, uint32_t major,
if (seg->region_size)
param_count += 2;
+ if (seg->writebehind)
+ param_count += 2;
+
/* rebuilds is 64-bit */
param_count += 2 * hweight32(seg->rebuilds & 0xFFFFFFFF);
param_count += 2 * hweight32(seg->rebuilds >> 32);
+ /* rebuilds is 64-bit */
+ param_count += 2 * hweight32(seg->writemostly & 0xFFFFFFFF);
+ param_count += 2 * hweight32(seg->writemostly >> 32);
+
if ((seg->type == SEG_RAID1) && seg->stripe_size)
log_error("WARNING: Ignoring RAID1 stripe size");
@@ -2150,6 +2159,13 @@ static int _raid_emit_segment_line(struct dm_task *dmt, uint32_t major,
if (seg->rebuilds & (1 << i))
EMIT_PARAMS(pos, " rebuild %u", i);
+ for (i = 0; i < (seg->area_count / 2); i++)
+ if (seg->writemostly & (1 << i))
+ EMIT_PARAMS(pos, " write_mostly %u", i);
+
+ if (seg->writebehind)
+ EMIT_PARAMS(pos, " writebehind %u", seg->writebehind);
+
/* Print number of metadata/data device pairs */
EMIT_PARAMS(pos, " %u", seg->area_count/2);
@@ -2826,19 +2842,15 @@ int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
return 1;
}
-int dm_tree_node_add_raid_target(struct dm_tree_node *node,
- uint64_t size,
- const char *raid_type,
- uint32_t region_size,
- uint32_t stripe_size,
- uint64_t rebuilds,
- uint64_t flags)
+int dm_tree_node_add_raid_target_with_params(struct dm_tree_node *node,
+ uint64_t size,
+ struct dm_tree_node_raid_params *p)
{
int i;
struct load_segment *seg = NULL;
for (i = 0; dm_segtypes[i].target && !seg; i++)
- if (!strcmp(raid_type, dm_segtypes[i].target))
+ if (!strcmp(p->raid_type, dm_segtypes[i].target))
if (!(seg = _add_segment(node,
dm_segtypes[i].type, size)))
return_0;
@@ -2846,15 +2858,37 @@ int dm_tree_node_add_raid_target(struct dm_tree_node *node,
if (!seg)
return_0;
- seg->region_size = region_size;
- seg->stripe_size = stripe_size;
+ seg->region_size = p->region_size;
+ seg->stripe_size = p->stripe_size;
seg->area_count = 0;
- seg->rebuilds = rebuilds;
- seg->flags = flags;
+ seg->rebuilds = p->rebuilds;
+ seg->writemostly = p->writemostly;
+ seg->writebehind = p->writebehind;
+ seg->flags = p->flags;
return 1;
}
+int dm_tree_node_add_raid_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *raid_type,
+ uint32_t region_size,
+ uint32_t stripe_size,
+ uint64_t rebuilds,
+ uint64_t flags)
+{
+ struct dm_tree_node_raid_params params;
+
+ memset(¶ms, 0, sizeof(params));
+ params.raid_type = raid_type;
+ params.region_size = region_size;
+ params.stripe_size = stripe_size;
+ params.rebuilds = rebuilds;
+ params.flags = flags;
+
+ return dm_tree_node_add_raid_target_with_params(node, size, ¶ms);
+}
+
/*
* Various RAID status versions include:
diff --git a/man/lvchange.8.in b/man/lvchange.8.in
index 295eea2..80747d3 100644
--- a/man/lvchange.8.in
+++ b/man/lvchange.8.in
@@ -42,6 +42,8 @@ lvchange \- change attributes of a logical volume
.RB [ \-\-refresh ]
.RB [ \-t | \-\-test ]
.RB [ \-v | \-\-verbose ]
+.RB [ \-\-writebehind BehindCount ]
+.RB [ \-\-writemostly PhysicalVolume ]
.RB [ \-Z | \-\-zero
.RI { y | n }]
.I LogicalVolumePath
@@ -169,6 +171,25 @@ This is not necessary in normal operation, but may be useful
if something has gone wrong or if you're doing clustering
manually without a clustered lock manager.
.TP
+.BR \-\-writebehind " BehindCount"
+Specify the maximum number of outstanding writes that are allowed to
+devices in a RAID 1 logical volume that are marked as \fIwrite-mostly\fP.
+Once this value is exceeded, writes become synchronous (i.e. all writes
+to the constituent devices must complete before the array signals the
+write has completed). Setting the value to zero clears the preference
+and allows the system to choose the value arbitrarily.
+.TP
+.BR \-\-writemostly " PhysicalVolume[:{t|y|n}]"
+Mark a device in a RAID1 logical volume as \fIwrite-mostly\fP. All reads
+to these drives will be avoided unless absolutely necessary. This keeps
+the number of I/Os to the drive to a minimum. The default behavior is to
+set the write-mostly attribute for the specified physical volume in the
+logical volume. It is possible to also remove the write-mostly flag by
+appending a ":n" to the physical volume or to toggle the value by specifying
+":t". The \fI--writemostly\fP argument can be specified more than one time
+in a single command; making it possible to toggle the write-mostly attributes
+for all the physical volumes in a logical volume at once.
+.TP
.BR \-Z ", " \-\-zero " {" \fIy | \fIn }
Set zeroing mode for thin pool. Note: already provisioned blocks from pool
in non-zero mode are not cleared in unwritten parts when setting zero to
diff --git a/man/lvs.8.in b/man/lvs.8.in
index 727353b..1626e17 100644
--- a/man/lvs.8.in
+++ b/man/lvs.8.in
@@ -118,6 +118,7 @@ sync_action,
sync_percent,
thin_count,
transaction_id,
+writebehind,
zero.
.IP
With \fB\-\-segments\fP, any "seg_" prefixes are optional;
@@ -161,7 +162,7 @@ snapshots of thin volumes using the new thin provisioning driver appear as (t).
.IP 8 3
Newly-allocated data blocks are overwritten with blocks of (z)eroes before use.
.IP 9 3
-Volume Health: (p)artial, (r)efresh needed, (m)ismatches exist.
+Volume Health: (p)artial, (r)efresh needed, (m)ismatches exist, (w)ritemostly.
(p)artial signifies that one or more of the Physical Volumes this Logical
Volume uses is missing from the system. (r)efresh signifies that one or
more of the Physical Volumes this RAID Logical Volume uses had suffered a
@@ -172,7 +173,8 @@ has portions of the array that are not coherent or that the array has
recently repaired inconsistencies. An additional "check" after a "repair"
of a RAID logical volume will clear this flag if no additional discrepancies
are found. ("check" and "repair" of a RAID Logical Volume can be done via
-the 'lvchange' command.)
+the 'lvchange' command.) (w)ritemostly signifies the devices in a RAID 1
+logical volume that have been marked write-mostly.
.RE
.TP
.BR \-O ", " \-\-sort
diff --git a/scripts/gdbinit b/scripts/gdbinit
index fa58948..83d1c8e 100644
--- a/scripts/gdbinit
+++ b/scripts/gdbinit
@@ -324,6 +324,11 @@ define __status
set $_s_status = $_s_status & ~0x10000000U
printf " MERGING"
end
+# if ($_s_status & LV_WRITEMOSTLY)
+ if ($_s_status & 0x10000000000U)
+ set $_s_status = $_s_status & ~0x10000000000U
+ printf " LV_WRITEMOSTLY"
+ end
if ($_s_status)
printf " 0x%x", $_s_status
diff --git a/test/shell/lvchange-raid.sh b/test/shell/lvchange-raid.sh
index a1c9540..0ef00b3 100644
--- a/test/shell/lvchange-raid.sh
+++ b/test/shell/lvchange-raid.sh
@@ -14,11 +14,102 @@
. lib/test
-# dm-raid v1.5.0+ contains RAID scrubbing support
-aux target_at_least dm-raid 1 5 0 || skip
+# dm-raid v1.4.1+ contains RAID10 support
+aux target_at_least dm-raid 1 4 1 || skip
aux prepare_vg 5
+# run_writemostly_check <VG> <LV>
+run_writemostly_check() {
+ d0=`lvs -a --noheadings -o devices $1/${2}_rimage_0 | sed s/\(.\)//`
+ d0=$(sed s/^[[:space:]]*// <<< "$d0")
+ d1=`lvs -a --noheadings -o devices $1/${2}_rimage_1 | sed s/\(.\)//`
+ d1=$(sed s/^[[:space:]]*// <<< "$d1")
+
+ # No writemostly flag should be there yet.
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*-$'
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_1 | grep '.*-$'
+
+ if [ `lvs --noheadings -o segtype $1/$2` != "raid1" ]; then
+ not lvchange --writemostly $d0 $1/$2
+ return
+ fi
+
+ # Set the flag
+ lvchange --writemostly $d0 $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+
+ # Running again should leave it set (not toggle)
+ lvchange --writemostly $d0 $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+
+ # Running again with ':y' should leave it set
+ lvchange --writemostly $d0:y $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+
+ # ':n' should unset it
+ lvchange --writemostly $d0:n $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*-$'
+
+ # ':n' again should leave it unset
+ lvchange --writemostly $d0:n $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*-$'
+
+ # ':t' toggle to set
+ lvchange --writemostly $d0:t $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+
+ # ':t' toggle to unset
+ lvchange --writemostly $d0:t $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*-$'
+
+ # ':y' to set
+ lvchange --writemostly $d0:y $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+
+ # Toggle both at once
+ lvchange --writemostly $d0:t --writemostly $d1:t $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*-$'
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_1 | grep '.*w$'
+
+ # Toggle both at once again
+ lvchange --writemostly $d0:t --writemostly $d1:t $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_1 | grep '.*-$'
+
+ # Toggle one, unset the other
+ lvchange --writemostly $d0:n --writemostly $d1:t $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*-$'
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_1 | grep '.*w$'
+
+ # Toggle one, set the other
+ lvchange --writemostly $d0:y --writemostly $d1:t $1/$2
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_1 | grep '.*-$'
+
+ # Partial flag supercedes writemostly flag
+ aux disable_dev $d0
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*p$'
+ aux enable_dev $d0
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*w$'
+
+ # Catch Bad writebehind values
+ not lvchange --writebehind "invalid" $1/$2
+ not lvchange --writebehind -256 $1/$2
+
+ # Set writebehind
+ [ ! `lvs --noheadings -o writebehind $1/$2` ]
+ lvchange --writebehind 512 $1/$2
+ [ `lvs --noheadings -o writebehind $1/$2` -eq 512 ]
+
+ # Converting to linear should clear flags and writebehind
+ lvconvert -m 0 $1/$2 $d1
+ lvconvert --type raid1 -m 1 $1/$2 $d1
+ [ ! `lvs --noheadings -o writebehind $1/$2` ]
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_0 | grep '.*-$'
+ lvs -a --noheadings -o lv_attr $1/${2}_rimage_1 | grep '.*-$'
+}
+
# run_syncaction_check <VG> <LV>
run_syncaction_check() {
local device
@@ -109,6 +200,10 @@ run_refresh_check() {
}
run_checks() {
+ if aux target_at_least dm-raid 1 1 0; then
+ run_writemostly_check $1 $2
+ fi
+
if aux target_at_least dm-raid 1 5 0; then
run_syncaction_check $1 $2
fi
diff --git a/tools/args.h b/tools/args.h
index 81793a5..bfcc5b9 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -87,6 +87,8 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", NULL, 0)
arg(atversion_ARG, '\0', "atversion", string_arg, 0)
arg(validate_ARG, '\0', "validate", NULL, 0)
arg(syncaction_ARG, '\0', "syncaction", string_arg, 0)
+arg(writemostly_ARG, '\0', "writemostly", string_arg, ARG_GROUPABLE)
+arg(writebehind_ARG, '\0', "writebehind", int_arg, 0)
/* Allow some variations */
arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0)
diff --git a/tools/commands.h b/tools/commands.h
index 3124a13..ace077f 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -96,6 +96,8 @@ xx(lvchange,
"\t[-v|--verbose]\n"
"\t[-y|--yes]\n"
"\t[--version]\n"
+ "\t[--writebehind BehindCount\n"
+ "\t[--writemostly PhysicalVolume]\n"
"\t[-Z|--zero {y|n}]\n"
"\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
@@ -104,7 +106,7 @@ xx(lvchange,
major_ARG, minor_ARG, monitor_ARG, noudevsync_ARG, partial_ARG,
permission_ARG, persistent_ARG, poll_ARG, readahead_ARG, resync_ARG,
refresh_ARG, addtag_ARG, deltag_ARG, syncaction_ARG, sysinit_ARG, test_ARG,
- yes_ARG, zero_ARG)
+ yes_ARG, writebehind_ARG, writemostly_ARG, zero_ARG)
xx(lvconvert,
"Change logical volume layout",
diff --git a/tools/lvchange.c b/tools/lvchange.c
index 834ae0f..7b0a517 100644
--- a/tools/lvchange.c
+++ b/tools/lvchange.c
@@ -699,6 +699,125 @@ static int lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv, int
return 1;
}
+static int lvchange_writemostly(struct logical_volume *lv)
+{
+ int s, pv_count, i = 0;
+ char **pv_names;
+ const char *tmp_str;
+ struct pv_list *pvl;
+ struct arg_value_group_list *group;
+ struct cmd_context *cmd = lv->vg->cmd;
+ struct lv_segment *raid_seg = first_seg(lv);
+
+ if (strcmp(raid_seg->segtype->name, "raid1")) {
+ log_error("--write%s can only be used with 'raid1' segment type",
+ arg_count(cmd, writemostly_ARG) ? "mostly" : "behind");
+ return 0;
+ }
+
+ if (arg_count(cmd, writebehind_ARG))
+ raid_seg->writebehind = arg_uint_value(cmd, writebehind_ARG, 0);
+
+ if (arg_count(cmd, writemostly_ARG)) {
+ /* writemostly can be specified more than once */
+ pv_count = arg_count(cmd, writemostly_ARG);
+ pv_names = dm_pool_alloc(cmd->mem, sizeof(char *) * pv_count);
+ if (!pv_names)
+ return_0;
+
+ dm_list_iterate_items(group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(group->arg_values,
+ writemostly_ARG))
+ continue;
+
+ if (!(tmp_str = grouped_arg_str_value(group->arg_values,
+ writemostly_ARG,
+ NULL)))
+ return_0;
+
+ /*
+ * Writemostly PV specifications can be:
+ * <PV> - Turn on writemostly
+ * <PV>:t - Toggle writemostly
+ * <PV>:n - Turn off writemostly
+ * <PV>:y - Turn on writemostly
+ *
+ * We allocate strlen + 3 to add our own ':{t|n|y}' if
+ * not present plus the trailing '\0'.
+ */
+ if (!(pv_names[i] = dm_pool_zalloc(cmd->mem,
+ strlen(tmp_str) + 3)))
+ return_0;
+
+ if ((tmp_str[strlen(tmp_str) - 2] != ':') &&
+ ((tmp_str[strlen(tmp_str) - 1] != 't') ||
+ (tmp_str[strlen(tmp_str) - 1] != 'y') ||
+ (tmp_str[strlen(tmp_str) - 1] != 'n')))
+ /* Default to 'y' if no mode specified */
+ sprintf(pv_names[i], "%s:y", tmp_str);
+ else
+ sprintf(pv_names[i], "%s", tmp_str);
+ i++;
+ }
+
+ for (i = 0; i < pv_count; i++)
+ pv_names[i][strlen(pv_names[i]) - 2] = '\0';
+
+ for (i = 0; i < pv_count; i++) {
+ if (!(pvl = find_pv_in_vg(lv->vg, pv_names[i]))) {
+ log_error("%s not found in volume group, %s",
+ pv_names[i], lv->vg->name);
+ return 0;
+ }
+
+ for (s = 0; s < raid_seg->area_count; s++) {
+ /*
+ * We don't bother checking the metadata area,
+ * since writemostly only affects the data areas.
+ */
+ if ((seg_type(raid_seg, s) == AREA_UNASSIGNED))
+ continue;
+
+ if (lv_is_on_pv(seg_lv(raid_seg, s), pvl->pv)) {
+ if (pv_names[i][strlen(pv_names[i]) + 1] == 'y')
+ seg_lv(raid_seg, s)->status |=
+ LV_WRITEMOSTLY;
+ else if (pv_names[i][strlen(pv_names[i]) + 1] == 'n')
+ seg_lv(raid_seg, s)->status &=
+ ~LV_WRITEMOSTLY;
+ else if (pv_names[i][strlen(pv_names[i]) + 1] == 't')
+ seg_lv(raid_seg, s)->status ^=
+ LV_WRITEMOSTLY;
+ else
+ return_0;
+ }
+ }
+ }
+ }
+
+ if (!vg_write(lv->vg))
+ return_0;
+
+ if (!suspend_lv(cmd, lv)) {
+ vg_revert(lv->vg);
+ return_0;
+ }
+
+ if (!vg_commit(lv->vg)) {
+ if (!resume_lv(cmd, lv))
+ stack;
+ return_0;
+ }
+
+ log_very_verbose("Updating writemostly for \"%s\" in kernel", lv->name);
+ if (!resume_lv(cmd, lv)) {
+ log_error("Problem reactivating %s", lv->name);
+ return 0;
+ }
+
+ return 1;
+}
+
static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
void *handle __attribute__((unused)))
{
@@ -870,6 +989,17 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
docmds++;
}
+ /* change writemostly/writebehind */
+ if (arg_count(cmd, writemostly_ARG) || arg_count(cmd, writebehind_ARG)) {
+ if (!archived && !archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ doit += lvchange_writemostly(lv);
+ docmds++;
+ }
+
if (doit)
log_print_unless_silent("Logical volume \"%s\" changed", lv->name);
@@ -945,6 +1075,8 @@ int lvchange(struct cmd_context *cmd, int argc, char **argv)
arg_count(cmd, alloc_ARG) ||
arg_count(cmd, discards_ARG) ||
arg_count(cmd, syncaction_ARG) ||
+ arg_count(cmd, writebehind_ARG) ||
+ arg_count(cmd, writemostly_ARG) ||
arg_count(cmd, zero_ARG);
int update = update_partial_safe || update_partial_unsafe;
11 years