Thread (2 messages) flat view 2 messages, 2 authors, 2006-09-15

[Bluez-devel] L2CAP Flow Control

From: Martin Röhricht <hidden>
Date: 2006-09-15 20:30:38

Hi everybody,

after some months of coding, testing, deadlocks, debugging,
book-readings, etc. I present my first working version of L2CAP flow
control for the BlueZ stack. This is an early alpha release and may
contain errors -- but hey, it is the first really working version here
so be lenient toward me :-)

I will now go over some of the details of my patch:

We start with four timer functions of which two correspond to the
monitor timer and the retransmission timer respectively. Those four
functions should be pretty self explanatory. In short we've got two
starter functions and two signal handlers. Due to the lack of nice test
cases I couldn't test the functionality of both timers as it occurs.
The next function within the patch is the l2cap_init() function that got
extended quite a bit to set proper configuration values for flow control
mode, initialize the SKB TxQueue and both timers accordingly.
The next part is of much more interest. The function l2cap_do_send_rfc()
is used in combination with l2cap_segment_sdu() as a replacement for
l2cap_do_send() in case of flow control mode. Both new functions are
called from within l2cap_sock_sendmsg():
	if (l2cap_pi(sk)->mode == L2CAP_MODE_FLOW) {
		err = l2cap_segment_sdu(sk, msg, len);
		if (err < 0)
			goto out;
		if (l2cap_do_send_rfc(sk) < 0)
			err = -1;
	} else {
		err = l2cap_do_send(sk, msg, len);
	}

So at first l2cap_segment_sdu() is invoked. This is part of a sending
procedure so in case we get a SDU from user space that is too big to fit
into a single PDU of size MTU, we've got to split it into several
smaller PDUs. The incoming argument len reflects the size of the message
being sent, so at first we differentiate if this size fits into the
MTU's configured size to build one single frame. If the size is greater
we need to build segmented I-Frames. This procedure is closely related
to the one implemented within the original l2cap_do_send() which works
for B-Frames. But working in flow control mode with I- and S-Frames
instead of B-Frames requires to use individual control fields (with
individual send and receive sequence numbers etc.) and a unique frame
check sequence per frame. Further more we need to make sure to send only
as many frames as fit into the peer's receive buffer. Therefore we
cannot use the scatter/gather i/o mechanism as far as I know. So in case
the size is too long for one single frame, we prepare several I-Frames
step by step and put them all into the send queue:
	skb_queue_tail(&l2cap_pi(sk)->tx_queue, skb);
If this is all done error-free we call l2cap_do_send_rfc() which checks
the occupied spaces in the peer's receive buffer and sends frames from
it's send queue accordingly:
	while ((pi->tx_queue_frames > 0) && (pi->otxw > occupied)) {
		skb = skb_dequeue(&pi->tx_queue);
		if (skb == NULL) {
			goto fail;
		}
		if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0)
			goto fail;

		pi->tx_queue_frames--;
		occupied++;
	}

I will go over the following functions which are only used for the
configuration process as I already explained them in detail some time
ago.

The next generic function is l2cap_send_sframe() which is used to send
S-Frames as acknowledgements. Nothing spectacular happens within this
function -- it is like the transmission of I-Frames but much less
complicated as we do not have any user input data to handle. 
The next function, l2cap_invalid_frame_detection() is the first of a
series of functions which correspond to the reception part mainly. As
the name suggests we check an incoming frame (no matter if I-Frame or
S-Frame) for all the kinds of validity that are mentioned in the
specification. I tried to sort the cause of errors according to the
spec.
l2cap_process_txseq() and l2cap_process_reqseq() do what they are
supposed to do: they check the corresponding bits in the control field
of an incoming frame and adjust the socket's variables if appropriate.
What comes next is another crucial function, l2cap_reassembly(), say the
counter-part to l2cap_segment_sdu(). In this function -- which is called
after all the different stages of error checking from within
l2cap_data_channel on receiption of a frame -- we prepare the incoming
frames to be reassembled into one final SDU.
 Corresponding to the use of the SAR field we may have to take care of
the type of frame (i.e. START, END, CONTINUATION or UNSEGMENTED). I
tried to implemented this functionality as clean and short as possible.
Some of this code may be errorneous due to the lack of testing -- so
some of this work should work at least theoretically. In case of an
unsegmented SDU the steps are pretty much straight forward -- basically
the information payload is checked out (sock_queue_rcv_skb()) and an
S-Frame is sent as a response. Somewhat more complicated is the case in
which we receive a bigger segmented SDU. In this case a new temporary
socket buffer in the l2cap_pi(sk) struct is created and used to
reassemble the SDU. Keep in mind and be always aware of the fact that
flow control mode in the Bluetooth L2CAP layer does not contain any
retransmission mechanisms. So in case of a lost frame, the final
information payload that is passed to the user space is just not the one
expected, but L2CAP doesn't care about it. Nevertheless I had to
implement some error checking facilities that make sure that we are not
crashing just by the absence of any specific frame.
Finally the l2cap_data_channel() has been extended. This is the starting
point of this whole work if you check the comment of prior versions
which has been erased ;-) So if we are in flow control mode, all the
aforementioned functions are called in logical order.


That's basically it. Be aware of enough errors in this code to fill
evenings with debugging sessions. This code works here by sending files
from one patched linux computer to another patched one. Using the debug
output you can verify that flow control is in action even though I
couldn't create a test case in which you could really flood one peer
with frames. This code has to be tested against all different kinds of
devices and stacks as I had to make some modifications to the
configuration process and some stacks have a very "sensitive" behaviour
regarding this.
Feel free to test it and harden it with me. :-)

Bye,
Martin

Attachments

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help