aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/drivers/line.c
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2007-02-10 04:43:52 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-11 13:51:21 -0500
commitd79a580936396bbcd2f4fae2c6215f9cf81e3c0d (patch)
tree72c91c7475e2562c5459c4d6d0499cdd671e0b50 /arch/um/drivers/line.c
parent5cf885d01f30be710a339976c485f92bb8a8946d (diff)
[PATCH] uml: console locking fixes
Clean up the console driver locking. There are various problems here, including sleeping under a spinlock and spinlock recursion, some of which are fixed here. This patch deals with the locking involved with opens and closes. The problem is that an mconsole request to change a console's configuration can race with an open. Changing a configuration should only be done when a console isn't opened. Also, an open must be looking at a stable configuration. In addition, a get configuration request must observe the same locking since it must also see a stable configuration. With the old locking, it was possible for this to hang indefinitely in some cases because open would block for a long time waiting for a connection from the host while holding the lock needed by the mconsole request. As explained in the long comment, this is fixed by adding a spinlock for the use count and configuration and a mutex for the actual open and close. Signed-off-by: Jeff Dike <jdike@addtoit.com> Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/um/drivers/line.c')
-rw-r--r--arch/um/drivers/line.c186
1 files changed, 122 insertions, 64 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 83301e1ef67c..799fca3644e9 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -191,7 +191,6 @@ void line_flush_buffer(struct tty_struct *tty)
191 /*XXX: copied from line_write, verify if it is correct!*/ 191 /*XXX: copied from line_write, verify if it is correct!*/
192 if(tty->stopped) 192 if(tty->stopped)
193 return; 193 return;
194 //return 0;
195 194
196 spin_lock_irqsave(&line->lock, flags); 195 spin_lock_irqsave(&line->lock, flags);
197 err = flush_buffer(line); 196 err = flush_buffer(line);
@@ -421,42 +420,84 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
421 return err; 420 return err;
422} 421}
423 422
423/* Normally, a driver like this can rely mostly on the tty layer
424 * locking, particularly when it comes to the driver structure.
425 * However, in this case, mconsole requests can come in "from the
426 * side", and race with opens and closes.
427 *
428 * The problem comes from line_setup not wanting to sleep if
429 * the device is open or being opened. This can happen because the
430 * first opener of a device is responsible for setting it up on the
431 * host, and that can sleep. The open of a port device will sleep
432 * until someone telnets to it.
433 *
434 * The obvious solution of putting everything under a mutex fails
435 * because then trying (and failing) to change the configuration of an
436 * open(ing) device will block until the open finishes. The right
437 * thing to happen is for it to fail immediately.
438 *
439 * We can put the opening (and closing) of the host device under a
440 * separate lock, but that has to be taken before the count lock is
441 * released. Otherwise, you open a window in which another open can
442 * come through and assume that the host side is opened and working.
443 *
444 * So, if the tty count is one, open will take the open mutex
445 * inside the count lock. Otherwise, it just returns. This will sleep
446 * if the last close is pending, and will block a setup or get_config,
447 * but that should not last long.
448 *
449 * So, what we end up with is that open and close take the count lock.
450 * If the first open or last close are happening, then the open mutex
451 * is taken inside the count lock and the host opening or closing is done.
452 *
453 * setup and get_config only take the count lock. setup modifies the
454 * device configuration only if the open count is zero. Arbitrarily
455 * long blocking of setup doesn't happen because something would have to be
456 * waiting for an open to happen. However, a second open with
457 * tty->count == 1 can't happen, and a close can't happen until the open
458 * had finished.
459 *
460 * We can't maintain our own count here because the tty layer doesn't
461 * match opens and closes. It will call close if an open failed, and
462 * a tty hangup will result in excess closes. So, we rely on
463 * tty->count instead. It is one on both the first open and last close.
464 */
465
424int line_open(struct line *lines, struct tty_struct *tty) 466int line_open(struct line *lines, struct tty_struct *tty)
425{ 467{
426 struct line *line; 468 struct line *line = &lines[tty->index];
427 int err = -ENODEV; 469 int err = -ENODEV;
428 470
429 line = &lines[tty->index]; 471 spin_lock(&line->count_lock);
430 tty->driver_data = line; 472 if(!line->valid)
473 goto out_unlock;
474
475 err = 0;
476 if(tty->count > 1)
477 goto out_unlock;
431 478
432 /* The IRQ which takes this lock is not yet enabled and won't be run 479 mutex_lock(&line->open_mutex);
433 * before the end, so we don't need to use spin_lock_irq.*/ 480 spin_unlock(&line->count_lock);
434 spin_lock(&line->lock);
435 481
436 tty->driver_data = line; 482 tty->driver_data = line;
437 line->tty = tty; 483 line->tty = tty;
438 if(!line->valid)
439 goto out;
440 484
441 if(tty->count == 1){ 485 enable_chan(line);
442 /* Here the device is opened, if necessary, and interrupt 486 INIT_DELAYED_WORK(&line->task, line_timer_cb);
443 * is registered.
444 */
445 enable_chan(line);
446 INIT_DELAYED_WORK(&line->task, line_timer_cb);
447
448 if(!line->sigio){
449 chan_enable_winch(&line->chan_list, tty);
450 line->sigio = 1;
451 }
452 487
453 chan_window_size(&line->chan_list, &tty->winsize.ws_row, 488 if(!line->sigio){
454 &tty->winsize.ws_col); 489 chan_enable_winch(&line->chan_list, tty);
490 line->sigio = 1;
455 } 491 }
456 492
457 err = 0; 493 chan_window_size(&line->chan_list, &tty->winsize.ws_row,
458out: 494 &tty->winsize.ws_col);
459 spin_unlock(&line->lock); 495
496 mutex_unlock(&line->open_mutex);
497 return err;
498
499out_unlock:
500 spin_unlock(&line->count_lock);
460 return err; 501 return err;
461} 502}
462 503
@@ -466,25 +507,38 @@ void line_close(struct tty_struct *tty, struct file * filp)
466{ 507{
467 struct line *line = tty->driver_data; 508 struct line *line = tty->driver_data;
468 509
469 /* XXX: I assume this should be called in process context, not with 510 /* If line_open fails (and tty->driver_data is never set),
470 * interrupts disabled! 511 * tty_open will call line_close. So just return in this case.
471 */ 512 */
472 spin_lock_irq(&line->lock); 513 if(line == NULL)
514 return;
473 515
474 /* We ignore the error anyway! */ 516 /* We ignore the error anyway! */
475 flush_buffer(line); 517 flush_buffer(line);
476 518
477 if(tty->count == 1){ 519 spin_lock(&line->count_lock);
478 line->tty = NULL; 520 if(!line->valid)
479 tty->driver_data = NULL; 521 goto out_unlock;
522
523 if(tty->count > 1)
524 goto out_unlock;
480 525
481 if(line->sigio){ 526 mutex_lock(&line->open_mutex);
482 unregister_winch(tty); 527 spin_unlock(&line->count_lock);
483 line->sigio = 0; 528
484 } 529 line->tty = NULL;
530 tty->driver_data = NULL;
531
532 if(line->sigio){
533 unregister_winch(tty);
534 line->sigio = 0;
485 } 535 }
486 536
487 spin_unlock_irq(&line->lock); 537 mutex_unlock(&line->open_mutex);
538 return;
539
540out_unlock:
541 spin_unlock(&line->count_lock);
488} 542}
489 543
490void close_lines(struct line *lines, int nlines) 544void close_lines(struct line *lines, int nlines)
@@ -495,6 +549,30 @@ void close_lines(struct line *lines, int nlines)
495 close_chan(&lines[i].chan_list, 0); 549 close_chan(&lines[i].chan_list, 0);
496} 550}
497 551
552static void setup_one_line(struct line *lines, int n, char *init, int init_prio)
553{
554 struct line *line = &lines[n];
555
556 spin_lock(&line->count_lock);
557
558 if(line->tty != NULL){
559 printk("line_setup - device %d is open\n", n);
560 goto out;
561 }
562
563 if (line->init_pri <= init_prio){
564 line->init_pri = init_prio;
565 if (!strcmp(init, "none"))
566 line->valid = 0;
567 else {
568 line->init_str = init;
569 line->valid = 1;
570 }
571 }
572out:
573 spin_unlock(&line->count_lock);
574}
575
498/* Common setup code for both startup command line and mconsole initialization. 576/* Common setup code for both startup command line and mconsole initialization.
499 * @lines contains the array (of size @num) to modify; 577 * @lines contains the array (of size @num) to modify;
500 * @init is the setup string; 578 * @init is the setup string;
@@ -526,32 +604,11 @@ int line_setup(struct line *lines, unsigned int num, char *init)
526 n, num - 1); 604 n, num - 1);
527 return 0; 605 return 0;
528 } 606 }
529 else if (n >= 0){ 607 else if (n >= 0)
530 if (lines[n].tty != NULL) { 608 setup_one_line(lines, n, init, INIT_ONE);
531 printk("line_setup - device %d is open\n", n);
532 return 0;
533 }
534 if (lines[n].init_pri <= INIT_ONE){
535 lines[n].init_pri = INIT_ONE;
536 if (!strcmp(init, "none"))
537 lines[n].valid = 0;
538 else {
539 lines[n].init_str = init;
540 lines[n].valid = 1;
541 }
542 }
543 }
544 else { 609 else {
545 for(i = 0; i < num; i++){ 610 for(i = 0; i < num; i++)
546 if(lines[i].init_pri <= INIT_ALL){ 611 setup_one_line(lines, i, init, INIT_ALL);
547 lines[i].init_pri = INIT_ALL;
548 if(!strcmp(init, "none")) lines[i].valid = 0;
549 else {
550 lines[i].init_str = init;
551 lines[i].valid = 1;
552 }
553 }
554 }
555 } 612 }
556 return n == -1 ? num : n; 613 return n == -1 ? num : n;
557} 614}
@@ -602,13 +659,13 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
602 659
603 line = &lines[dev]; 660 line = &lines[dev];
604 661
605 spin_lock(&line->lock); 662 spin_lock(&line->count_lock);
606 if(!line->valid) 663 if(!line->valid)
607 CONFIG_CHUNK(str, size, n, "none", 1); 664 CONFIG_CHUNK(str, size, n, "none", 1);
608 else if(line->tty == NULL) 665 else if(line->tty == NULL)
609 CONFIG_CHUNK(str, size, n, line->init_str, 1); 666 CONFIG_CHUNK(str, size, n, line->init_str, 1);
610 else n = chan_config_string(&line->chan_list, str, size, error_out); 667 else n = chan_config_string(&line->chan_list, str, size, error_out);
611 spin_unlock(&line->lock); 668 spin_unlock(&line->count_lock);
612 669
613 return n; 670 return n;
614} 671}
@@ -688,6 +745,7 @@ void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
688 for(i = 0; i < nlines; i++){ 745 for(i = 0; i < nlines; i++){
689 line = &lines[i]; 746 line = &lines[i];
690 INIT_LIST_HEAD(&line->chan_list); 747 INIT_LIST_HEAD(&line->chan_list);
748 mutex_init(&line->open_mutex);
691 749
692 if(line->init_str == NULL) 750 if(line->init_str == NULL)
693 continue; 751 continue;