From 313a4a82f130e6668ba0f4550200662e168aa945 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 15 Sep 2008 20:58:43 -0700 Subject: 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 --- Manage.c | 17 +++++++++++++++++ managemon.c | 17 +++++++++++++---- msg.c | 33 +++++++++++++++++++++++++++++---- msg.h | 1 + 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) -- cgit v1.2.3