Title: sr_rwio()

sr_rwio() is either called by sr_rec() upon receipt of a read, write, or ioctl request or by process_req_q() to handle queued messages. The handling of a read, write, or ioctl request is a complex operation that is best explained by an example. We will analyze two different read requests, one with data ready to be delivered to user processes and one with no data ready to be delivered.

SCENARIO 1: There is data ready to be delivered to the user process and there are no messages in the read queue (i.e., srf_read_q, srf_read_q_tail == null). The file descriptor was opened by the udp client.

When a message arrives requesting a read operation (i.e., m->mq_mess.m_type == DEV_READ), udp_read() is called, which in turn calls udp_packet2user(). udp_packet2user() calls sr_put_userdata() twice. The first time sr_put_userdata() is called, the data is transferred to the user process. The second time sr_put_userdata() is called, a reply is sent to the user process by sr_reply(), which ultimately calls mq_free(), freeing the message.

SCENARIO 2: There are no read messages waiting in the read queue of a file descriptor opened by the udp client. However, this time, there is no data waiting to be read.

A DEV_READ message (message 1) arrives and sr_rec() calls sr_rwio() to handle the message. Since there are no messages in the read queue, SFF_READ_IP is not raised. After setting the SFF_READ_IP flag, sr_rwio() calls udp_read(). However, since no data is waiting to be read, udp_read() returns NW_SUSPEND. sr_rwio() then sets the SFF_READ_SUSP flag and returns NW_SUSPEND to sr_rec(), which then sends a REVIVE message back to the file system (in other words, it instructs the FS to revive the process requesting the read). After this, the sr file descriptor is shown:

A second DEV_READ message (message 2) arrives and sr_rec() again calls sr_rwio(). Because the SFF_READ_IP flag is already raised by the first message, the second message is appended to the read queue and sr_rwio() returns NW_SUSPEND. Again, sr_reply() sends a REVIVE message to the file system.

A third DEV_READ message (message 3) arrives and, as before, is placed at the end of the read queue and a REVIVE message is sent to the file system.

Finally, some data arrives (possibly from the ethernet task). udp_packet2user() is ultimately called and it, in turn, calls sr_put_userdata() twice: the first time to deliver the packet to the buffer of the user process and the second time to return the number of bytes delivered to the user process. The second time sr_put_data() is called, it calls sr_reply(), which sets the read queue head to null.

After sr_reply() returns, sr_put_userdata() then calls process_req_q(), passing in mq as the first parameter. process_req_q() goes through the remaining messages, starting with mq (see figure above), handling the read requests (by calling sr_rwio()) if possible. If there is enough data to satisfy all of the read requests (i.e., the requests of messages 2 and 3), the read queue is as appears in the figure on the left. If, for example, there is not even enough data to satisfy the request of message 2, the message queue is as appears in the figure on the right.

If there was enough data to satisfy the requests of messages 2 and 3, the remaining messages are freed by sr_reply(), just as message 1 was previously freed. Note that the sr_read_q field in the right-hand figure above was set by sr_rwio() before returning.