• Keine Ergebnisse gefunden

Kernel support for buffering data

Im Dokument sea UNIX Writing Drivers (Seite 121-128)

Description of the device

4.9 Buffering data

4.9.4 Kernel support for buffering data

The kernel provides two major buffering schemes for device drivers to use, depending on whether they are character device drivers or block device drivers. Both schemes have been designed to address the issues that we have. discussed in this chapter.

Buffering data 105

Character device drivers can use buffers called character lists, or clists. Chapter 5 gives a full account of how clists work. All that we need to understand for the time being is how to use two new kernel support routines putc (K) and getc (K) which put data onto and take data off a clist:

#include (sys/tty.h) putc(c, cp)

int Ci

struct clist *CPi

And:

#include (sys/tty.h) getc(cp)

struct clist *CPi

Character device drivers are free to use an alternative buffering scheme, if they so wish.

Block device drivers implicitly use 1 K buffers from the kernel's buffer cache for all their 110. On output, data moves from the user process into the buffer cache, and from there out to the device. The reverse happens on input. A full account of block device drivers is given in Chapter 7.

We can now complete our modifications to the parallel printer driver from Chapter 3. The device driver uses a clist to decouple the user process from the interrupt routine. Note that XXintr is called indirectly at task-time via XXstart. To aid readability, we have added a new routine called utok14 to take care of transferring data between user space and the kernel. The printer runs at software priority level 2:

/* * src/lpintr.c

* Copyright (c) 1992 Peter Kettle and Steve Statler

* Interrupt driven parallel printer driver for the onboard

* printer port of a PC AT compatible.

This is based on the simple printer driver discussed in

* Chapter 3.

* The enhancements include the use of interrupts, clist

* buffering, and moving the code which copies from user to

* kernel into a separate module.

* ./configure -a lpinit lpwrite lpintr -v VECTOR -1 INTPRI

* -T 1 -c -m MAJOR

*/

#include (sys/types.h)

* Initialize printer controller and announce its presence.

*/

UInterrupt driven printer driver");

* lpwrite()

Buffering data 107

lpwrite(dev) dev_t dev;

int c, s;

while ((c = utok()) >= 0) {

s = sp12(); /* protect lpq from while (lpq.c_cc >= HIWAT) {

lpstart();

lpflags 1= LPSLEEP;

sleep (&lpq, LPPRI);

putc(c, &lpq);

splx(s) ; s sp12 () ; lpstart() ; splx(s) ;

lpintr() */

/* * lpstart()

If the driver is already busy (possibly just waiting for

* an interrupt), return.

* Otherwise call lpintr() at task-time to start up output.

*/

lpstart()

{

if (lpflags & LPACTlVE) return;

lpflags 1= LPACTIVE;

lpintr(O);

/* * lpintr ()

* Called at task-time from lpwrite() to start up output,

* and at interrupt-time when the printer's on-board buffer

* empties.

Output characters whilst the printer is idle and the

* clist isn't empty.

*/

lpintr(irq) int irq;

int c;

if ((lpflags & LPACTlVE) 0) { return;

/*

while ((inb(PSTATUS) & READY) && ((c = getc(&lpq)) >=

0)) {

outb(PDATA, c);

outb(PCNTRL, PRIME I STROBE);

outb(PCNTRL, PRIME);

if ((lpq.c_cc < LOWAT) && (lpflags & LPSLEEP)) { lpflags &= -LPSLEEP;

wakeup (&lpq) ; if (lpq.c_cc == 0) {

lpflags &= -LPACTIVE;

outb(PCNTRL, PRIME I INTENBL);

* utok()

* Use copyin(K) to transfer a byte of data from user space

* into the kernel, then adjust the U-area I/O fields.

* Returns -1 on fail, or no more data.

*/

utok()

{

char C;

int s;

if (u.u_count == 0) { return (-1) ;

s = copyin(u.u_base, &c, 1);

if (s == -1) {

seterror(EFAULT);

return(-1);

u.u_count--;

u.u_base++;

u.u_offset++; /* for completeness */

return ( (int)c);

4.10 Summary

In this chapter, we have described how interrupts work and why it is beneficial to use them. We have examined a process' context in detail, and we have explained what a context switch is and why it is

Exercise 109

important to enable context switches to happen in a user, multi-tasking operating system such as UNIX. We have presented a list of rules for use when writing interrupt routines, and we have looked at the benefits of buffering data.

Finally, we have seen that maximum bandwidth to a device can be attained by using interrupts, and by decoupling the task-time and interrupt-time parts of a driver with a buffer.

QUIZ

To test your understanding of this chapter, try to answer the following questions.

4.1 How should a task-time process force a context switch when there is no work for it to do?

4.2 Is a device driver permitted to call wakeup (K) at interrupt-time?

4.3 How many clock cycles (approximately) does a context switch take on an Intel CPU?

4.4 How should a task-time process protect data that is shared with the interrupt routine?

4.5 How should a device driver arrange to receive notification of interrupted sleep (K) s?

4.6 How does the kernel determine whether an interrupt occurred in user mode or system mode?

EXERCISE

Modify the mouse device driver from Chapter 3 so that it is interrupt driven.

Here are some hints:

• Set jumper 2 on the mouse to use a spare IRQ line. Use the hwconfig(ADM) command to find out which IRQ lines are free .

• Use interrupt priority 6.

• Remember to deconfigure your XXpoll routine!

• Remember to specify the -T 1 option to configure (ADM) so that the system knows to expect interrupts.

• A quick, simple solution would be to call XXpoll from XXintr.

Advanced session:

• Modify xXopen to examine the minor device number. If the minor device number is 0, use timeouts. If the minor device number is 1, use interrupts. Remember to create a new entry in /dev with the appropriate minor device number.

Disable interrupts or timeouts when the device is closed.

A suggested answer is given in I Answers to Exercises'.

NOTES

1. We use the notation exemplified by CS :ElP to describe segment selector registers (CS is the code segment selector) and offsets into segments (ElP is the 32-bit instruction pointer, used as an offset into the code segment).

2. PDBR is an alternative name for Control Register 3 (CR3).

3. Except for the Double fault exception, which is handled in its own context.

4. The process which is waiting for the interrupt will probably be asleep.

5. This is exactly the reverse of the software priority levels that UNIX uses!

6. An exception to this priority rule is dumb serial cards, which operate at priority level 7. This is to avoid overruns and data loss.

7. We refer to the spl routines generally by using the notation spl (K).

8. If the CPU has to switch from privilege level 3 to privilege 0 to handle the interrupt, the system stack will be empty at this point.

9. The i386 automatically pushes an error code for some exceptions. The interrupt handler pushes a dummy error code so that all exception and interrupt stack frames are the same size.

10. Some implementations of UNIX provide real-time extensions, but do not necessarily provide the guaranteed response time of dedicated real-time operating systems.

11. More than one process can sleep on the same wait channel.

12. The clock interrupt routine sets runrun directly, to force context switches at the end of time-slices.

13. If the dispatcher found the same process on the run queue that had just been taken off the CPU, the actual context switch is bypassed.

14. utok is equivalent to SCQ's cpass (K).

11111 I1IIII 11 II 11.IU//fIJIII!!11iJIl

Line disciplines and serial

Im Dokument sea UNIX Writing Drivers (Seite 121-128)