[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>

authored by Russell King and committed by Russell King 68ac64cd e0a515bc

+61 -54
+60 -54
drivers/serial/serial_core.c
··· 1500 1500 static struct uart_state *uart_get(struct uart_driver *drv, int line) 1501 1501 { 1502 1502 struct uart_state *state; 1503 + int ret = 0; 1503 1504 1504 - mutex_lock(&port_mutex); 1505 1505 state = drv->state + line; 1506 1506 if (mutex_lock_interruptible(&state->mutex)) { 1507 - state = ERR_PTR(-ERESTARTSYS); 1508 - goto out; 1507 + ret = -ERESTARTSYS; 1508 + goto err; 1509 1509 } 1510 1510 1511 1511 state->count++; 1512 - if (!state->port) { 1513 - state->count--; 1514 - mutex_unlock(&state->mutex); 1515 - state = ERR_PTR(-ENXIO); 1516 - goto out; 1512 + if (!state->port || state->port->flags & UPF_DEAD) { 1513 + ret = -ENXIO; 1514 + goto err_unlock; 1517 1515 } 1518 1516 1519 1517 if (!state->info) { ··· 1529 1531 tasklet_init(&state->info->tlet, uart_tasklet_action, 1530 1532 (unsigned long)state); 1531 1533 } else { 1532 - state->count--; 1533 - mutex_unlock(&state->mutex); 1534 - state = ERR_PTR(-ENOMEM); 1534 + ret = -ENOMEM; 1535 + goto err_unlock; 1535 1536 } 1536 1537 } 1537 - 1538 - out: 1539 - mutex_unlock(&port_mutex); 1540 1538 return state; 1539 + 1540 + err_unlock: 1541 + state->count--; 1542 + mutex_unlock(&state->mutex); 1543 + err: 1544 + return ERR_PTR(ret); 1541 1545 } 1542 1546 1543 1547 /* ··· 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 2088 static struct tty_operations uart_ops = { 2128 2089 .open = uart_open, 2129 2090 .close = uart_close, ··· 2231 2270 state = drv->state + port->line; 2232 2271 2233 2272 mutex_lock(&port_mutex); 2273 + mutex_lock(&state->mutex); 2234 2274 if (state->port) { 2235 2275 ret = -EINVAL; 2236 2276 goto out; ··· 2266 2304 port->cons && !(port->cons->flags & CON_ENABLED)) 2267 2305 register_console(port->cons); 2268 2306 2307 + /* 2308 + * Ensure UPF_DEAD is not set. 2309 + */ 2310 + port->flags &= ~UPF_DEAD; 2311 + 2269 2312 out: 2313 + mutex_unlock(&state->mutex); 2270 2314 mutex_unlock(&port_mutex); 2271 2315 2272 2316 return ret; ··· 2290 2322 int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) 2291 2323 { 2292 2324 struct uart_state *state = drv->state + port->line; 2325 + struct uart_info *info; 2293 2326 2294 2327 BUG_ON(in_interrupt()); 2295 2328 ··· 2301 2332 mutex_lock(&port_mutex); 2302 2333 2303 2334 /* 2335 + * Mark the port "dead" - this prevents any opens from 2336 + * succeeding while we shut down the port. 2337 + */ 2338 + mutex_lock(&state->mutex); 2339 + port->flags |= UPF_DEAD; 2340 + mutex_unlock(&state->mutex); 2341 + 2342 + /* 2304 2343 * Remove the devices from devfs 2305 2344 */ 2306 2345 tty_unregister_device(drv->tty_driver, port->line); 2307 2346 2308 - uart_unconfigure_port(drv, state); 2347 + info = state->info; 2348 + if (info && info->tty) 2349 + tty_vhangup(info->tty); 2350 + 2351 + /* 2352 + * All users of this port should now be disconnected from 2353 + * this driver, and the port shut down. We should be the 2354 + * only thread fiddling with this port from now on. 2355 + */ 2356 + state->info = NULL; 2357 + 2358 + /* 2359 + * Free the port IO and memory resources, if any. 2360 + */ 2361 + if (port->type != PORT_UNKNOWN) 2362 + port->ops->release_port(port); 2363 + 2364 + /* 2365 + * Indicate that there isn't a port here anymore. 2366 + */ 2367 + port->type = PORT_UNKNOWN; 2368 + 2369 + /* 2370 + * Kill the tasklet, and free resources. 2371 + */ 2372 + if (info) { 2373 + tasklet_kill(&info->tlet); 2374 + kfree(info); 2375 + } 2376 + 2309 2377 state->port = NULL; 2310 2378 mutex_unlock(&port_mutex); 2311 2379
+1
include/linux/serial_core.h
··· 254 254 #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) 255 255 #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) 256 256 #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) 257 + #define UPF_DEAD ((__force upf_t) (1 << 30)) 257 258 #define UPF_IOREMAP ((__force upf_t) (1 << 31)) 258 259 259 260 #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))