Index: lib/ioctl/libdm-iface.c =================================================================== --- lib/ioctl/libdm-iface.c.orig +++ lib/ioctl/libdm-iface.c @@ -118,6 +118,67 @@ static struct cmd_data _cmd_data_v4[] = # define DM_EXISTS_FLAG 0x00000004 #endif +static unsigned long long get_seq() +{ + struct stat s; + int r; + unsigned long long seq = 0; + FILE* fp; + r = stat("/sys/kernel/uevent_seqnum", &s); + if (r) { return seq; } + if (!S_ISREG(s.st_mode)) { return seq; } + fp = fopen("/sys/kernel/uevent_seqnum", "r"); + if (fp == NULL) { return seq; } + fscanf(fp, "%llu\n", &seq); + fclose(fp); + return seq; +} + +static int wait_4_udev(unsigned long long start_seq, unsigned long long end_seq) +{ + FILE* fp; + struct stat s; + int r; + int ite=30; /* Iterate 30 times. Otherwise, it's considered timed out */ + unsigned long long seq; + /* + * len("/dev/.udev/queue/") + len(str(0xffffffffffffffff)) = 38 + */ + #define PATH_SIZE 40 + char filename[PATH_SIZE]; + #undef PATH_SIZE + + if (start_seq > end_seq || end_seq == 0) return 1; + + /* Wait until end_seq is enqueued into udev */ + r = stat("/dev/.udev/uevent_seqnum", &s); + if (r) { return 1; } + if (!S_ISREG(s.st_mode)) { return 1; } + while (ite-- > 0) { + fp = fopen("/dev/.udev/uevent_seqnum", "r"); + if (fp == NULL) { return 1; } + fscanf(fp, "%llu\n", &seq); + fclose(fp); + if (seq >= end_seq) { break; } + usleep(200000); + } + if (ite <=0 ) return 1; + + /* Loop to make sure all $seq are unlinked in /dev/.udev/queue then */ + for (seq = start_seq; seq <= end_seq; seq++) { + ite = 30; + while (ite-- > 0) { + snprintf(filename, sizeof(filename), "/dev/.udev/queue/%llu", seq); + r = open(filename, O_RDONLY); + if (r == -1 && errno == ENOENT) break; + if (r >= 0) close(r); + usleep(200000); + } + if (ite <=0 ) return 1; + } + return 0; +} + static void *_align(void *ptr, unsigned int a) { register unsigned long agn = --a; @@ -642,6 +703,7 @@ static int _dm_task_run_v1(struct dm_tas { struct dm_ioctl_v1 *dmi; unsigned int command; + unsigned long long before_seq, after_seq; dmi = _flatten_v1(dmt); if (!dmi) { @@ -668,6 +730,8 @@ static int _dm_task_run_v1(struct dm_tas dmi->name, dmi->uuid, dmt->newname ? " " : "", dmt->newname ? dmt->newname : "", dmi->data_size); + + before_seq = get_seq(); if (dmt->type == DM_DEVICE_LIST) { if (!_dm_names_v1(dmi)) goto bad; @@ -687,10 +751,13 @@ static int _dm_task_run_v1(struct dm_tas #else /* Userspace alternative for testing */ #endif + after_seq = get_seq(); if (dmi->flags & DM_BUFFER_FULL_FLAG) /* FIXME Increase buffer size and retry operation (if query) */ log_error("WARNING: libdevmapper buffer too small for data"); + wait_4_udev(before_seq, after_seq); + switch (dmt->type) { case DM_DEVICE_CREATE: add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev), @@ -1640,6 +1707,7 @@ int dm_task_run(struct dm_task *dmt) { struct dm_ioctl *dmi; unsigned command; + unsigned long long before_seq, after_seq; #ifdef DM_COMPAT if (_dm_version == 1) @@ -1670,9 +1738,11 @@ int dm_task_run(struct dm_task *dmt) return 0; repeat_ioctl: + before_seq = get_seq(); if (!(dmi = _do_dm_ioctl(dmt, command, _ioctl_buffer_double_factor))) return 0; + after_seq = get_seq(); if (dmi->flags & DM_BUFFER_FULL_FLAG) { switch (dmt->type) { case DM_DEVICE_LIST_VERSIONS: @@ -1689,6 +1759,8 @@ repeat_ioctl: } } + wait_4_udev(before_seq, after_seq); + switch (dmt->type) { case DM_DEVICE_CREATE: if (dmt->dev_name && *dmt->dev_name)