[PATCH] serial-port: turn open_count assertion in mm_serial_port_close into simple return
Ben Chan
benchan at chromium.org
Sat Jan 11 18:36:19 PST 2014
On Sat, Jan 11, 2014 at 2:32 PM, Aleksander Morgado
<aleksander at aleksander.es> wrote:
>
> On 11/01/14 01:54, Ben Chan wrote:
> > When mm_serial_port_close is clearing a non-empty command queue, the
> > response handler callback of a command could potentially call
> > mm_serial_port_close again (e.g. via at_command_context_free,
> > at_sequence_context_free in mm-base-modem-at.c), which triggers the
> > following assertion:
> >
> > ModemManager: <debug> (ttyACM0) forced to close port
> > ModemManager: <debug> (ttyACM0) device open count is 0 (close)
> > ModemManager: <debug> (ttyACM0) closing serial port...
> > ModemManager: <debug> (ttyACM0) serial port closed
> > ModemManager: mm_serial_port_close: assertion `priv->open_count > 0' failed
> >
> > The assertion is confusing as the condition is normal. This patch thus turns
> > the 'priv->open_count > 0' assertion in mm_serial_port_close into a simple
> > return.
> > ---
>
> I wouldn't say the condition is normal. Each port open request has its
> corresponding port close request. The only case in which we should not
> warn about close requests happening to a port with open_count==0 is when
> the port was forced-closed, which is already handled for what I can see
> "if (priv->forced_close) return;".
>
> Now, I'm not sure in which situation you got that warning, but the
> warning is indeed useful to detect issues with port open/close count
> balance... The logs say that the ttyACM0 was forced-close; maybe
> priv->forced_close wasn't set?
>
The following call sequence illustrate the problem.
1. mm_serial_port_close_force calls mm_serial_port_close.
2. Before mm_serial_port_close_force even finishes and returns, it
invokes a response callback of a command in priv->queue
3. The response callback completes and frees the associated context,
which calls mm_serial_port_close_force again
4. The assertion is caused by the second call to mm_serial_port_close_force.
mm_serial_port_close_force {
...
priv->open_count = 1;
mm_serial_port_close (self) {
if (priv->forced_close) // forced_close = FALSE
return;
g_return_if_fail (priv->open_count > 0); // open_count = 1
priv->open_count--; // open_count = 0
...
for (i = 0; i < g_queue_get_length (priv->queue); i++) {
MMQueueData *item = g_queue_peek_nth (priv->queue, i);
if (item->callback) {
....
g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS
(self)->handle_response != NULL);
error = g_error_new_literal (MM_SERIAL_ERROR,
MM_SERIAL_ERROR_SEND_FAILED,
"Serial port is now closed");
...
MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
response,
error,
item->callback,
item->user_data) {
// !!! here handle_response will eventually
triggers the following sequence:
at_command_parse_response {
at_command_context_free {
mm_serial_port_close (self) {
if (priv->forced_close) //
forced_close = FALSE
return;
g_return_if_fail (priv->open_count
> 0); // open_count = 0 --> assertion!
}
}
}
}
g_error_free (error);
g_byte_array_free (response, TRUE);
}
....
}
}
}
priv->forced_close = TRUE;
...
}
More information about the ModemManager-devel
mailing list