Commit 68ac64cd authored by Russell King's avatar Russell King Committed by Russell King

[SERIAL] Clean up serial locking when obtaining a reference to a port

The locking for the uart_port is over complicated, and can be
simplified if we introduce a flag to indicate that a port is "dead"
and will be removed.

This also helps the validator because it removes a case of non-nested
unlock ordering.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
parent e0a515bc
...@@ -1500,20 +1500,18 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) ...@@ -1500,20 +1500,18 @@ uart_block_til_ready(struct file *filp, struct uart_state *state)
static struct uart_state *uart_get(struct uart_driver *drv, int line) static struct uart_state *uart_get(struct uart_driver *drv, int line)
{ {
struct uart_state *state; struct uart_state *state;
int ret = 0;
mutex_lock(&port_mutex);
state = drv->state + line; state = drv->state + line;
if (mutex_lock_interruptible(&state->mutex)) { if (mutex_lock_interruptible(&state->mutex)) {
state = ERR_PTR(-ERESTARTSYS); ret = -ERESTARTSYS;
goto out; goto err;
} }
state->count++; state->count++;
if (!state->port) { if (!state->port || state->port->flags & UPF_DEAD) {
state->count--; ret = -ENXIO;
mutex_unlock(&state->mutex); goto err_unlock;
state = ERR_PTR(-ENXIO);
goto out;
} }
if (!state->info) { if (!state->info) {
...@@ -1531,15 +1529,17 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) ...@@ -1531,15 +1529,17 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line)
tasklet_init(&state->info->tlet, uart_tasklet_action, tasklet_init(&state->info->tlet, uart_tasklet_action,
(unsigned long)state); (unsigned long)state);
} else { } else {
state->count--; ret = -ENOMEM;
mutex_unlock(&state->mutex); goto err_unlock;
state = ERR_PTR(-ENOMEM);
} }
} }
out:
mutex_unlock(&port_mutex);
return state; return state;
err_unlock:
state->count--;
mutex_unlock(&state->mutex);
err:
return ERR_PTR(ret);
} }
/* /*
...@@ -2085,45 +2085,6 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, ...@@ -2085,45 +2085,6 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
} }
} }
/*
* This reverses the effects of uart_configure_port, hanging up the
* port before removal.
*/
static void
uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state)
{
struct uart_port *port = state->port;
struct uart_info *info = state->info;
if (info && info->tty)
tty_vhangup(info->tty);
mutex_lock(&state->mutex);
state->info = NULL;
/*
* Free the port IO and memory resources, if any.
*/
if (port->type != PORT_UNKNOWN)
port->ops->release_port(port);
/*
* Indicate that there isn't a port here anymore.
*/
port->type = PORT_UNKNOWN;
/*
* Kill the tasklet, and free resources.
*/
if (info) {
tasklet_kill(&info->tlet);
kfree(info);
}
mutex_unlock(&state->mutex);
}
static struct tty_operations uart_ops = { static struct tty_operations uart_ops = {
.open = uart_open, .open = uart_open,
.close = uart_close, .close = uart_close,
...@@ -2270,6 +2231,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) ...@@ -2270,6 +2231,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
state = drv->state + port->line; state = drv->state + port->line;
mutex_lock(&port_mutex); mutex_lock(&port_mutex);
mutex_lock(&state->mutex);
if (state->port) { if (state->port) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
...@@ -2304,7 +2266,13 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) ...@@ -2304,7 +2266,13 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
port->cons && !(port->cons->flags & CON_ENABLED)) port->cons && !(port->cons->flags & CON_ENABLED))
register_console(port->cons); register_console(port->cons);
/*
* Ensure UPF_DEAD is not set.
*/
port->flags &= ~UPF_DEAD;
out: out:
mutex_unlock(&state->mutex);
mutex_unlock(&port_mutex); mutex_unlock(&port_mutex);
return ret; return ret;
...@@ -2322,6 +2290,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) ...@@ -2322,6 +2290,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
{ {
struct uart_state *state = drv->state + port->line; struct uart_state *state = drv->state + port->line;
struct uart_info *info;
BUG_ON(in_interrupt()); BUG_ON(in_interrupt());
...@@ -2331,12 +2300,49 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) ...@@ -2331,12 +2300,49 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
mutex_lock(&port_mutex); mutex_lock(&port_mutex);
/*
* Mark the port "dead" - this prevents any opens from
* succeeding while we shut down the port.
*/
mutex_lock(&state->mutex);
port->flags |= UPF_DEAD;
mutex_unlock(&state->mutex);
/* /*
* Remove the devices from devfs * Remove the devices from devfs
*/ */
tty_unregister_device(drv->tty_driver, port->line); tty_unregister_device(drv->tty_driver, port->line);
uart_unconfigure_port(drv, state); info = state->info;
if (info && info->tty)
tty_vhangup(info->tty);
/*
* All users of this port should now be disconnected from
* this driver, and the port shut down. We should be the
* only thread fiddling with this port from now on.
*/
state->info = NULL;
/*
* Free the port IO and memory resources, if any.
*/
if (port->type != PORT_UNKNOWN)
port->ops->release_port(port);
/*
* Indicate that there isn't a port here anymore.
*/
port->type = PORT_UNKNOWN;
/*
* Kill the tasklet, and free resources.
*/
if (info) {
tasklet_kill(&info->tlet);
kfree(info);
}
state->port = NULL; state->port = NULL;
mutex_unlock(&port_mutex); mutex_unlock(&port_mutex);
......
...@@ -254,6 +254,7 @@ struct uart_port { ...@@ -254,6 +254,7 @@ struct uart_port {
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
#define UPF_DEAD ((__force upf_t) (1 << 30))
#define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_IOREMAP ((__force upf_t) (1 << 31))
#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment