Another device-driver? Getting ready to program the network interface

advertisement
Another device-driver?
Getting ready to program the
network interface
Inexpensive NIC: $8.95
RealTek 8139 processor
ConnectGear D30-TX (Made in China)
Hardware components
packet
main
memory
TX FIFO
buffer
B
U
S
CPU
nic
RX FIFO
transceiver
LAN
cable
PC-to-PC connection
PC
PC
NIC
NIC
RJ-45
connector
RJ-45
connector
UTP Category-5
“crossover” cable
Kudlick Classroom’s stations
8
9
10
15
4
5
6
7
1
2
3
16
17
18
19
11
12
13
14
LECTERN
20
28
29
30
24
25
26
27
21
22
23
In-class exercise #1
• A network device-driver will need to know
certain identifying information about the
workstation it is running on -- so it can let
other stations know to whom they should
send their reply-messages
• Our ‘utsinfo.c’ module shows how kernel
code can find out the name of the ‘node’
on which it is executing
• Try it out – then study its code to use later
Some NIC characteristics
• With our previous device-driver examples
(e.g., dram, vram, and hd), the data to be
read was already there waiting to be read
• But with a network interface card (NIC),
we may want to read its data before any
data has arrived from other workstations
• In such cases we would like to wait until
data arrives rather than abandon reading
Could do ‘busy waiting’?
• It is possible for a network driver to ‘poll’ a
status-bit continuously until data is ready
(as we did with the IDE Controller’s status)
• This is called ‘busy waiting’ – it could take
up a lot of valuable time before any benefit
is realized
• In a multitasking system we want to avoid
‘busy waiting’ whenever possible!
Alternative is ‘blocking’
• If trying to read from device-files when no
data is present, but new data is expected
to arrive, the system can ‘block’ the task
from consuming valuable CPU time while
it waits, by ‘putting the task to sleep’ and
arranging for it to be ‘awakened’ as soon
as some new data has actually arrived
What does ‘sleep’ mean?
• The Linux kernel puts a task to sleep by simply
modifying the value of its ‘state’ variable:
–
–
–
–
TASK_RUNNING
TASK_STOPPED
TASK_UNINTERRUPTIBLE
TASK_INTERRUPTIBLE
• Only tasks with ‘state == TASK_RUNNING’ are
scheduled to be granted time on the CPU
What does ‘wakeup’ mean?
• A sleeping task is one whose ‘task.state’ is
equal to ‘TASK_INTERRUPTIBLE’ or to
‘TASK_UNINTERRUPTIBLE’
• A sleeping task is ‘woken up’ by changing
its ‘task,state’ to be ‘TASK_RUNNING’
• When the Linux scheduler sees that a task
is in the ‘TASK_RUNNING’ state, it grants
that task some CPU time for execution
‘run’ queues and ‘wait’ queues
• In order for Linux to efficiently manage the
scheduling of the various tasks, separate
queues are maintained for ‘running’ tasks
and for tasks that are asleep while waiting
for a particular event to occur (such as the
arrival of new data from the network)
Some tasks are ‘ready-to-run’
Kernel support-routines
• The Linux kernel makes it easy for drivers
to perform the ‘sleep’ and ‘wakeup’ actions
while avoiding potential ‘race conditions’
which are inherent in a ‘preemptive’ kernel
that might be running on multiple CPUs
Use of Linux wait-queues
•
•
•
•
•
#include <linux/sched.h>
wait_queue_head_t
my_queue;
init_waitqueue_head( &my_queue );
sleep_on( &my_queue );
wake_up( &my_queue );
• But can’t unload driver if task stays asleep!
Kernel waitqueues
waitqueue
waitqueue
waitqueue
waitqueue
‘interruptible’ is preferred
#include <linux/sched.h>
wait_queue_head_t
wq;
init_waitqueue_head( &wq );
wait_event_interruptible( wq, <condition> );
wake_up_interruptible( &wq );
An ‘interruptible’ sleep can awoken by a signal --- in case you might want to ‘unload’ your driver!
A convenient ‘macro’
• DECLARE_WAIT_QUEUE_HEAD( wq );
• This statement can be placed outside your
module’s functions (i.e., a ‘global’ object)
• It combines declaration with initialization:
wait_queue_head_t wq;
init_waitqueue_head( &wq );
A character device: ‘stash’
•
•
•
•
Device works like a public ‘clipboard’
It uses kernel memory to store its data
It allows ‘communication’ between tasks
What one task writes, another can read!
Ringbuffer
•
•
•
•
•
A first-in first-out data-structure (FIFO)
Uses a storage array of finite length
Uses two array-indices: ‘head’ and ‘tail’
Data is added at the current ‘tail’ position
Data is removed from the ‘head’ position
Ringbuffer (continued)
•
•
•
•
•
One array-position is always left unused
Condition ‘head == tail’ means “empty”
Condition tail == head-1 means “full”
Both ‘head’ and ‘tail’ will “wraparound”
Calculation: next = ( next+1 )%RINGSIZE;
read-algorithm for ‘stash’
• if ( ringbuffer_is_empty )
{
// sleep, until another task supplies some data
// or else exit if a signal is received by this task
}
•
•
•
•
Remove a byte from the ringbuffer;
Copy the byte to user-space;
Awaken any sleeping writers;
return 1;
write-algorithm for ‘stash’
• if ( ringbuffer_is_full )
{
// sleep, until some data is removed by another task
// or else exit if a signal is received by this task
}
•
•
•
•
Copy a byte from user-space;
Insert this byte into ringbuffer;
Awaken any sleeping readers;
return 1;
Demonstration of ‘stash’
• Quick demo: we can use I/O redirection
• For demonstrating ‘write’ to /dev/stash:
$ echo “Hello” > /dev/stash
• For demonstrating ‘read’ from /dev/stash:
$ cat /dev/stash
The ‘device’ file-node
• We cannot use the ‘stash.c’ device-driver
until a device-node has been created that
allows both ‘read’ and ‘write’ access (the
SysAdmin must do this setup for us):
#root mknod /dev/stash c 40 0
#root chmod a+rw /dev/stash
• You can try using the ‘sudo’ command to
these steps (if that privilege was granted)
Alternative: use ‘/dev/foo’
• If you cannot create the ‘/dev/stash’ node,
you can use an existing ‘generic’ node if
you change the driver’s ‘major’ number
and the device-name you register it with
• Use the command: $ ls –l /dev/foo
to
find out what major number you must use
In-class exercise #2
• Add a ‘get_info()’ function to this driver to
create a pseudo-file (named ‘/proc/stash’)
that will show the current contents of the
ringbuffer (if any) and the current values
for the ‘head’ and ‘tail’ buffer-indices
• Don’t forget: use ‘create_proc_info_entry()’
in your ‘init_module()’ function, and call
‘remove_proc_entry()’ during ‘cleanup’
Download