summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Pitt <martin.pitt@ubuntu.com>2011-06-19 20:48:22 +0200
committerMartin Pitt <martin.pitt@ubuntu.com>2011-06-21 19:04:55 +0200
commit2d0272eb3be71f6ee3f52c55a3d608764b675fb0 (patch)
treea01acc919fce9d8ba4e2e722ba9591db01914f24
parent1c7d933431e49ff6840a388eb23134633155a442 (diff)
Bug 34710 — CD-ROM polling failed due to O_EXCL flag
Newer kernels (2.6.38+) support in-kernel polling of CD-ROM/SD card devices. udev 172 and later enable this feature by default: http://git.kernel.org/?p=linux/hotplug/udev.git;a=commitdiff;h=c5a41da949 That will also handle the eject button properly and send out remove uevents, which causes stale mounts to be cleaned up properly. In-kernel polling avoids the userspace race conditions with accessing the drive. If kernel polling is not supported or enabled, fall back to the previous method of unlocking the CD drive door right after mounting, to keep the hardware button working.
-rw-r--r--src/device-private.h3
-rw-r--r--src/device.c7
-rw-r--r--src/poller.c55
3 files changed, 64 insertions, 1 deletions
diff --git a/src/device-private.h b/src/device-private.h
index a6db7f2..e519083 100644
--- a/src/device-private.h
+++ b/src/device-private.h
@@ -59,6 +59,9 @@ struct DevicePrivate
gboolean job_is_cancellable;
double job_percentage;
+ gboolean checked_in_kernel_polling;
+ gboolean using_in_kernel_polling;
+
guint linux_md_poll_timeout_id;
/* A list of current polling inhibitors (Inhibitor objects) */
diff --git a/src/device.c b/src/device.c
index b30fe1c..c4b4ab3 100644
--- a/src/device.c
+++ b/src/device.c
@@ -6213,7 +6213,12 @@ filesystem_mount_completed_cb (DBusGMethodInvocation *context,
update_info (device);
drain_pending_changes (device, FALSE);
- unlock_cd_tray (device);
+ /* If the kernel and device support sending EJECT_REQUEST change uevents
+ * and we use in-kernel polling, keep the door locked, as udev calls
+ * eject on pressing the button. Otherwise unlock it, to keep the
+ * hardware button working without userspace support */
+ if (!device->priv->using_in_kernel_polling)
+ unlock_cd_tray (device);
dbus_g_method_return (context, data->mount_point);
}
diff --git a/src/poller.c b/src/poller.c
index 9517b5d..cc807d8 100644
--- a/src/poller.c
+++ b/src/poller.c
@@ -31,6 +31,7 @@
#include <fcntl.h>
#include <string.h>
#include <errno.h>
+#include <gudev/gudev.h>
#include "poller.h"
#include "device.h"
@@ -304,6 +305,52 @@ poller_setup (int argc,
/* ---------------------------------------------------------------------------------------------------- */
+static gboolean
+check_in_kernel_polling (Device* d)
+{
+ /* only check once */
+ if (!d->priv->checked_in_kernel_polling)
+ {
+ int poll_time;
+ int fd;
+ char c;
+
+ d->priv->checked_in_kernel_polling = TRUE;
+
+ poll_time = g_udev_device_get_sysfs_attr_as_int (d->priv->d, "events_poll_msecs");
+#ifdef POLL_SHOW_DEBUG
+ g_print("**** POLLER (%d): per-device poll time for %s: %i\n", getpid (), d->priv->device_file, poll_time);
+#endif
+
+ if (poll_time >= 0)
+ {
+ d->priv->using_in_kernel_polling = (poll_time > 0);
+ goto out;
+ }
+
+ /* -1 means using global polling interval, so check the global default */
+ /* check global default */
+ fd = open("/sys/module/block/parameters/events_dfl_poll_msecs", O_RDONLY);
+ if (fd > 0)
+ {
+ if (read (fd, &c, 1) > 0)
+ {
+#ifdef POLL_SHOW_DEBUG
+ g_print("**** POLLER (%d): global poll time first char: %c\n", getpid (), c);
+#endif
+ /* if this is positive, we use in-kernel polling */
+ d->priv->using_in_kernel_polling = (c != '0' && c != '-');
+ }
+ close (fd);
+ }
+ }
+
+out:
+ return d->priv->using_in_kernel_polling;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
void
poller_set_devices (GList *devices)
{
@@ -320,6 +367,14 @@ poller_set_devices (GList *devices)
{
Device *device = DEVICE (l->data);
+ if (check_in_kernel_polling (device))
+ {
+#ifdef POLL_SHOW_DEBUG
+ g_print("**** POLLER (%d): Kernel is polling %s already, ignoring\n", getpid (), device->priv->device_file);
+#endif
+ continue;
+ }
+
device_array[n++] = device->priv->device_file;
}