diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-09-09 17:36:37 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2012-03-24 19:29:53 -0400 |
commit | d8c215adbf3901aa7d00a0f17f08d77be689f838 (patch) | |
tree | 88f7f49abb5944725f3b60d8f28a41580e7f915f /arch/um/drivers/line.c | |
parent | 43574c1afea4f798592c03cf4d4ecea4fd0a8416 (diff) |
um: convert count_lock to mutex, fix a race in line_open()
If two processes are opening the same line, the second to get
into line_open() will decide that it doesn't need to do anything
(correctly) or wait for anything. The latter, unfortunately,
is incorrect - the first opener might not be through yet. We
need to have exclusion covering the entire line_init(), including
the blocking parts. Moreover, the next patch will need to
widen the exclusion on mconsole side of things, also including
the blocking bits, so let's just convert that sucker to mutex...
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um/drivers/line.c')
-rw-r--r-- | arch/um/drivers/line.c | 26 |
1 files changed, 9 insertions, 17 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 3eea99e98a1..dc7e216df6a 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c | |||
@@ -409,7 +409,7 @@ int line_open(struct line *lines, struct tty_struct *tty) | |||
409 | struct line *line = &lines[tty->index]; | 409 | struct line *line = &lines[tty->index]; |
410 | int err = -ENODEV; | 410 | int err = -ENODEV; |
411 | 411 | ||
412 | spin_lock(&line->count_lock); | 412 | mutex_lock(&line->count_lock); |
413 | if (!line->valid) | 413 | if (!line->valid) |
414 | goto out_unlock; | 414 | goto out_unlock; |
415 | 415 | ||
@@ -421,10 +421,9 @@ int line_open(struct line *lines, struct tty_struct *tty) | |||
421 | tty->driver_data = line; | 421 | tty->driver_data = line; |
422 | line->tty = tty; | 422 | line->tty = tty; |
423 | 423 | ||
424 | spin_unlock(&line->count_lock); | ||
425 | err = enable_chan(line); | 424 | err = enable_chan(line); |
426 | if (err) /* line_close() will be called by our caller */ | 425 | if (err) /* line_close() will be called by our caller */ |
427 | return err; | 426 | goto out_unlock; |
428 | 427 | ||
429 | INIT_DELAYED_WORK(&line->task, line_timer_cb); | 428 | INIT_DELAYED_WORK(&line->task, line_timer_cb); |
430 | 429 | ||
@@ -435,11 +434,8 @@ int line_open(struct line *lines, struct tty_struct *tty) | |||
435 | 434 | ||
436 | chan_window_size(&line->chan_list, &tty->winsize.ws_row, | 435 | chan_window_size(&line->chan_list, &tty->winsize.ws_row, |
437 | &tty->winsize.ws_col); | 436 | &tty->winsize.ws_col); |
438 | |||
439 | return 0; | ||
440 | |||
441 | out_unlock: | 437 | out_unlock: |
442 | spin_unlock(&line->count_lock); | 438 | mutex_unlock(&line->count_lock); |
443 | return err; | 439 | return err; |
444 | } | 440 | } |
445 | 441 | ||
@@ -459,7 +455,7 @@ void line_close(struct tty_struct *tty, struct file * filp) | |||
459 | /* We ignore the error anyway! */ | 455 | /* We ignore the error anyway! */ |
460 | flush_buffer(line); | 456 | flush_buffer(line); |
461 | 457 | ||
462 | spin_lock(&line->count_lock); | 458 | mutex_lock(&line->count_lock); |
463 | BUG_ON(!line->valid); | 459 | BUG_ON(!line->valid); |
464 | 460 | ||
465 | if (--line->count) | 461 | if (--line->count) |
@@ -468,17 +464,13 @@ void line_close(struct tty_struct *tty, struct file * filp) | |||
468 | line->tty = NULL; | 464 | line->tty = NULL; |
469 | tty->driver_data = NULL; | 465 | tty->driver_data = NULL; |
470 | 466 | ||
471 | spin_unlock(&line->count_lock); | ||
472 | |||
473 | if (line->sigio) { | 467 | if (line->sigio) { |
474 | unregister_winch(tty); | 468 | unregister_winch(tty); |
475 | line->sigio = 0; | 469 | line->sigio = 0; |
476 | } | 470 | } |
477 | 471 | ||
478 | return; | ||
479 | |||
480 | out_unlock: | 472 | out_unlock: |
481 | spin_unlock(&line->count_lock); | 473 | mutex_unlock(&line->count_lock); |
482 | } | 474 | } |
483 | 475 | ||
484 | void close_lines(struct line *lines, int nlines) | 476 | void close_lines(struct line *lines, int nlines) |
@@ -495,7 +487,7 @@ static int setup_one_line(struct line *lines, int n, char *init, | |||
495 | struct line *line = &lines[n]; | 487 | struct line *line = &lines[n]; |
496 | int err = -EINVAL; | 488 | int err = -EINVAL; |
497 | 489 | ||
498 | spin_lock(&line->count_lock); | 490 | mutex_lock(&line->count_lock); |
499 | 491 | ||
500 | if (line->count) { | 492 | if (line->count) { |
501 | *error_out = "Device is already open"; | 493 | *error_out = "Device is already open"; |
@@ -510,7 +502,7 @@ static int setup_one_line(struct line *lines, int n, char *init, | |||
510 | } | 502 | } |
511 | err = 0; | 503 | err = 0; |
512 | out: | 504 | out: |
513 | spin_unlock(&line->count_lock); | 505 | mutex_unlock(&line->count_lock); |
514 | return err; | 506 | return err; |
515 | } | 507 | } |
516 | 508 | ||
@@ -609,13 +601,13 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str, | |||
609 | 601 | ||
610 | line = &lines[dev]; | 602 | line = &lines[dev]; |
611 | 603 | ||
612 | spin_lock(&line->count_lock); | 604 | mutex_lock(&line->count_lock); |
613 | if (!line->valid) | 605 | if (!line->valid) |
614 | CONFIG_CHUNK(str, size, n, "none", 1); | 606 | CONFIG_CHUNK(str, size, n, "none", 1); |
615 | else if (line->tty == NULL) | 607 | else if (line->tty == NULL) |
616 | CONFIG_CHUNK(str, size, n, line->init_str, 1); | 608 | CONFIG_CHUNK(str, size, n, line->init_str, 1); |
617 | else n = chan_config_string(&line->chan_list, str, size, error_out); | 609 | else n = chan_config_string(&line->chan_list, str, size, error_out); |
618 | spin_unlock(&line->count_lock); | 610 | mutex_unlock(&line->count_lock); |
619 | 611 | ||
620 | return n; | 612 | return n; |
621 | } | 613 | } |