summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2008-09-15 20:58:43 -0700
committerDan Williams <dan.j.williams@intel.com>2008-09-15 20:58:43 -0700
commit313a4a82f130e6668ba0f4550200662e168aa945 (patch)
tree56f6b17ba26b00684153bbbc976dd1c1f825b122
parent4795982e68f72ef44bd29c9883e5f6291088a3e1 (diff)
ping_manager() to prevent 'add' before 'remove' completes
It is currently possible to remove a device and re-add it without the manager noticing, i.e. without detecting a mdstat->devcnt container->devcnt mismatch. Introduce ping_manager() to arrange for mdmon to run manage_container() prior to mdadm dropping the exclusive open() on the container. Despite these precautions sysfs_read() may still fail. If this happens invalidate container->devcnt to ensure manage_container() runs at the next event. Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--Manage.c17
-rw-r--r--managemon.c17
-rw-r--r--msg.c33
-rw-r--r--msg.h1
4 files changed, 60 insertions, 8 deletions
diff --git a/Manage.c b/Manage.c
index 76447eda..5853515d 100644
--- a/Manage.c
+++ b/Manage.c
@@ -712,6 +712,23 @@ int Manage_subdevs(char *devname, int fd,
close(lfd);
return 1;
}
+ if (tst->ss->external) {
+ /*
+ * Before dropping our exclusive open we make an
+ * attempt at preventing mdmon from seeing an
+ * 'add' event before reconciling this 'remove'
+ * event.
+ */
+ char *name = devnum2devname(fd2devnum(fd));
+
+ if (!name) {
+ fprintf(stderr, Name ": unable to get container name\n");
+ return 1;
+ }
+
+ ping_manager(name);
+ free(name);
+ }
close(lfd);
if (verbose >= 0)
fprintf(stderr, Name ": hot removed %s\n",
diff --git a/managemon.c b/managemon.c
index fc34d778..fc5da713 100644
--- a/managemon.c
+++ b/managemon.c
@@ -265,8 +265,11 @@ static void manage_container(struct mdstat_ent *mdstat,
* These need to be remove from, or added to, the array
*/
mdi = sysfs_read(-1, mdstat->devnum, GET_DEVS);
- if (!mdi)
+ if (!mdi) {
+ /* invalidate the current count so we can try again */
+ container->devcnt = -1;
return;
+ }
/* check for removals */
for (cdp = &container->devs; *cdp; ) {
@@ -525,14 +528,15 @@ static void handle_message(struct supertype *container, struct metadata_update *
struct metadata_update *mu;
- if (msg->len == 0) {
- int cnt;
-
+ if (msg->len <= 0)
while (update_queue_pending || update_queue) {
check_update_queue(container);
usleep(15*1000);
}
+ if (msg->len == 0) { /* ping_monitor */
+ int cnt;
+
cnt = monitor_loop_cnt;
if (cnt & 1)
cnt += 2; /* wait until next pselect */
@@ -542,6 +546,11 @@ static void handle_message(struct supertype *container, struct metadata_update *
while (monitor_loop_cnt - cnt < 0)
usleep(10 * 1000);
+ } else if (msg->len == -1) { /* ping_manager */
+ struct mdstat_ent *mdstat = mdstat_read(1, 0);
+
+ manage(mdstat, container);
+ free_mdstat(mdstat);
} else {
mu = malloc(sizeof(*mu));
mu->len = msg->len;
diff --git a/msg.c b/msg.c
index 013bcb99..5a4839fa 100644
--- a/msg.c
+++ b/msg.c
@@ -81,12 +81,12 @@ static int recv_buf(int fd, void* buf, int len, int tmo)
int send_message(int fd, struct metadata_update *msg, int tmo)
{
- __u32 len = msg->len;
+ __s32 len = msg->len;
int rv;
rv = send_buf(fd, &start_magic, 4, tmo);
rv = rv ?: send_buf(fd, &len, 4, tmo);
- if (len)
+ if (len > 0)
rv = rv ?: send_buf(fd, msg->buf, msg->len, tmo);
rv = send_buf(fd, &end_magic, 4, tmo);
@@ -96,7 +96,7 @@ int send_message(int fd, struct metadata_update *msg, int tmo)
int receive_message(int fd, struct metadata_update *msg, int tmo)
{
__u32 magic;
- __u32 len;
+ __s32 len;
int rv;
rv = recv_buf(fd, &magic, 4, tmo);
@@ -105,7 +105,7 @@ int receive_message(int fd, struct metadata_update *msg, int tmo)
rv = recv_buf(fd, &len, 4, tmo);
if (rv < 0 || len > MSG_MAX_LEN)
return -1;
- if (len) {
+ if (len > 0) {
msg->buf = malloc(len);
if (msg->buf == NULL)
return -1;
@@ -177,6 +177,7 @@ int connect_monitor(char *devname)
return sfd;
}
+/* give the monitor a chance to update the metadata */
int ping_monitor(char *devname)
{
int sfd = connect_monitor(devname);
@@ -196,3 +197,27 @@ int ping_monitor(char *devname)
close(sfd);
return err;
}
+
+/* give the manager a chance to view the updated container state. This
+ * would naturally happen due to the manager noticing a change in
+ * /proc/mdstat; however, pinging encourages this detection to happen
+ * while an exclusive open() on the container is active
+ */
+int ping_manager(char *devname)
+{
+ int sfd = connect_monitor(devname);
+ struct metadata_update msg = { .len = -1 };
+ int err = 0;
+
+ if (sfd < 0)
+ return sfd;
+
+ err = send_message(sfd, &msg, 20);
+
+ /* check the reply */
+ if (!err && wait_reply(sfd, 20) != 0)
+ err = -1;
+
+ close(sfd);
+ return err;
+}
diff --git a/msg.h b/msg.h
index 4dc805e5..b9bd205d 100644
--- a/msg.h
+++ b/msg.h
@@ -27,5 +27,6 @@ extern int ack(int fd, int tmo);
extern int wait_reply(int fd, int tmo);
extern int connect_monitor(char *devname);
extern int ping_monitor(char *devname);
+extern int ping_manager(char *devname);
#define MSG_MAX_LEN (4*1024*1024)