diff options
| author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2006-04-30 06:13:50 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-04-30 06:13:50 -0400 |
| commit | 68ac64cd3fd89fdaa091701f6ab98a9065e9b1b5 (patch) | |
| tree | 349dc1446bb52b87ce11f7ba2bb52d90679d3dd5 | |
| parent | e0a515bc6a2188f02916e976f419a8640312e32a (diff) | |
[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: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
| -rw-r--r-- | drivers/serial/serial_core.c | 114 | ||||
| -rw-r--r-- | include/linux/serial_core.h | 1 |
2 files changed, 61 insertions, 54 deletions
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index fcd7744c4253..aeb8153ccf24 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c | |||
| @@ -1500,20 +1500,18 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) | |||
| 1500 | static struct uart_state *uart_get(struct uart_driver *drv, int line) | 1500 | static struct uart_state *uart_get(struct uart_driver *drv, int line) |
| 1501 | { | 1501 | { |
| 1502 | struct uart_state *state; | 1502 | struct uart_state *state; |
| 1503 | int ret = 0; | ||
| 1503 | 1504 | ||
| 1504 | mutex_lock(&port_mutex); | ||
| 1505 | state = drv->state + line; | 1505 | state = drv->state + line; |
| 1506 | if (mutex_lock_interruptible(&state->mutex)) { | 1506 | if (mutex_lock_interruptible(&state->mutex)) { |
| 1507 | state = ERR_PTR(-ERESTARTSYS); | 1507 | ret = -ERESTARTSYS; |
| 1508 | goto out; | 1508 | goto err; |
| 1509 | } | 1509 | } |
| 1510 | 1510 | ||
| 1511 | state->count++; | 1511 | state->count++; |
| 1512 | if (!state->port) { | 1512 | if (!state->port || state->port->flags & UPF_DEAD) { |
| 1513 | state->count--; | 1513 | ret = -ENXIO; |
| 1514 | mutex_unlock(&state->mutex); | 1514 | goto err_unlock; |
| 1515 | state = ERR_PTR(-ENXIO); | ||
| 1516 | goto out; | ||
| 1517 | } | 1515 | } |
| 1518 | 1516 | ||
| 1519 | if (!state->info) { | 1517 | if (!state->info) { |
| @@ -1531,15 +1529,17 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) | |||
| 1531 | tasklet_init(&state->info->tlet, uart_tasklet_action, | 1529 | tasklet_init(&state->info->tlet, uart_tasklet_action, |
| 1532 | (unsigned long)state); | 1530 | (unsigned long)state); |
| 1533 | } else { | 1531 | } else { |
| 1534 | state->count--; | 1532 | ret = -ENOMEM; |
| 1535 | mutex_unlock(&state->mutex); | 1533 | goto err_unlock; |
| 1536 | state = ERR_PTR(-ENOMEM); | ||
| 1537 | } | 1534 | } |
| 1538 | } | 1535 | } |
| 1539 | |||
| 1540 | out: | ||
| 1541 | mutex_unlock(&port_mutex); | ||
| 1542 | return state; | 1536 | return state; |
| 1537 | |||
| 1538 | err_unlock: | ||
| 1539 | state->count--; | ||
| 1540 | mutex_unlock(&state->mutex); | ||
| 1541 | err: | ||
| 1542 | return ERR_PTR(ret); | ||
| 1543 | } | 1543 | } |
| 1544 | 1544 | ||
| 1545 | /* | 1545 | /* |
| @@ -2085,45 +2085,6 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, | |||
| 2085 | } | 2085 | } |
| 2086 | } | 2086 | } |
| 2087 | 2087 | ||
| 2088 | /* | ||
| 2089 | * This reverses the effects of uart_configure_port, hanging up the | ||
| 2090 | * port before removal. | ||
| 2091 | */ | ||
| 2092 | static void | ||
| 2093 | uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) | ||
| 2094 | { | ||
| 2095 | struct uart_port *port = state->port; | ||
| 2096 | struct uart_info *info = state->info; | ||
| 2097 | |||
| 2098 | if (info && info->tty) | ||
| 2099 | tty_vhangup(info->tty); | ||
| 2100 | |||
| 2101 | mutex_lock(&state->mutex); | ||
| 2102 | |||
| 2103 | state->info = NULL; | ||
| 2104 | |||
| 2105 | /* | ||
| 2106 | * Free the port IO and memory resources, if any. | ||
| 2107 | */ | ||
| 2108 | if (port->type != PORT_UNKNOWN) | ||
| 2109 | port->ops->release_port(port); | ||
| 2110 | |||
| 2111 | /* | ||
| 2112 | * Indicate that there isn't a port here anymore. | ||
| 2113 | */ | ||
| 2114 | port->type = PORT_UNKNOWN; | ||
| 2115 | |||
| 2116 | /* | ||
| 2117 | * Kill the tasklet, and free resources. | ||
| 2118 | */ | ||
| 2119 | if (info) { | ||
| 2120 | tasklet_kill(&info->tlet); | ||
| 2121 | kfree(info); | ||
| 2122 | } | ||
| 2123 | |||
| 2124 | mutex_unlock(&state->mutex); | ||
| 2125 | } | ||
| 2126 | |||
| 2127 | static struct tty_operations uart_ops = { | 2088 | static struct tty_operations uart_ops = { |
| 2128 | .open = uart_open, | 2089 | .open = uart_open, |
| 2129 | .close = uart_close, | 2090 | .close = uart_close, |
| @@ -2270,6 +2231,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) | |||
| 2270 | state = drv->state + port->line; | 2231 | state = drv->state + port->line; |
| 2271 | 2232 | ||
| 2272 | mutex_lock(&port_mutex); | 2233 | mutex_lock(&port_mutex); |
| 2234 | mutex_lock(&state->mutex); | ||
| 2273 | if (state->port) { | 2235 | if (state->port) { |
| 2274 | ret = -EINVAL; | 2236 | ret = -EINVAL; |
| 2275 | goto out; | 2237 | goto out; |
| @@ -2304,7 +2266,13 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) | |||
| 2304 | port->cons && !(port->cons->flags & CON_ENABLED)) | 2266 | port->cons && !(port->cons->flags & CON_ENABLED)) |
| 2305 | register_console(port->cons); | 2267 | register_console(port->cons); |
| 2306 | 2268 | ||
| 2269 | /* | ||
| 2270 | * Ensure UPF_DEAD is not set. | ||
| 2271 | */ | ||
| 2272 | port->flags &= ~UPF_DEAD; | ||
| 2273 | |||
| 2307 | out: | 2274 | out: |
| 2275 | mutex_unlock(&state->mutex); | ||
| 2308 | mutex_unlock(&port_mutex); | 2276 | mutex_unlock(&port_mutex); |
| 2309 | 2277 | ||
| 2310 | return ret; | 2278 | return ret; |
| @@ -2322,6 +2290,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) | |||
| 2322 | int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) | 2290 | int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) |
| 2323 | { | 2291 | { |
| 2324 | struct uart_state *state = drv->state + port->line; | 2292 | struct uart_state *state = drv->state + port->line; |
| 2293 | struct uart_info *info; | ||
| 2325 | 2294 | ||
| 2326 | BUG_ON(in_interrupt()); | 2295 | BUG_ON(in_interrupt()); |
| 2327 | 2296 | ||
| @@ -2332,11 +2301,48 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) | |||
| 2332 | mutex_lock(&port_mutex); | 2301 | mutex_lock(&port_mutex); |
| 2333 | 2302 | ||
| 2334 | /* | 2303 | /* |
| 2304 | * Mark the port "dead" - this prevents any opens from | ||
| 2305 | * succeeding while we shut down the port. | ||
| 2306 | */ | ||
| 2307 | mutex_lock(&state->mutex); | ||
| 2308 | port->flags |= UPF_DEAD; | ||
| 2309 | mutex_unlock(&state->mutex); | ||
| 2310 | |||
| 2311 | /* | ||
| 2335 | * Remove the devices from devfs | 2312 | * Remove the devices from devfs |
| 2336 | */ | 2313 | */ |
| 2337 | tty_unregister_device(drv->tty_driver, port->line); | 2314 | tty_unregister_device(drv->tty_driver, port->line); |
| 2338 | 2315 | ||
| 2339 | uart_unconfigure_port(drv, state); | 2316 | info = state->info; |
| 2317 | if (info && info->tty) | ||
| 2318 | tty_vhangup(info->tty); | ||
| 2319 | |||
| 2320 | /* | ||
| 2321 | * All users of this port should now be disconnected from | ||
| 2322 | * this driver, and the port shut down. We should be the | ||
| 2323 | * only thread fiddling with this port from now on. | ||
| 2324 | */ | ||
| 2325 | state->info = NULL; | ||
| 2326 | |||
| 2327 | /* | ||
| 2328 | * Free the port IO and memory resources, if any. | ||
| 2329 | */ | ||
| 2330 | if (port->type != PORT_UNKNOWN) | ||
| 2331 | port->ops->release_port(port); | ||
| 2332 | |||
| 2333 | /* | ||
| 2334 | * Indicate that there isn't a port here anymore. | ||
| 2335 | */ | ||
| 2336 | port->type = PORT_UNKNOWN; | ||
| 2337 | |||
| 2338 | /* | ||
| 2339 | * Kill the tasklet, and free resources. | ||
| 2340 | */ | ||
| 2341 | if (info) { | ||
| 2342 | tasklet_kill(&info->tlet); | ||
| 2343 | kfree(info); | ||
| 2344 | } | ||
| 2345 | |||
| 2340 | state->port = NULL; | 2346 | state->port = NULL; |
| 2341 | mutex_unlock(&port_mutex); | 2347 | mutex_unlock(&port_mutex); |
| 2342 | 2348 | ||
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index c32e60e79dea..bd14858121ea 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h | |||
| @@ -254,6 +254,7 @@ struct uart_port { | |||
| 254 | #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) | 254 | #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) |
| 255 | #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) | 255 | #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) |
| 256 | #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) | 256 | #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) |
| 257 | #define UPF_DEAD ((__force upf_t) (1 << 30)) | ||
| 257 | #define UPF_IOREMAP ((__force upf_t) (1 << 31)) | 258 | #define UPF_IOREMAP ((__force upf_t) (1 << 31)) |
| 258 | 259 | ||
| 259 | #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) | 260 | #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) |
