summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorWaiman Long <longman@redhat.com>2016-11-23 14:06:45 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-29 15:42:40 -0500
commit7edd7e82b96bcbcc2f9437449df29fde0f08295e (patch)
tree56a205cd9e02c8a7606f46d287997140d25f8559 /drivers/tty
parent5020ded78348092eac5e9909018f6d53e24eadb6 (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.c115
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
26static unsigned short translations[][256] = { 38static 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)
309int con_set_trans_old(unsigned char __user * arg) 321int 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
347int con_set_trans_new(ushort __user * arg) 364int 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)
367int con_get_trans_new(ushort __user * arg) 382int 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
641out_unlock: 662out_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 */
736int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) 758int 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