[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
libparted: avoid race in informing the kernel of partition table changes
From: |
Jim Meyering |
Subject: |
libparted: avoid race in informing the kernel of partition table changes |
Date: |
Fri, 30 Apr 2010 16:31:22 +0200 |
Here are two patches.
The first is just renaming and clean-up.
The meaty 2nd one has the following long commit log.
--------------------------------------------------------------
libparted: avoid race in informing the kernel of partition table changes
When sync'ing a partition table change using the latest
code, sometimes we'd get an unwarranted failure like this:
Warning: Partition(s) 1 on /dev/sdd have been written, but we
have been unable to inform the kernel of the change, probably because
it/they are in use. As a result, the old partition(s) will remain in
use. You should reboot now before making further changes.
To be precise, when running the partition-resizing root-only test
in a loop:
for i in $(seq 240); do make -C tests check VERBOSE=yes \
TESTS=t3000-resize-fs.sh >& log.$i && printf . || echo $i $?; done
I would typically see about 50% of them fail on a Fedora 13 system.
It was obvious that this was due to a race condition when I found that
modifying that tests' parted...resize invocation to go via strace changed
the timing enough to make the test pass every time.
The fix is to retry the partition-removal step upon any EBUSY failure,
currently for up to 1 second (retrying up to 100 times, sleeping 10ms
after each failure).
* libparted/arch/linux.c (_disk_sync_part_table): Allocate "ok" using
calloc, now that its initial values matter.
Retry each removal upon EBUSY-failure.
* bootstrap.conf (gnulib_modules): Use gnulib's usleep module.
>From 9acc5869985c8616066af4825f52fb6c474f63dd Mon Sep 17 00:00:00 2001
From: Jim Meyering <address@hidden>
Date: Fri, 30 Apr 2010 12:01:08 +0200
Subject: [PATCH 1/2] libparted: variable renaming, minor "goto" reorg
* libparted/arch/linux.c (_disk_sync_part_table): Rename local array:
s/rets/ok/, for readability.
Use only a single label, "cleanup:", rather than two: free_rets
and free_errnums.
---
libparted/arch/linux.c | 27 +++++++++++++--------------
1 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c
index d8f3393..8116c76 100644
--- a/libparted/arch/linux.c
+++ b/libparted/arch/linux.c
@@ -2452,29 +2452,29 @@ _disk_sync_part_table (PedDisk* disk)
if (lpn < 0)
return 0;
int ret = 0;
- int *rets = ped_malloc(sizeof(int) * lpn);
- if (!rets)
+ int *ok = ped_malloc(sizeof(int) * lpn);
+ if (!ok)
return 0;
int *errnums = ped_malloc(sizeof(int) * lpn);
if (!errnums)
- goto free_rets;
+ goto cleanup;
int i;
for (i = 1; i <= lpn; i++) {
- rets[i - 1] = _blkpg_remove_partition (disk, i);
+ ok[i - 1] = _blkpg_remove_partition (disk, i);
errnums[i - 1] = errno;
}
for (i = 1; i <= lpn; i++) {
const PedPartition *part = ped_disk_get_partition (disk, i);
if (part) {
- if (!rets[i - 1] && errnums[i - 1] == EBUSY) {
+ if (!ok[i - 1] && errnums[i - 1] == EBUSY) {
struct hd_geometry geom;
unsigned long long length = 0;
/* get start and length of existing partition
*/
char *dev_name = _device_get_part_path
(disk->dev, i);
if (!dev_name)
- goto free_errnums;
+ goto cleanup;
int fd = open (dev_name, O_RDONLY);
if (fd == -1
|| ioctl (fd, HDIO_GETGEO, &geom)
@@ -2487,14 +2487,14 @@ _disk_sync_part_table (PedDisk* disk)
if (fd != -1)
close (fd);
free (dev_name);
- goto free_errnums;
+ goto cleanup;
}
free (dev_name);
length /= disk->dev->sector_size;
close (fd);
if (geom.start == part->geom.start
&& length == part->geom.length)
- rets[i - 1] = 1;
+ ok[i - 1] = 1;
/* If the new partition is unchanged and the
existing one was not removed because it was
in use, then reset the error flag and do not
@@ -2509,7 +2509,7 @@ _disk_sync_part_table (PedDisk* disk)
PED_EXCEPTION_RETRY_CANCEL,
_("Failed to add partition %d (%s)"),
i, strerror (errno));
- goto free_errnums;
+ goto cleanup;
}
}
}
@@ -2517,12 +2517,12 @@ _disk_sync_part_table (PedDisk* disk)
char *bad_part_list = NULL;
/* now warn about any errors */
for (i = 1; i <= lpn; i++) {
- if (rets[i - 1] || errnums[i - 1] == ENXIO)
+ if (ok[i - 1] || errnums[i - 1] == ENXIO)
continue;
if (bad_part_list == NULL) {
bad_part_list = malloc (lpn * 5);
if (!bad_part_list)
- goto free_errnums;
+ goto cleanup;
bad_part_list[0] = 0;
}
sprintf (bad_part_list + strlen (bad_part_list), "%d, ", i);
@@ -2543,10 +2543,9 @@ _disk_sync_part_table (PedDisk* disk)
ret = 1;
free (bad_part_list);
}
- free_errnums:
+ cleanup:
free (errnums);
- free_rets:
- free (rets);
+ free (ok);
return ret;
}
--
1.7.1.328.g9993c
>From 0f850220b3f26bb969a1a7ff78dc550691a89566 Mon Sep 17 00:00:00 2001
From: Jim Meyering <address@hidden>
Date: Fri, 30 Apr 2010 12:28:16 +0200
Subject: [PATCH 2/2] libparted: avoid race in informing the kernel of partition
table changes
When sync'ing a partition table change using the latest
code, sometimes we'd get an unwarranted failure like this:
Warning: Partition(s) 1 on /dev/sdd have been written, but we
have been unable to inform the kernel of the change, probably because
it/they are in use. As a result, the old partition(s) will remain in
use. You should reboot now before making further changes.
To be precise, when running the partition-resizing root-only test
in a loop:
for i in $(seq 240); do make -C tests check VERBOSE=yes \
TESTS=t3000-resize-fs.sh >& log.$i && printf . || echo $i $?; done
I would typically see about 50% of them fail on a Fedora 13 system.
It was obvious that this was due to a race condition when I found that
modifying that tests' parted...resize invocation to go via strace changed
the timing enough to make the test pass every time.
The fix is to retry the partition-removal step upon any EBUSY failure,
currently for up to 1 second (retrying up to 100 times, sleeping 10ms
after each failure).
* libparted/arch/linux.c (_disk_sync_part_table): Allocate "ok" using
calloc, now that its initial values matter.
Retry each removal upon EBUSY-failure.
* bootstrap.conf (gnulib_modules): Use gnulib's usleep module.
---
bootstrap.conf | 1 +
libparted/arch/linux.c | 28 +++++++++++++++++++++++-----
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/bootstrap.conf b/bootstrap.conf
index 6c9287d..4ca51a7 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -58,6 +58,7 @@ gnulib_modules="
unlink
update-copyright
useless-if-before-free
+ usleep
vc-list-files
version-etc-fsf
warnings
diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c
index 8116c76..73a8e6f 100644
--- a/libparted/arch/linux.c
+++ b/libparted/arch/linux.c
@@ -2452,17 +2452,35 @@ _disk_sync_part_table (PedDisk* disk)
if (lpn < 0)
return 0;
int ret = 0;
- int *ok = ped_malloc(sizeof(int) * lpn);
+ int *ok = calloc (lpn, sizeof *ok);
if (!ok)
return 0;
int *errnums = ped_malloc(sizeof(int) * lpn);
if (!errnums)
goto cleanup;
- int i;
- for (i = 1; i <= lpn; i++) {
- ok[i - 1] = _blkpg_remove_partition (disk, i);
- errnums[i - 1] = errno;
+ /* Attempt to remove each and every partition, retrying for
+ up to max_sleep_seconds upon any failure due to EBUSY. */
+ unsigned int sleep_microseconds = 10000;
+ unsigned int max_sleep_seconds = 1;
+ unsigned int n_sleep = (max_sleep_seconds
+ * 1000000 / sleep_microseconds);
+ int i;
+ for (i = 0; i < n_sleep; i++) {
+ if (i)
+ usleep (sleep_microseconds);
+ bool busy = false;
+ int j;
+ for (j = 0; j < lpn; j++) {
+ if (!ok[j]) {
+ ok[j] = _blkpg_remove_partition (disk, j + 1);
+ errnums[j] = errno;
+ if (!ok[j] && errnums[j] == EBUSY)
+ busy = true;
+ }
+ }
+ if (!busy)
+ break;
}
for (i = 1; i <= lpn; i++) {
--
1.7.1.328.g9993c
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- libparted: avoid race in informing the kernel of partition table changes,
Jim Meyering <=