aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRoman Zippel <zippel@linux-m68k.org>2006-03-26 04:38:08 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-26 11:57:02 -0500
commit432569bb9d9d424d7ffe5b21f8205c55bdd1aaa8 (patch)
treeda649d202625d061d4fca27a6a63c4f81076724e /kernel
parent3b98a5328171cebc867f70484b20bd34948cd7f6 (diff)
[PATCH] hrtimers: simplify nanosleep
nanosleep is the only user of the expired state, so let it manage this itself, which makes the hrtimer code a bit simpler. The remaining time is also only calculated if requested. Signed-off-by: Roman Zippel <zippel@linux-m68k.org> Acked-by: Ingo Molnar <mingo@elte.hu> Acked-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/hrtimer.c142
1 files changed, 62 insertions, 80 deletions
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index e989c9981a96..59ec50c1e905 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -625,30 +625,20 @@ static inline void run_hrtimer_queue(struct hrtimer_base *base)
625 fn = timer->function; 625 fn = timer->function;
626 data = timer->data; 626 data = timer->data;
627 set_curr_timer(base, timer); 627 set_curr_timer(base, timer);
628 timer->state = HRTIMER_RUNNING; 628 timer->state = HRTIMER_INACTIVE;
629 __remove_hrtimer(timer, base); 629 __remove_hrtimer(timer, base);
630 spin_unlock_irq(&base->lock); 630 spin_unlock_irq(&base->lock);
631 631
632 /* 632 restart = fn(data);
633 * fn == NULL is special case for the simplest timer
634 * variant - wake up process and do not restart:
635 */
636 if (!fn) {
637 wake_up_process(data);
638 restart = HRTIMER_NORESTART;
639 } else
640 restart = fn(data);
641 633
642 spin_lock_irq(&base->lock); 634 spin_lock_irq(&base->lock);
643 635
644 /* Another CPU has added back the timer */ 636 /* Another CPU has added back the timer */
645 if (timer->state != HRTIMER_RUNNING) 637 if (timer->state != HRTIMER_INACTIVE)
646 continue; 638 continue;
647 639
648 if (restart == HRTIMER_RESTART) 640 if (restart != HRTIMER_NORESTART)
649 enqueue_hrtimer(timer, base); 641 enqueue_hrtimer(timer, base);
650 else
651 timer->state = HRTIMER_EXPIRED;
652 } 642 }
653 set_curr_timer(base, NULL); 643 set_curr_timer(base, NULL);
654 spin_unlock_irq(&base->lock); 644 spin_unlock_irq(&base->lock);
@@ -672,79 +662,70 @@ void hrtimer_run_queues(void)
672 * Sleep related functions: 662 * Sleep related functions:
673 */ 663 */
674 664
675/** 665struct sleep_hrtimer {
676 * schedule_hrtimer - sleep until timeout 666 struct hrtimer timer;
677 * 667 struct task_struct *task;
678 * @timer: hrtimer variable initialized with the correct clock base 668 int expired;
679 * @mode: timeout value is abs/rel 669};
680 *
681 * Make the current task sleep until @timeout is
682 * elapsed.
683 *
684 * You can set the task state as follows -
685 *
686 * %TASK_UNINTERRUPTIBLE - at least @timeout is guaranteed to
687 * pass before the routine returns. The routine will return 0
688 *
689 * %TASK_INTERRUPTIBLE - the routine may return early if a signal is
690 * delivered to the current task. In this case the remaining time
691 * will be returned
692 *
693 * The current task state is guaranteed to be TASK_RUNNING when this
694 * routine returns.
695 */
696static ktime_t __sched
697schedule_hrtimer(struct hrtimer *timer, const enum hrtimer_mode mode)
698{
699 /* fn stays NULL, meaning single-shot wakeup: */
700 timer->data = current;
701 670
702 hrtimer_start(timer, timer->expires, mode); 671static int nanosleep_wakeup(void *data)
672{
673 struct sleep_hrtimer *t = data;
703 674
704 schedule(); 675 t->expired = 1;
705 hrtimer_cancel(timer); 676 wake_up_process(t->task);
706 677
707 /* Return the remaining time: */ 678 return HRTIMER_NORESTART;
708 if (timer->state != HRTIMER_EXPIRED)
709 return ktime_sub(timer->expires, timer->base->get_time());
710 else
711 return (ktime_t) {.tv64 = 0 };
712} 679}
713 680
714static inline ktime_t __sched 681static int __sched do_nanosleep(struct sleep_hrtimer *t, enum hrtimer_mode mode)
715schedule_hrtimer_interruptible(struct hrtimer *timer,
716 const enum hrtimer_mode mode)
717{ 682{
718 set_current_state(TASK_INTERRUPTIBLE); 683 t->timer.function = nanosleep_wakeup;
684 t->timer.data = t;
685 t->task = current;
686 t->expired = 0;
687
688 do {
689 set_current_state(TASK_INTERRUPTIBLE);
690 hrtimer_start(&t->timer, t->timer.expires, mode);
691
692 schedule();
719 693
720 return schedule_hrtimer(timer, mode); 694 if (unlikely(!t->expired)) {
695 hrtimer_cancel(&t->timer);
696 mode = HRTIMER_ABS;
697 }
698 } while (!t->expired && !signal_pending(current));
699
700 return t->expired;
721} 701}
722 702
723static long __sched nanosleep_restart(struct restart_block *restart) 703static long __sched nanosleep_restart(struct restart_block *restart)
724{ 704{
705 struct sleep_hrtimer t;
725 struct timespec __user *rmtp; 706 struct timespec __user *rmtp;
726 struct timespec tu; 707 struct timespec tu;
727 void *rfn_save = restart->fn; 708 ktime_t time;
728 struct hrtimer timer;
729 ktime_t rem;
730 709
731 restart->fn = do_no_restart_syscall; 710 restart->fn = do_no_restart_syscall;
732 711
733 hrtimer_init(&timer, (clockid_t) restart->arg3, HRTIMER_ABS); 712 hrtimer_init(&t.timer, restart->arg3, HRTIMER_ABS);
734 713 t.timer.expires.tv64 = ((u64)restart->arg1 << 32) | (u64) restart->arg0;
735 timer.expires.tv64 = ((u64)restart->arg1 << 32) | (u64) restart->arg0;
736 714
737 rem = schedule_hrtimer_interruptible(&timer, HRTIMER_ABS); 715 if (do_nanosleep(&t, HRTIMER_ABS))
738
739 if (rem.tv64 <= 0)
740 return 0; 716 return 0;
741 717
742 rmtp = (struct timespec __user *) restart->arg2; 718 rmtp = (struct timespec __user *) restart->arg2;
743 tu = ktime_to_timespec(rem); 719 if (rmtp) {
744 if (rmtp && copy_to_user(rmtp, &tu, sizeof(tu))) 720 time = ktime_sub(t.timer.expires, t.timer.base->get_time());
745 return -EFAULT; 721 if (time.tv64 <= 0)
722 return 0;
723 tu = ktime_to_timespec(time);
724 if (copy_to_user(rmtp, &tu, sizeof(tu)))
725 return -EFAULT;
726 }
746 727
747 restart->fn = rfn_save; 728 restart->fn = nanosleep_restart;
748 729
749 /* The other values in restart are already filled in */ 730 /* The other values in restart are already filled in */
750 return -ERESTART_RESTARTBLOCK; 731 return -ERESTART_RESTARTBLOCK;
@@ -754,33 +735,34 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
754 const enum hrtimer_mode mode, const clockid_t clockid) 735 const enum hrtimer_mode mode, const clockid_t clockid)
755{ 736{
756 struct restart_block *restart; 737 struct restart_block *restart;
757 struct hrtimer timer; 738 struct sleep_hrtimer t;
758 struct timespec tu; 739 struct timespec tu;
759 ktime_t rem; 740 ktime_t rem;
760 741
761 hrtimer_init(&timer, clockid, mode); 742 hrtimer_init(&t.timer, clockid, mode);
762 743 t.timer.expires = timespec_to_ktime(*rqtp);
763 timer.expires = timespec_to_ktime(*rqtp); 744 if (do_nanosleep(&t, mode))
764
765 rem = schedule_hrtimer_interruptible(&timer, mode);
766 if (rem.tv64 <= 0)
767 return 0; 745 return 0;
768 746
769 /* Absolute timers do not update the rmtp value and restart: */ 747 /* Absolute timers do not update the rmtp value and restart: */
770 if (mode == HRTIMER_ABS) 748 if (mode == HRTIMER_ABS)
771 return -ERESTARTNOHAND; 749 return -ERESTARTNOHAND;
772 750
773 tu = ktime_to_timespec(rem); 751 if (rmtp) {
774 752 rem = ktime_sub(t.timer.expires, t.timer.base->get_time());
775 if (rmtp && copy_to_user(rmtp, &tu, sizeof(tu))) 753 if (rem.tv64 <= 0)
776 return -EFAULT; 754 return 0;
755 tu = ktime_to_timespec(rem);
756 if (copy_to_user(rmtp, &tu, sizeof(tu)))
757 return -EFAULT;
758 }
777 759
778 restart = &current_thread_info()->restart_block; 760 restart = &current_thread_info()->restart_block;
779 restart->fn = nanosleep_restart; 761 restart->fn = nanosleep_restart;
780 restart->arg0 = timer.expires.tv64 & 0xFFFFFFFF; 762 restart->arg0 = t.timer.expires.tv64 & 0xFFFFFFFF;
781 restart->arg1 = timer.expires.tv64 >> 32; 763 restart->arg1 = t.timer.expires.tv64 >> 32;
782 restart->arg2 = (unsigned long) rmtp; 764 restart->arg2 = (unsigned long) rmtp;
783 restart->arg3 = (unsigned long) timer.base->index; 765 restart->arg3 = (unsigned long) t.timer.base->index;
784 766
785 return -ERESTART_RESTARTBLOCK; 767 return -ERESTART_RESTARTBLOCK;
786} 768}
an> } #endif /* _NOTYET */ /* * NAME: extBalloc() * * FUNCTION: allocate disk blocks to form an extent. * * initially, we will try to allocate disk blocks for the * requested size (nblocks). if this fails (nblocks * contiguous free blocks not avaliable), we'll try to allocate * a smaller number of blocks (producing a smaller extent), with * this smaller number of blocks consisting of the requested * number of blocks rounded down to the next smaller power of 2 * number (i.e. 16 -> 8). we'll continue to round down and * retry the allocation until the number of blocks to allocate * is smaller than the number of blocks per page. * * PARAMETERS: * ip - the inode of the file. * hint - disk block number to be used as an allocation hint. * *nblocks - pointer to an s64 value. on entry, this value specifies * the desired number of block to be allocated. on successful * exit, this value is set to the number of blocks actually * allocated. * blkno - pointer to a block address that is filled in on successful * return with the starting block number of the newly * allocated block range. * * RETURN VALUES: * 0 - success * -EIO - i/o error. * -ENOSPC - insufficient disk resources. */ static int extBalloc(struct inode *ip, s64 hint, s64 * nblocks, s64 * blkno) { struct jfs_inode_info *ji = JFS_IP(ip); struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); s64 nb, nblks, daddr, max; int rc, nbperpage = sbi->nbperpage; struct bmap *bmp = sbi->bmap; int ag; /* get the number of blocks to initially attempt to allocate. * we'll first try the number of blocks requested unless this * number is greater than the maximum number of contiguous free * blocks in the map. in that case, we'll start off with the * maximum free. */ max = (s64) 1 << bmp->db_maxfreebud; if (*nblocks >= max && *nblocks > nbperpage) nb = nblks = (max > nbperpage) ? max : nbperpage; else nb = nblks = *nblocks; /* try to allocate blocks */ while ((rc = dbAlloc(ip, hint, nb, &daddr)) != 0) { /* if something other than an out of space error, * stop and return this error. */ if (rc != -ENOSPC) return (rc); /* decrease the allocation request size */ nb = min(nblks, extRoundDown(nb)); /* give up if we cannot cover a page */ if (nb < nbperpage) return (rc); } *nblocks = nb; *blkno = daddr; if (S_ISREG(ip->i_mode) && (ji->fileset == FILESYSTEM_I)) { ag = BLKTOAG(daddr, sbi); spin_lock_irq(&ji->ag_lock); if (ji->active_ag == -1) { atomic_inc(&bmp->db_active[ag]); ji->active_ag = ag; } else if (ji->active_ag != ag) { atomic_dec(&bmp->db_active[ji->active_ag]); atomic_inc(&bmp->db_active[ag]); ji->active_ag = ag; } spin_unlock_irq(&ji->ag_lock); } return (0); } #ifdef _NOTYET /* * NAME: extBrealloc() * * FUNCTION: attempt to extend an extent's allocation. * * Initially, we will try to extend the extent's allocation * in place. If this fails, we'll try to move the extent * to a new set of blocks. If moving the extent, we initially * will try to allocate disk blocks for the requested size * (newnblks). if this fails (new contiguous free blocks not * avaliable), we'll try to allocate a smaller number of * blocks (producing a smaller extent), with this smaller * number of blocks consisting of the requested number of * blocks rounded down to the next smaller power of 2 * number (i.e. 16 -> 8). We'll continue to round down and * retry the allocation until the number of blocks to allocate * is smaller than the number of blocks per page. * * PARAMETERS: * ip - the inode of the file. * blkno - starting block number of the extents current allocation. * nblks - number of blocks within the extents current allocation. * newnblks - pointer to a s64 value. on entry, this value is the * the new desired extent size (number of blocks). on * successful exit, this value is set to the extent's actual * new size (new number of blocks). * newblkno - the starting block number of the extents new allocation. * * RETURN VALUES: * 0 - success * -EIO - i/o error. * -ENOSPC - insufficient disk resources. */ static int extBrealloc(struct inode *ip, s64 blkno, s64 nblks, s64 * newnblks, s64 * newblkno) { int rc; /* try to extend in place */ if ((rc = dbExtend(ip, blkno, nblks, *newnblks - nblks)) == 0) { *newblkno = blkno; return (0); } else { if (rc != -ENOSPC) return (rc); } /* in place extension not possible. * try to move the extent to a new set of blocks. */ return (extBalloc(ip, blkno, newnblks, newblkno)); } #endif /* _NOTYET */ /* * NAME: extRoundDown() * * FUNCTION: round down a specified number of blocks to the next * smallest power of 2 number. * * PARAMETERS: * nb - the inode of the file. * * RETURN VALUES: * next smallest power of 2 number. */ static s64 extRoundDown(s64 nb) { int i; u64 m, k; for (i = 0, m = (u64) 1 << 63; i < 64; i++, m >>= 1) { if (m & nb) break; } i = 63 - i; k = (u64) 1 << i; k = ((k - 1) & nb) ? k : k >> 1; return (k); }