- Linux Scheduler
- Preemption
- Spinlocks and Semaphores
- System Clock: Of Time and Timers
- Summary
- Exercises
7.4 System Clock: Of Time and Timers
For scheduling, the kernel uses the system clock to know how long a task has been running. We already covered the system clock in Chapter 5 by using it as an example for the discussion on interrupts. Here, we explore the Real-Time Clock and its uses and implementation; but first, let’s recap clocks in general.
The clock is a periodic signal applied to a processor, which allows it to function in the time domain. The processor depends on the clock signal to know when it can perform its next function, such as adding two integers or fetching data from memory. The speed of this clock signal (1.4GHz, 2GHz, and so on) has historically been used to compare the processing speed of systems at the local electronics store.
At any given moment, your system has several clocks and/or timers running. Simple examples include the time of day displayed in the bottom corner of your screen (otherwise known as wall time), the cursor patiently pulsing on a cluttered desktop, or your laptop screensaver taking over because of inactivity. More complicated examples of timekeeping include audio and video playback, key repeat (holding a key down), how fast communications ports run, and, as previously discussed, how long a task can run.
7.4.1 Real-Time Clock: What Time Is It?
The Linux interface to wall clock time is accomplished through the /dev/rtc device driver ioctl() function. The device for this driver is called a Real-Time Clock (RTC). The RTC9 provides timekeeping functions with a small 114-byte user NVRAM. The input to this device is a 32.768KHz oscillator and a connection for battery backup. Some discrete models of the RTC have the oscillator and battery built in, while other RTCs are now built in to the peripheral bus controller (for example, the Southbridge) of a processor chipset. The RTC not only reports the time of day, but it is also a programmable timer that is capable of interrupting the system. The frequency of interrupts varies from 2Hz to 8,192Hz. The RTC can also interrupt daily, like an alarm clock. Here, we explore the RTC code:
–---------------------------------------------------------------------- /include/linux/rtc.h /* * ioctl calls that are permitted to the /dev/rtc interface, if * any of the RTC drivers are enabled. */ 70 #define RTC_AIE_ON _IO(’p’, 0x01) /* Alarm int. enable on */ 71 #define RTC_AIE_OFF _IO(’p’, 0x02) /* ... off */ 72 #define RTC_UIE_ON _IO(’p’, 0x03) /* Update int. enable on */ 73 #define RTC_UIE_OFF _ IO(’p’, 0x04) /* ... off */ 74 #define RTC_PIE_ON _IO(’p’, 0x05) /* Periodic int. enable on */ 75 #define RTC_PIE_OFF _IO(’p’, 0x06) /* ... off */ 76 #define RTC_WIE_ON _IO(’p’, 0x0f) /* Watchdog int. enable on */ 77 #define RTC_WIE_OFF _IO(’p’, 0x10) /* ... off */ 78 #define RTC_ALM_SET _IOW(’p’, 0x07, struct rtc_time) /* Set alarm time */ 79 #define RTC_ALM_READ _IOR(’p’, 0x08, struct rtc_time) /* Read alarm time*/ 80 #define RTC_RD_TIME _IOR(’p’, 0x09, struct rtc_time) /* Read RTC time */ 81 #define RTC_SET_TIME _IOW(’p’, 0x0a, struct rtc_time) /* Set RTC time */ 82 #define RTC_IRQP_READ _IOR(’p’, 0x0b, unsigned long) /* Read IRQ rate*/ 83 #define RTC_IRQP_SET _IOW(’p’, 0x0c, unsigned long) /* Set IRQ rate */ 84 #define RTC_EPOCH_READ _IOR(’p’, 0x0d, unsigned long) /* Read epoch */ 85 #define RTC_EPOCH_SET _IOW(’p’, 0x0e, unsigned long) /* Set epoch */ 86 87 #define RTC_WKALM_SET _IOW(’p’, 0x0f, struct rtc_wkalrm)/*Set wakeupalarm*/ 88 #define RTC_WKALM_RD _IOR(’p’, 0x10, struct rtc_wkalrm)/*Get wakeupalarm*/ 89 90 #define RTC_PLL_GET _IOR(’p’, 0x11, struct rtc_pll_info) /* Get PLL correction */ 91 #define RTC_PLL_SET _IOW(’p’, 0x12, struct rtc_pll_info) /* Set PLL correction */ -----------------------------------------------------------------------
The ioctl() control functions are listed in include/linux/rtc.h. At this writing, not all the ioctl() calls for the RTC are implemented for the PPC architecture. These control functions each call lower-level hardware-specific functions (if implemented). The example in this section uses the RTC_RD_TIME function.
The following is a sample ioctl() call to get the time of day. This program simply opens the driver and queries the RTC hardware for the current date and time, and prints the information to stderr. Note that only one user can access the RTC driver at a time. The code to enforce this is shown in the driver discussion.
–---------------------------------------------------------------------- Documentation/rtc.txt /* * Trimmed down version of code in /Documentation/rtc.txt * */ int main(void) { int fd, retval = 0; //unsigned long tmp, data; struct rtc_time rtc_tm; fd = open ("/dev/rtc", O_RDONLY); /* Read the RTC time/date */ retval = ioctl(fd, RTC_RD_TIME, &rtc_tm); /* print out the time from the rtc_tm variable */ close(fd); return 0; } /* end main */ ------------------------------------------------------------------------
This code is a segment of a more complete example in /Documentation/ rtc.txt. The two main lines of code in this program are the open() command and the ioctl() call. open() tells us which driver we will use (/dev/rtc) and ioctl() indicates a specific path through the code down to the physical RTC interface by way of the RTC_RD_TIME command. The driver code for the open() command resides in the driver source, but its only significance to this discussion is which device driver was opened.
7.4.2 Reading the PPC Real-Time Clock
At kernel compile time, the appropriate code tree (x86, PPC, MIPS, and so on) is inserted. The source branch for PPC is discussed here in the source code file for the generic RTC driver for non-x86 systems:
–---------------------------------------------------------------------- /drivers/char/genrtc.c 276 static int gen_rtc_ioctl(struct inode *inode, struct file *file, 277 unsigned int cmd, unsigned long arg) 278 { 279 struct rtc_time wtime; 280 struct rtc_pll_info pll; 281 282 switch (cmd) { 283 284 case RTC_PLL_GET: ... 290 case RTC_PLL_SET: ... 298 case RTC_UIE_OFF: /* disable ints from RTC updates. */ ... 302 case RTC_UIE_ON: /* enable ints for RTC updates. */ ... 305 case RTC_RD_TIME: /* Read the time/date from RTC */ 306 307 memset(&wtime, 0, sizeof(wtime)); 308 get_rtc_time(&wtime); 309 310 return copy_to_user((void *)arg,&wtime,sizeof(wtime)) ? -EFAULT:0; 311 312 case RTC_SET_TIME: /* Set the RTC */ 313 return -EINVAL; 314 } ... 353 static int gen_rtc_open(struct inode *inode, struct file *file) 354 { 355 if (gen_rtc_status & RTC_IS_OPEN) 356 return -EBUSY; 357 gen_rtc_status |= RTC_IS_OPEN; ------------------------------------------------------------------------
This code is the case statement for the ioctl command set. Because we made the ioctl call from the user space test program with the RTC_RD_TIME flag, control is transferred to line 305. The next call is at line 308, get_rtc_time(&wtime) in rtc.h (see the following code). Before leaving this code segment, note line 353. This allows only one user to access, via open(), the driver at a time by setting the status to RTC_IS_OPEN:
–---------------------------------------------------------------------- include/asm-ppc/rtc.h 045 static inline unsigned int get_rtc_time(struct rtc_time *time) 046 { 047 if (ppc_md.get_rtc_time) { 048 unsigned long nowtime; 049 050 nowtime = (ppc_md.get_rtc_time)(); 051 052 to_tm(nowtime, time); 053 054 time->tm_year -= 1900; 055 time->tm_mon -= 1; /* Make sure userland has a 0-based month */ 056 } 057 return RTC_24H; 058 } ------------------------------------------------------------------------
The inline function get_rtc_time() calls the function that the structure variable pointed at by ppc_md.get_rtc_time on line 50. Early in the kernel initialization, this variable is set in chrp_setup.c:
–---------------------------------------------------------------------- arch/ppc/platforms/chrp_setup.c 447 chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, 448 unsigned long r6, unsigned long r7) 449 { ... 477 ppc_md.time_init = chrp_time_init; 478 ppc_md.set_rtc_time = chrp_set_rtc_time; 479 ppc_md.get_rtc_time = chrp_get_rtc_time; 480 ppc_md.calibrate_decr = chrp_calibrate_decr; ------------------------------------------------------------------------
The function chrp_get_rtc_time() (on line 479) is defined in chrp_time.c in the following code segment. Because the time information in CMOS memory is updated on a periodic basis, the block of read code is enclosed in a for loop, which rereads the block if the update is in progress:
–---------------------------------------------------------------------- arch/ppc/platforms/chrp_time.c 122 unsigned long __chrp chrp_get_rtc_time(void) 123 { 124 unsigned int year, mon, day, hour, min, sec; 125 int uip, i; ... 141 for ( i = 0; i<1000000; i++) { 142 uip = chrp_cmos_clock_read(RTC_FREQ_SELECT); 143 sec = chrp_cmos_clock_read(RTC_SECONDS); 144 min = chrp_cmos_clock_read(RTC_MINUTES); 145 hour = chrp_cmos_clock_read(RTC_HOURS); 146 day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); 147 mon = chrp_cmos_clock_read(RTC_MONTH); 148 year = chrp_cmos_clock_read(RTC_YEAR); 149 uip |= chrp_cmos_clock_read(RTC_FREQ_SELECT); 150 if ((uip & RTC_UIP)==0) break; 151 } 152 if (!(chrp_cmos_clock_read(RTC_CONTROL) 153 & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 154 { 155 BCD_TO_BIN(sec); 156 BCD_TO_BIN(min); 157 BCD_TO_BIN(hour); 158 BCD_TO_BIN(day); 159 BCD_TO_BIN(mon); 160 BCD_TO_BIN(year); 161 } ... 054 int __chrp chrp_cmos_clock_read(int addr) 055 { if (nvram_as1 != 0) 056 outb(addr>>8, nvram_as1); 057 outb(addr, nvram_as0); 058 return (inb(nvram_data)); 059 } ------------------------------------------------------------------------
Finally, in chrp_get_rtc_time(), the values of the individual components of the time structure are read from the RTC device by using the function chrp_cmos_clock_read. These values are formatted and returned in the rtc_tm structure that was passed into the ioctl call back in the userland test program.
7.4.3 Reading the x86 Real-Time Clock
The methodology for reading the RTC on the x86 system is similar to, but somewhat more compact and robust than, the PPC method. Once again, we follow the open driver /dev/rtc, but this time, the build has compiled the file rtc.c for the x86 architecture. The source branch for x86 is discussed here:
–---------------------------------------------------------------------- drivers/char/rtc.c ... 352 static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) 353 { ... switch (cmd) { ... 482 case RTC_RD_TIME: /* Read the time/date from RTC */ 483 { 484 rtc_get_rtc_time(&wtime); 485 break; 486 } ... 1208 void rtc_get_rtc_time(struct rtc_time *rtc_tm) 1209 { ... 1238 spin_lock_irq(&rtc_lock); 1239 rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); 1240 rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); 1241 rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); 1242 rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); 1243 rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); 1244 rtc_tm->tm_year = CMOS_READ(RTC_YEAR); 1245 ctrl = CMOS_READ(RTC_CONTROL); ... 1249 spin_unlock_irq(&rtc_lock); 1250 1251 if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 1252 { 1253 BCD_TO_BIN(rtc_tm->tm_sec); 1254 BCD_TO_BIN(rtc_tm->tm_min); 1255 BCD_TO_BIN(rtc_tm->tm_hour); 1256 BCD_TO_BIN(rtc_tm->tm_mday); 1257 BCD_TO_BIN(rtc_tm->tm_mon); 1258 BCD_TO_BIN(rtc_tm->tm_year); 1259 } ------------------------------------------------------------------------
The test program uses the ioctl() flag RTC_RD_TIME in its call to the driver rtc.c. The ioctl switch statement then fills the time structure from the CMOS memory of the RTC. Here is the x86 implementation of how the RTC hardware is read:
–---------------------------------------------------------------------- include/asm-i386/mc146818rtc.h ... 018 #define CMOS_READ(addr) ({ \ 019 outb_p((addr),RTC_PORT(0)); \ 020 inb_p(RTC_PORT(1)); \ 021 }) -----------------------------------------------------------------------