aboutsummaryrefslogtreecommitdiff
path: root/src/mm-plugin-base.c
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-03-11 13:20:43 -0800
committerDan Williams <dcbw@redhat.com>2010-03-11 13:20:43 -0800
commit749f9c0eb569b29772dde9561b9856e4f878d9ef (patch)
tree6f211060b7e7fa6e4cf9e17c2731f56c6fad5504 /src/mm-plugin-base.c
parentc7739979ed4a8be97d03ab7ed0087d63dc218cf4 (diff)
core: stop probing known-unusable ports early
Some ports we know we shouldn't use when we get certain responses from them. Reading from these ports triggers kernel bugs (at least on 2.6.31 and 2.6.32) relating to flow control in some drivers (*cough* hso *cough*), so lets try not to aggravate the kernel too much. This happens on Icera-based Option devices like the GI0322 (AT&T Quicksilver) for example. (note: AFAICT this doesn't have any relation to the recent XON/XOFF patch, since I get this problem without the XON/XOFF patch on both 2.6.31 and 2.6.32 as well) --- BUG: sleeping function called from invalid context at kernel/mutex.c:94 in_atomic(): 1, irqs_disabled(): 1, pid: 9295, name: modem-manager Pid: 9295, comm: modem-manager Not tainted 2.6.32.9-67.fc12.x86_64 #1 Call Trace: <IRQ> [<ffffffff81045d41>] __might_sleep+0xed/0xef [<ffffffff81454dd0>] mutex_lock+0x24/0x50 [<ffffffff8104811e>] ? enqueue_task_fair+0x2a/0x6d [<ffffffff812af79f>] tty_throttle+0x1b/0x49 [<ffffffff812af0d9>] n_tty_receive_buf+0xdbb/0xe12 [<ffffffff810459fd>] ? task_rq_unlock+0x11/0x13 [<ffffffff81050c5c>] ? try_to_wake_up+0x2f3/0x305 [<ffffffff8110de0c>] ? __kmalloc+0x37/0x15e [<ffffffff8110de42>] ? __kmalloc+0x6d/0x15e [<ffffffff812b12c9>] flush_to_ldisc+0xf8/0x18d [<ffffffff812b13ae>] tty_flip_buffer_push+0x50/0x61 [<ffffffffa040ccd5>] put_rxbuf_data+0xea/0x124 [hso] [<ffffffffa040cd97>] put_rxbuf_data_and_resubmit_bulk_urb+0x21/0x6b [hso] [<ffffffffa040d0b1>] hso_std_serial_read_bulk_callback+0x14d/0x15f [hso] [<ffffffff8132edf7>] ? dma_unmap_single_attrs.clone.0+0x38/0x3a [<ffffffff8132ef74>] usb_hcd_giveback_urb+0x91/0xc5 [<ffffffff813417c8>] ehci_urb_done+0x7b/0x90 [<ffffffff81050c5c>] ? try_to_wake_up+0x2f3/0x305 [<ffffffff81341b45>] qh_completions+0x368/0x4b9 [<ffffffff8103e7a0>] ? __wake_up_common+0x4e/0x84 [<ffffffff81343f70>] ehci_work+0x95/0x732 [<ffffffff81045b53>] ? __wake_up+0x44/0x4d [<ffffffff81070490>] ? insert_work+0x8e/0x9b [<ffffffff81345f01>] ehci_irq+0x2be/0x420 [<ffffffff8107071a>] ? __queue_work+0x3a/0x41 [<ffffffff81049e43>] ? resched_cpu+0x6e/0x77 [<ffffffff8107075d>] ? delayed_work_timer_fn+0x3c/0x3e [<ffffffff810b0e44>] ? __rcu_process_callbacks+0x7d/0x28a [<ffffffff8132e846>] usb_hcd_irq+0x3f/0x7b [<ffffffff810acd61>] handle_IRQ_event+0x60/0x121 [<ffffffff810aeb8e>] handle_fasteoi_irq+0x8b/0xc7 [<ffffffff81014625>] handle_irq+0x8b/0x96 [<ffffffff81459c14>] do_IRQ+0x5c/0xbc [<ffffffff81012693>] ret_from_intr+0x0/0x11
Diffstat (limited to 'src/mm-plugin-base.c')
-rw-r--r--src/mm-plugin-base.c55
1 files changed, 54 insertions, 1 deletions
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 202eaac9..4b74feb7 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -77,6 +77,8 @@ typedef enum {
PROBE_STATE_LAST
} ProbeState;
+static void probe_complete (MMPluginBaseSupportsTask *task);
+
/*****************************************************************************/
G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OBJECT)
@@ -91,6 +93,7 @@ typedef struct {
guint open_id;
guint32 open_tries;
+ guint full_id;
MMSerialPort *probe_port;
guint32 probed_caps;
@@ -198,6 +201,11 @@ mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task,
priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
g_return_if_fail (priv->callback != NULL);
+ if (priv->full_id) {
+ g_source_remove (priv->full_id);
+ priv->full_id = 0;
+ }
+
subsys = g_udev_device_get_subsystem (priv->port);
name = g_udev_device_get_name (priv->port);
@@ -251,11 +259,15 @@ supports_task_dispose (GObject *object)
if (priv->open_id)
g_source_remove (priv->open_id);
+ if (priv->full_id)
+ g_source_remove (priv->full_id);
if (priv->probe_id)
g_source_remove (priv->probe_id);
- if (priv->probe_port)
+ if (priv->probe_port) {
+ mm_serial_port_close (priv->probe_port);
g_object_unref (priv->probe_port);
+ }
G_OBJECT_CLASS (mm_plugin_base_supports_task_parent_class)->dispose (object);
}
@@ -349,6 +361,44 @@ parse_cgmm (const char *buf)
return 0;
}
+static const char *dq_strings[] = {
+ "option/faema_", "os_logids.h", NULL
+};
+
+static void
+port_buffer_full (MMSerialPort *port, GString *buffer, gpointer user_data)
+{
+ MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
+ MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (user_data);
+ const char **iter;
+ size_t iter_len;
+ int i;
+
+ /* Check for an immediate disqualification response. There are some
+ * ports (Option Icera-based chipsets have them, as do Qualcomm Gobi
+ * devices before their firmware is loaded) that just shouldn't be
+ * probed if we get a certain response because we know they can't be
+ * used. Kernel bugs (at least with 2.6.31 and 2.6.32) also trigger port
+ * flow control kernel oopses if we read too much data for these ports.
+ */
+
+ for (iter = &dq_strings[0]; iter && *iter; iter++) {
+ /* Search in the response for the item; the response could have embedded
+ * nulls so we can't use memcmp() or strstr() on the whole response.
+ */
+ iter_len = strlen (*iter);
+ for (i = 0; i < buffer->len - iter_len; i++) {
+ if (!memcmp (&buffer->str[i], *iter, iter_len)) {
+ /* Immediately close the port and complete probing */
+ priv->probed_caps = 0;
+ mm_serial_port_close (priv->probe_port);
+ probe_complete (task);
+ return;
+ }
+ }
+ }
+}
+
static gboolean
emit_probe_result (gpointer user_data)
{
@@ -626,6 +676,9 @@ try_open (gpointer user_data)
port = mm_plugin_base_supports_task_get_port (task);
g_assert (port);
+ task_priv->full_id = g_signal_connect (task_priv->probe_port, "buffer-full",
+ G_CALLBACK (port_buffer_full), task);
+
g_debug ("(%s): probe requested by plugin '%s'",
g_udev_device_get_name (port),
mm_plugin_get_name (MM_PLUGIN (task_priv->plugin)));