diff options
Diffstat (limited to 'kernel/sys.c')
-rw-r--r-- | kernel/sys.c | 133 |
1 files changed, 95 insertions, 38 deletions
diff --git a/kernel/sys.c b/kernel/sys.c index f006632c2ba7..9a24374c23bc 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/highuid.h> | 17 | #include <linux/highuid.h> |
18 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
19 | #include <linux/kernel.h> | ||
20 | #include <linux/kexec.h> | ||
19 | #include <linux/workqueue.h> | 21 | #include <linux/workqueue.h> |
20 | #include <linux/device.h> | 22 | #include <linux/device.h> |
21 | #include <linux/key.h> | 23 | #include <linux/key.h> |
@@ -405,6 +407,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user | |||
405 | case LINUX_REBOOT_CMD_HALT: | 407 | case LINUX_REBOOT_CMD_HALT: |
406 | notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL); | 408 | notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL); |
407 | system_state = SYSTEM_HALT; | 409 | system_state = SYSTEM_HALT; |
410 | device_suspend(PMSG_SUSPEND); | ||
408 | device_shutdown(); | 411 | device_shutdown(); |
409 | printk(KERN_EMERG "System halted.\n"); | 412 | printk(KERN_EMERG "System halted.\n"); |
410 | machine_halt(); | 413 | machine_halt(); |
@@ -415,6 +418,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user | |||
415 | case LINUX_REBOOT_CMD_POWER_OFF: | 418 | case LINUX_REBOOT_CMD_POWER_OFF: |
416 | notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL); | 419 | notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL); |
417 | system_state = SYSTEM_POWER_OFF; | 420 | system_state = SYSTEM_POWER_OFF; |
421 | device_suspend(PMSG_SUSPEND); | ||
418 | device_shutdown(); | 422 | device_shutdown(); |
419 | printk(KERN_EMERG "Power down.\n"); | 423 | printk(KERN_EMERG "Power down.\n"); |
420 | machine_power_off(); | 424 | machine_power_off(); |
@@ -431,11 +435,30 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user | |||
431 | 435 | ||
432 | notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer); | 436 | notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer); |
433 | system_state = SYSTEM_RESTART; | 437 | system_state = SYSTEM_RESTART; |
438 | device_suspend(PMSG_FREEZE); | ||
434 | device_shutdown(); | 439 | device_shutdown(); |
435 | printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer); | 440 | printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer); |
436 | machine_restart(buffer); | 441 | machine_restart(buffer); |
437 | break; | 442 | break; |
438 | 443 | ||
444 | #ifdef CONFIG_KEXEC | ||
445 | case LINUX_REBOOT_CMD_KEXEC: | ||
446 | { | ||
447 | struct kimage *image; | ||
448 | image = xchg(&kexec_image, 0); | ||
449 | if (!image) { | ||
450 | unlock_kernel(); | ||
451 | return -EINVAL; | ||
452 | } | ||
453 | notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); | ||
454 | system_state = SYSTEM_RESTART; | ||
455 | device_shutdown(); | ||
456 | printk(KERN_EMERG "Starting new kernel\n"); | ||
457 | machine_shutdown(); | ||
458 | machine_kexec(image); | ||
459 | break; | ||
460 | } | ||
461 | #endif | ||
439 | #ifdef CONFIG_SOFTWARE_SUSPEND | 462 | #ifdef CONFIG_SOFTWARE_SUSPEND |
440 | case LINUX_REBOOT_CMD_SW_SUSPEND: | 463 | case LINUX_REBOOT_CMD_SW_SUSPEND: |
441 | { | 464 | { |
@@ -525,7 +548,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) | |||
525 | } | 548 | } |
526 | if (new_egid != old_egid) | 549 | if (new_egid != old_egid) |
527 | { | 550 | { |
528 | current->mm->dumpable = 0; | 551 | current->mm->dumpable = suid_dumpable; |
529 | smp_wmb(); | 552 | smp_wmb(); |
530 | } | 553 | } |
531 | if (rgid != (gid_t) -1 || | 554 | if (rgid != (gid_t) -1 || |
@@ -556,7 +579,7 @@ asmlinkage long sys_setgid(gid_t gid) | |||
556 | { | 579 | { |
557 | if(old_egid != gid) | 580 | if(old_egid != gid) |
558 | { | 581 | { |
559 | current->mm->dumpable=0; | 582 | current->mm->dumpable = suid_dumpable; |
560 | smp_wmb(); | 583 | smp_wmb(); |
561 | } | 584 | } |
562 | current->gid = current->egid = current->sgid = current->fsgid = gid; | 585 | current->gid = current->egid = current->sgid = current->fsgid = gid; |
@@ -565,7 +588,7 @@ asmlinkage long sys_setgid(gid_t gid) | |||
565 | { | 588 | { |
566 | if(old_egid != gid) | 589 | if(old_egid != gid) |
567 | { | 590 | { |
568 | current->mm->dumpable=0; | 591 | current->mm->dumpable = suid_dumpable; |
569 | smp_wmb(); | 592 | smp_wmb(); |
570 | } | 593 | } |
571 | current->egid = current->fsgid = gid; | 594 | current->egid = current->fsgid = gid; |
@@ -596,7 +619,7 @@ static int set_user(uid_t new_ruid, int dumpclear) | |||
596 | 619 | ||
597 | if(dumpclear) | 620 | if(dumpclear) |
598 | { | 621 | { |
599 | current->mm->dumpable = 0; | 622 | current->mm->dumpable = suid_dumpable; |
600 | smp_wmb(); | 623 | smp_wmb(); |
601 | } | 624 | } |
602 | current->uid = new_ruid; | 625 | current->uid = new_ruid; |
@@ -653,7 +676,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) | |||
653 | 676 | ||
654 | if (new_euid != old_euid) | 677 | if (new_euid != old_euid) |
655 | { | 678 | { |
656 | current->mm->dumpable=0; | 679 | current->mm->dumpable = suid_dumpable; |
657 | smp_wmb(); | 680 | smp_wmb(); |
658 | } | 681 | } |
659 | current->fsuid = current->euid = new_euid; | 682 | current->fsuid = current->euid = new_euid; |
@@ -703,7 +726,7 @@ asmlinkage long sys_setuid(uid_t uid) | |||
703 | 726 | ||
704 | if (old_euid != uid) | 727 | if (old_euid != uid) |
705 | { | 728 | { |
706 | current->mm->dumpable = 0; | 729 | current->mm->dumpable = suid_dumpable; |
707 | smp_wmb(); | 730 | smp_wmb(); |
708 | } | 731 | } |
709 | current->fsuid = current->euid = uid; | 732 | current->fsuid = current->euid = uid; |
@@ -748,7 +771,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) | |||
748 | if (euid != (uid_t) -1) { | 771 | if (euid != (uid_t) -1) { |
749 | if (euid != current->euid) | 772 | if (euid != current->euid) |
750 | { | 773 | { |
751 | current->mm->dumpable = 0; | 774 | current->mm->dumpable = suid_dumpable; |
752 | smp_wmb(); | 775 | smp_wmb(); |
753 | } | 776 | } |
754 | current->euid = euid; | 777 | current->euid = euid; |
@@ -798,7 +821,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) | |||
798 | if (egid != (gid_t) -1) { | 821 | if (egid != (gid_t) -1) { |
799 | if (egid != current->egid) | 822 | if (egid != current->egid) |
800 | { | 823 | { |
801 | current->mm->dumpable = 0; | 824 | current->mm->dumpable = suid_dumpable; |
802 | smp_wmb(); | 825 | smp_wmb(); |
803 | } | 826 | } |
804 | current->egid = egid; | 827 | current->egid = egid; |
@@ -845,7 +868,7 @@ asmlinkage long sys_setfsuid(uid_t uid) | |||
845 | { | 868 | { |
846 | if (uid != old_fsuid) | 869 | if (uid != old_fsuid) |
847 | { | 870 | { |
848 | current->mm->dumpable = 0; | 871 | current->mm->dumpable = suid_dumpable; |
849 | smp_wmb(); | 872 | smp_wmb(); |
850 | } | 873 | } |
851 | current->fsuid = uid; | 874 | current->fsuid = uid; |
@@ -875,7 +898,7 @@ asmlinkage long sys_setfsgid(gid_t gid) | |||
875 | { | 898 | { |
876 | if (gid != old_fsgid) | 899 | if (gid != old_fsgid) |
877 | { | 900 | { |
878 | current->mm->dumpable = 0; | 901 | current->mm->dumpable = suid_dumpable; |
879 | smp_wmb(); | 902 | smp_wmb(); |
880 | } | 903 | } |
881 | current->fsgid = gid; | 904 | current->fsgid = gid; |
@@ -894,35 +917,69 @@ asmlinkage long sys_times(struct tms __user * tbuf) | |||
894 | */ | 917 | */ |
895 | if (tbuf) { | 918 | if (tbuf) { |
896 | struct tms tmp; | 919 | struct tms tmp; |
897 | struct task_struct *tsk = current; | ||
898 | struct task_struct *t; | ||
899 | cputime_t utime, stime, cutime, cstime; | 920 | cputime_t utime, stime, cutime, cstime; |
900 | 921 | ||
901 | read_lock(&tasklist_lock); | 922 | #ifdef CONFIG_SMP |
902 | utime = tsk->signal->utime; | 923 | if (thread_group_empty(current)) { |
903 | stime = tsk->signal->stime; | 924 | /* |
904 | t = tsk; | 925 | * Single thread case without the use of any locks. |
905 | do { | 926 | * |
906 | utime = cputime_add(utime, t->utime); | 927 | * We may race with release_task if two threads are |
907 | stime = cputime_add(stime, t->stime); | 928 | * executing. However, release task first adds up the |
908 | t = next_thread(t); | 929 | * counters (__exit_signal) before removing the task |
909 | } while (t != tsk); | 930 | * from the process tasklist (__unhash_process). |
910 | 931 | * __exit_signal also acquires and releases the | |
911 | /* | 932 | * siglock which results in the proper memory ordering |
912 | * While we have tasklist_lock read-locked, no dying thread | 933 | * so that the list modifications are always visible |
913 | * can be updating current->signal->[us]time. Instead, | 934 | * after the counters have been updated. |
914 | * we got their counts included in the live thread loop. | 935 | * |
915 | * However, another thread can come in right now and | 936 | * If the counters have been updated by the second thread |
916 | * do a wait call that updates current->signal->c[us]time. | 937 | * but the thread has not yet been removed from the list |
917 | * To make sure we always see that pair updated atomically, | 938 | * then the other branch will be executing which will |
918 | * we take the siglock around fetching them. | 939 | * block on tasklist_lock until the exit handling of the |
919 | */ | 940 | * other task is finished. |
920 | spin_lock_irq(&tsk->sighand->siglock); | 941 | * |
921 | cutime = tsk->signal->cutime; | 942 | * This also implies that the sighand->siglock cannot |
922 | cstime = tsk->signal->cstime; | 943 | * be held by another processor. So we can also |
923 | spin_unlock_irq(&tsk->sighand->siglock); | 944 | * skip acquiring that lock. |
924 | read_unlock(&tasklist_lock); | 945 | */ |
946 | utime = cputime_add(current->signal->utime, current->utime); | ||
947 | stime = cputime_add(current->signal->utime, current->stime); | ||
948 | cutime = current->signal->cutime; | ||
949 | cstime = current->signal->cstime; | ||
950 | } else | ||
951 | #endif | ||
952 | { | ||
925 | 953 | ||
954 | /* Process with multiple threads */ | ||
955 | struct task_struct *tsk = current; | ||
956 | struct task_struct *t; | ||
957 | |||
958 | read_lock(&tasklist_lock); | ||
959 | utime = tsk->signal->utime; | ||
960 | stime = tsk->signal->stime; | ||
961 | t = tsk; | ||
962 | do { | ||
963 | utime = cputime_add(utime, t->utime); | ||
964 | stime = cputime_add(stime, t->stime); | ||
965 | t = next_thread(t); | ||
966 | } while (t != tsk); | ||
967 | |||
968 | /* | ||
969 | * While we have tasklist_lock read-locked, no dying thread | ||
970 | * can be updating current->signal->[us]time. Instead, | ||
971 | * we got their counts included in the live thread loop. | ||
972 | * However, another thread can come in right now and | ||
973 | * do a wait call that updates current->signal->c[us]time. | ||
974 | * To make sure we always see that pair updated atomically, | ||
975 | * we take the siglock around fetching them. | ||
976 | */ | ||
977 | spin_lock_irq(&tsk->sighand->siglock); | ||
978 | cutime = tsk->signal->cutime; | ||
979 | cstime = tsk->signal->cstime; | ||
980 | spin_unlock_irq(&tsk->sighand->siglock); | ||
981 | read_unlock(&tasklist_lock); | ||
982 | } | ||
926 | tmp.tms_utime = cputime_to_clock_t(utime); | 983 | tmp.tms_utime = cputime_to_clock_t(utime); |
927 | tmp.tms_stime = cputime_to_clock_t(stime); | 984 | tmp.tms_stime = cputime_to_clock_t(stime); |
928 | tmp.tms_cutime = cputime_to_clock_t(cutime); | 985 | tmp.tms_cutime = cputime_to_clock_t(cutime); |
@@ -1225,7 +1282,7 @@ static void groups_sort(struct group_info *group_info) | |||
1225 | } | 1282 | } |
1226 | 1283 | ||
1227 | /* a simple bsearch */ | 1284 | /* a simple bsearch */ |
1228 | static int groups_search(struct group_info *group_info, gid_t grp) | 1285 | int groups_search(struct group_info *group_info, gid_t grp) |
1229 | { | 1286 | { |
1230 | int left, right; | 1287 | int left, right; |
1231 | 1288 | ||
@@ -1652,7 +1709,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
1652 | error = 1; | 1709 | error = 1; |
1653 | break; | 1710 | break; |
1654 | case PR_SET_DUMPABLE: | 1711 | case PR_SET_DUMPABLE: |
1655 | if (arg2 != 0 && arg2 != 1) { | 1712 | if (arg2 < 0 || arg2 > 2) { |
1656 | error = -EINVAL; | 1713 | error = -EINVAL; |
1657 | break; | 1714 | break; |
1658 | } | 1715 | } |