diff options
author | Waiman Long <longman@redhat.com> | 2016-11-23 14:06:45 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-11-29 15:42:40 -0500 |
commit | 7edd7e82b96bcbcc2f9437449df29fde0f08295e (patch) | |
tree | 56a205cd9e02c8a7606f46d287997140d25f8559 /drivers/tty | |
parent | 5020ded78348092eac5e9909018f6d53e24eadb6 (diff) |
console: Move userspace I/O out of console_lock to fix lockdep warning
When running certain workload on a debug kernel with lockdep turned on,
a ppc64 kvm guest could sometimes hit the following lockdep warning:
[ INFO: possible circular locking dependency detected ]
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(&mm->mmap_sem);
lock(console_lock);
lock(&mm->mmap_sem);
lock(cpu_hotplug.lock);
*** DEADLOCK ***
Looking at the console code, the console_lock-->mmap_sem scenario will
only happen when reading or writing the console unicode map leading to
a page fault.
To break this circular locking dependency, all the userspace I/O
operations in consolemap.c are now moved outside of the console_lock
critical sections so that the mmap_sem won't be acquired when holding
the console_lock.
Signed-off-by: Waiman Long <longman@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/vt/consolemap.c | 115 |
1 files changed, 74 insertions, 41 deletions
diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 9d7ab7b66a8a..71e81406ef71 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c | |||
@@ -9,6 +9,17 @@ | |||
9 | * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998 | 9 | * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998 |
10 | * | 10 | * |
11 | * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998 | 11 | * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998 |
12 | * | ||
13 | * In order to prevent the following circular lock dependency: | ||
14 | * &mm->mmap_sem --> cpu_hotplug.lock --> console_lock --> &mm->mmap_sem | ||
15 | * | ||
16 | * We cannot allow page fault to happen while holding the console_lock. | ||
17 | * Therefore, all the userspace copy operations have to be done outside | ||
18 | * the console_lock critical sections. | ||
19 | * | ||
20 | * As all the affected functions are all called directly from vt_ioctl(), we | ||
21 | * can allocate some small buffers directly on stack without worrying about | ||
22 | * stack overflow. | ||
12 | */ | 23 | */ |
13 | 24 | ||
14 | #include <linux/module.h> | 25 | #include <linux/module.h> |
@@ -22,6 +33,7 @@ | |||
22 | #include <linux/console.h> | 33 | #include <linux/console.h> |
23 | #include <linux/consolemap.h> | 34 | #include <linux/consolemap.h> |
24 | #include <linux/vt_kern.h> | 35 | #include <linux/vt_kern.h> |
36 | #include <linux/string.h> | ||
25 | 37 | ||
26 | static unsigned short translations[][256] = { | 38 | static unsigned short translations[][256] = { |
27 | /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ | 39 | /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ |
@@ -309,18 +321,19 @@ static void update_user_maps(void) | |||
309 | int con_set_trans_old(unsigned char __user * arg) | 321 | int con_set_trans_old(unsigned char __user * arg) |
310 | { | 322 | { |
311 | int i; | 323 | int i; |
312 | unsigned short *p = translations[USER_MAP]; | 324 | unsigned short inbuf[E_TABSZ]; |
313 | 325 | ||
314 | if (!access_ok(VERIFY_READ, arg, E_TABSZ)) | 326 | if (!access_ok(VERIFY_READ, arg, E_TABSZ)) |
315 | return -EFAULT; | 327 | return -EFAULT; |
316 | 328 | ||
317 | console_lock(); | 329 | for (i = 0; i < E_TABSZ ; i++) { |
318 | for (i=0; i<E_TABSZ ; i++) { | ||
319 | unsigned char uc; | 330 | unsigned char uc; |
320 | __get_user(uc, arg+i); | 331 | __get_user(uc, arg+i); |
321 | p[i] = UNI_DIRECT_BASE | uc; | 332 | inbuf[i] = UNI_DIRECT_BASE | uc; |
322 | } | 333 | } |
323 | 334 | ||
335 | console_lock(); | ||
336 | memcpy(translations[USER_MAP], inbuf, sizeof(inbuf)); | ||
324 | update_user_maps(); | 337 | update_user_maps(); |
325 | console_unlock(); | 338 | console_unlock(); |
326 | return 0; | 339 | return 0; |
@@ -330,35 +343,37 @@ int con_get_trans_old(unsigned char __user * arg) | |||
330 | { | 343 | { |
331 | int i, ch; | 344 | int i, ch; |
332 | unsigned short *p = translations[USER_MAP]; | 345 | unsigned short *p = translations[USER_MAP]; |
346 | unsigned char outbuf[E_TABSZ]; | ||
333 | 347 | ||
334 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ)) | 348 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ)) |
335 | return -EFAULT; | 349 | return -EFAULT; |
336 | 350 | ||
337 | console_lock(); | 351 | console_lock(); |
338 | for (i=0; i<E_TABSZ ; i++) | 352 | for (i = 0; i < E_TABSZ ; i++) |
339 | { | 353 | { |
340 | ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); | 354 | ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); |
341 | __put_user((ch & ~0xff) ? 0 : ch, arg+i); | 355 | outbuf[i] = (ch & ~0xff) ? 0 : ch; |
342 | } | 356 | } |
343 | console_unlock(); | 357 | console_unlock(); |
358 | |||
359 | for (i = 0; i < E_TABSZ ; i++) | ||
360 | __put_user(outbuf[i], arg+i); | ||
344 | return 0; | 361 | return 0; |
345 | } | 362 | } |
346 | 363 | ||
347 | int con_set_trans_new(ushort __user * arg) | 364 | int con_set_trans_new(ushort __user * arg) |
348 | { | 365 | { |
349 | int i; | 366 | int i; |
350 | unsigned short *p = translations[USER_MAP]; | 367 | unsigned short inbuf[E_TABSZ]; |
351 | 368 | ||
352 | if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short))) | 369 | if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short))) |
353 | return -EFAULT; | 370 | return -EFAULT; |
354 | 371 | ||
355 | console_lock(); | 372 | for (i = 0; i < E_TABSZ ; i++) |
356 | for (i=0; i<E_TABSZ ; i++) { | 373 | __get_user(inbuf[i], arg+i); |
357 | unsigned short us; | ||
358 | __get_user(us, arg+i); | ||
359 | p[i] = us; | ||
360 | } | ||
361 | 374 | ||
375 | console_lock(); | ||
376 | memcpy(translations[USER_MAP], inbuf, sizeof(inbuf)); | ||
362 | update_user_maps(); | 377 | update_user_maps(); |
363 | console_unlock(); | 378 | console_unlock(); |
364 | return 0; | 379 | return 0; |
@@ -367,16 +382,17 @@ int con_set_trans_new(ushort __user * arg) | |||
367 | int con_get_trans_new(ushort __user * arg) | 382 | int con_get_trans_new(ushort __user * arg) |
368 | { | 383 | { |
369 | int i; | 384 | int i; |
370 | unsigned short *p = translations[USER_MAP]; | 385 | unsigned short outbuf[E_TABSZ]; |
371 | 386 | ||
372 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short))) | 387 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short))) |
373 | return -EFAULT; | 388 | return -EFAULT; |
374 | 389 | ||
375 | console_lock(); | 390 | console_lock(); |
376 | for (i=0; i<E_TABSZ ; i++) | 391 | memcpy(outbuf, translations[USER_MAP], sizeof(outbuf)); |
377 | __put_user(p[i], arg+i); | ||
378 | console_unlock(); | 392 | console_unlock(); |
379 | 393 | ||
394 | for (i = 0; i < E_TABSZ ; i++) | ||
395 | __put_user(outbuf[i], arg+i); | ||
380 | return 0; | 396 | return 0; |
381 | } | 397 | } |
382 | 398 | ||
@@ -536,10 +552,20 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
536 | { | 552 | { |
537 | int err = 0, err1, i; | 553 | int err = 0, err1, i; |
538 | struct uni_pagedir *p, *q; | 554 | struct uni_pagedir *p, *q; |
555 | struct unipair *unilist, *plist; | ||
539 | 556 | ||
540 | if (!ct) | 557 | if (!ct) |
541 | return 0; | 558 | return 0; |
542 | 559 | ||
560 | unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); | ||
561 | if (!unilist) | ||
562 | return -ENOMEM; | ||
563 | |||
564 | for (i = ct, plist = unilist; i; i--, plist++, list++) { | ||
565 | __get_user(plist->unicode, &list->unicode); | ||
566 | __get_user(plist->fontpos, &list->fontpos); | ||
567 | } | ||
568 | |||
543 | console_lock(); | 569 | console_lock(); |
544 | 570 | ||
545 | /* Save original vc_unipagdir_loc in case we allocate a new one */ | 571 | /* Save original vc_unipagdir_loc in case we allocate a new one */ |
@@ -557,8 +583,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
557 | 583 | ||
558 | err1 = con_do_clear_unimap(vc); | 584 | err1 = con_do_clear_unimap(vc); |
559 | if (err1) { | 585 | if (err1) { |
560 | console_unlock(); | 586 | err = err1; |
561 | return err1; | 587 | goto out_unlock; |
562 | } | 588 | } |
563 | 589 | ||
564 | /* | 590 | /* |
@@ -592,8 +618,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
592 | *vc->vc_uni_pagedir_loc = p; | 618 | *vc->vc_uni_pagedir_loc = p; |
593 | con_release_unimap(q); | 619 | con_release_unimap(q); |
594 | kfree(q); | 620 | kfree(q); |
595 | console_unlock(); | 621 | err = err1; |
596 | return err1; | 622 | goto out_unlock; |
597 | } | 623 | } |
598 | } | 624 | } |
599 | } else { | 625 | } else { |
@@ -617,22 +643,17 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
617 | /* | 643 | /* |
618 | * Insert user specified unicode pairs into new table. | 644 | * Insert user specified unicode pairs into new table. |
619 | */ | 645 | */ |
620 | while (ct--) { | 646 | for (plist = unilist; ct; ct--, plist++) { |
621 | unsigned short unicode, fontpos; | 647 | err1 = con_insert_unipair(p, plist->unicode, plist->fontpos); |
622 | __get_user(unicode, &list->unicode); | 648 | if (err1) |
623 | __get_user(fontpos, &list->fontpos); | ||
624 | if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0) | ||
625 | err = err1; | 649 | err = err1; |
626 | list++; | ||
627 | } | 650 | } |
628 | 651 | ||
629 | /* | 652 | /* |
630 | * Merge with fontmaps of any other virtual consoles. | 653 | * Merge with fontmaps of any other virtual consoles. |
631 | */ | 654 | */ |
632 | if (con_unify_unimap(vc, p)) { | 655 | if (con_unify_unimap(vc, p)) |
633 | console_unlock(); | 656 | goto out_unlock; |
634 | return err; | ||
635 | } | ||
636 | 657 | ||
637 | for (i = 0; i <= 3; i++) | 658 | for (i = 0; i <= 3; i++) |
638 | set_inverse_transl(vc, p, i); /* Update inverse translations */ | 659 | set_inverse_transl(vc, p, i); /* Update inverse translations */ |
@@ -640,6 +661,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
640 | 661 | ||
641 | out_unlock: | 662 | out_unlock: |
642 | console_unlock(); | 663 | console_unlock(); |
664 | kfree(unilist); | ||
643 | return err; | 665 | return err; |
644 | } | 666 | } |
645 | 667 | ||
@@ -735,9 +757,15 @@ EXPORT_SYMBOL(con_copy_unimap); | |||
735 | */ | 757 | */ |
736 | int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) | 758 | int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) |
737 | { | 759 | { |
738 | int i, j, k, ect; | 760 | int i, j, k; |
761 | ushort ect; | ||
739 | u16 **p1, *p2; | 762 | u16 **p1, *p2; |
740 | struct uni_pagedir *p; | 763 | struct uni_pagedir *p; |
764 | struct unipair *unilist, *plist; | ||
765 | |||
766 | unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); | ||
767 | if (!unilist) | ||
768 | return -ENOMEM; | ||
741 | 769 | ||
742 | console_lock(); | 770 | console_lock(); |
743 | 771 | ||
@@ -750,21 +778,26 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni | |||
750 | for (j = 0; j < 32; j++) { | 778 | for (j = 0; j < 32; j++) { |
751 | p2 = *(p1++); | 779 | p2 = *(p1++); |
752 | if (p2) | 780 | if (p2) |
753 | for (k = 0; k < 64; k++) { | 781 | for (k = 0; k < 64; k++, p2++) { |
754 | if (*p2 < MAX_GLYPH && ect++ < ct) { | 782 | if (*p2 >= MAX_GLYPH) |
755 | __put_user((u_short)((i<<11)+(j<<6)+k), | 783 | continue; |
756 | &list->unicode); | 784 | if (ect < ct) { |
757 | __put_user((u_short) *p2, | 785 | unilist[ect].unicode = |
758 | &list->fontpos); | 786 | (i<<11)+(j<<6)+k; |
759 | list++; | 787 | unilist[ect].fontpos = *p2; |
760 | } | 788 | } |
761 | p2++; | 789 | ect++; |
762 | } | 790 | } |
763 | } | 791 | } |
764 | } | 792 | } |
765 | } | 793 | } |
766 | __put_user(ect, uct); | ||
767 | console_unlock(); | 794 | console_unlock(); |
795 | for (i = min(ect, ct), plist = unilist; i; i--, list++, plist++) { | ||
796 | __put_user(plist->unicode, &list->unicode); | ||
797 | __put_user(plist->fontpos, &list->fontpos); | ||
798 | } | ||
799 | __put_user(ect, uct); | ||
800 | kfree(unilist); | ||
768 | return ((ect <= ct) ? 0 : -ENOMEM); | 801 | return ((ect <= ct) ? 0 : -ENOMEM); |
769 | } | 802 | } |
770 | 803 | ||