diff options
author | Mat Martineau <mathewm@codeaurora.org> | 2012-04-11 13:48:42 -0400 |
---|---|---|
committer | Gustavo Padovan <gustavo@padovan.org> | 2012-05-09 00:40:30 -0400 |
commit | 3c588192b5e5328cdfc8e299c55477004d397208 (patch) | |
tree | 1bedbf322a6b1ded901dc00724e5f8c290098997 /net/bluetooth | |
parent | 9033894722ec595053c92bfa4359b37e7bc91b78 (diff) |
Bluetooth: Add the l2cap_seq_list structure for tracking frames
A sequence list is a data structure used to track frames that need to
be retransmitted, and frames that have been requested for
retransmission by the remote device. It can compactly represent a
list of sequence numbers within the ERTM transmit window. Memory for
the list is allocated once at connection time, and common operations
in ERTM are O(1).
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/l2cap_core.c | 150 |
1 files changed, 142 insertions, 8 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 03746f565fc4..041ebed9e647 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -232,6 +232,121 @@ static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err) | |||
232 | release_sock(sk); | 232 | release_sock(sk); |
233 | } | 233 | } |
234 | 234 | ||
235 | /* ---- L2CAP sequence number lists ---- */ | ||
236 | |||
237 | /* For ERTM, ordered lists of sequence numbers must be tracked for | ||
238 | * SREJ requests that are received and for frames that are to be | ||
239 | * retransmitted. These seq_list functions implement a singly-linked | ||
240 | * list in an array, where membership in the list can also be checked | ||
241 | * in constant time. Items can also be added to the tail of the list | ||
242 | * and removed from the head in constant time, without further memory | ||
243 | * allocs or frees. | ||
244 | */ | ||
245 | |||
246 | static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size) | ||
247 | { | ||
248 | size_t alloc_size, i; | ||
249 | |||
250 | /* Allocated size is a power of 2 to map sequence numbers | ||
251 | * (which may be up to 14 bits) in to a smaller array that is | ||
252 | * sized for the negotiated ERTM transmit windows. | ||
253 | */ | ||
254 | alloc_size = roundup_pow_of_two(size); | ||
255 | |||
256 | seq_list->list = kmalloc(sizeof(u16) * alloc_size, GFP_KERNEL); | ||
257 | if (!seq_list->list) | ||
258 | return -ENOMEM; | ||
259 | |||
260 | seq_list->mask = alloc_size - 1; | ||
261 | seq_list->head = L2CAP_SEQ_LIST_CLEAR; | ||
262 | seq_list->tail = L2CAP_SEQ_LIST_CLEAR; | ||
263 | for (i = 0; i < alloc_size; i++) | ||
264 | seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR; | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static inline void l2cap_seq_list_free(struct l2cap_seq_list *seq_list) | ||
270 | { | ||
271 | kfree(seq_list->list); | ||
272 | } | ||
273 | |||
274 | static inline bool l2cap_seq_list_contains(struct l2cap_seq_list *seq_list, | ||
275 | u16 seq) | ||
276 | { | ||
277 | /* Constant-time check for list membership */ | ||
278 | return seq_list->list[seq & seq_list->mask] != L2CAP_SEQ_LIST_CLEAR; | ||
279 | } | ||
280 | |||
281 | static u16 l2cap_seq_list_remove(struct l2cap_seq_list *seq_list, u16 seq) | ||
282 | { | ||
283 | u16 mask = seq_list->mask; | ||
284 | |||
285 | if (seq_list->head == L2CAP_SEQ_LIST_CLEAR) { | ||
286 | /* In case someone tries to pop the head of an empty list */ | ||
287 | return L2CAP_SEQ_LIST_CLEAR; | ||
288 | } else if (seq_list->head == seq) { | ||
289 | /* Head can be removed in constant time */ | ||
290 | seq_list->head = seq_list->list[seq & mask]; | ||
291 | seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR; | ||
292 | |||
293 | if (seq_list->head == L2CAP_SEQ_LIST_TAIL) { | ||
294 | seq_list->head = L2CAP_SEQ_LIST_CLEAR; | ||
295 | seq_list->tail = L2CAP_SEQ_LIST_CLEAR; | ||
296 | } | ||
297 | } else { | ||
298 | /* Walk the list to find the sequence number */ | ||
299 | u16 prev = seq_list->head; | ||
300 | while (seq_list->list[prev & mask] != seq) { | ||
301 | prev = seq_list->list[prev & mask]; | ||
302 | if (prev == L2CAP_SEQ_LIST_TAIL) | ||
303 | return L2CAP_SEQ_LIST_CLEAR; | ||
304 | } | ||
305 | |||
306 | /* Unlink the number from the list and clear it */ | ||
307 | seq_list->list[prev & mask] = seq_list->list[seq & mask]; | ||
308 | seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR; | ||
309 | if (seq_list->tail == seq) | ||
310 | seq_list->tail = prev; | ||
311 | } | ||
312 | return seq; | ||
313 | } | ||
314 | |||
315 | static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list) | ||
316 | { | ||
317 | /* Remove the head in constant time */ | ||
318 | return l2cap_seq_list_remove(seq_list, seq_list->head); | ||
319 | } | ||
320 | |||
321 | static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list) | ||
322 | { | ||
323 | if (seq_list->head != L2CAP_SEQ_LIST_CLEAR) { | ||
324 | u16 i; | ||
325 | for (i = 0; i <= seq_list->mask; i++) | ||
326 | seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR; | ||
327 | |||
328 | seq_list->head = L2CAP_SEQ_LIST_CLEAR; | ||
329 | seq_list->tail = L2CAP_SEQ_LIST_CLEAR; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq) | ||
334 | { | ||
335 | u16 mask = seq_list->mask; | ||
336 | |||
337 | /* All appends happen in constant time */ | ||
338 | |||
339 | if (seq_list->list[seq & mask] == L2CAP_SEQ_LIST_CLEAR) { | ||
340 | if (seq_list->tail == L2CAP_SEQ_LIST_CLEAR) | ||
341 | seq_list->head = seq; | ||
342 | else | ||
343 | seq_list->list[seq_list->tail & mask] = seq; | ||
344 | |||
345 | seq_list->tail = seq; | ||
346 | seq_list->list[seq & mask] = L2CAP_SEQ_LIST_TAIL; | ||
347 | } | ||
348 | } | ||
349 | |||
235 | static void l2cap_chan_timeout(struct work_struct *work) | 350 | static void l2cap_chan_timeout(struct work_struct *work) |
236 | { | 351 | { |
237 | struct l2cap_chan *chan = container_of(work, struct l2cap_chan, | 352 | struct l2cap_chan *chan = container_of(work, struct l2cap_chan, |
@@ -414,6 +529,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) | |||
414 | 529 | ||
415 | skb_queue_purge(&chan->srej_q); | 530 | skb_queue_purge(&chan->srej_q); |
416 | 531 | ||
532 | l2cap_seq_list_free(&chan->srej_list); | ||
533 | l2cap_seq_list_free(&chan->retrans_list); | ||
417 | list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { | 534 | list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { |
418 | list_del(&l->list); | 535 | list_del(&l->list); |
419 | kfree(l); | 536 | kfree(l); |
@@ -2045,8 +2162,10 @@ static void l2cap_ack_timeout(struct work_struct *work) | |||
2045 | l2cap_chan_put(chan); | 2162 | l2cap_chan_put(chan); |
2046 | } | 2163 | } |
2047 | 2164 | ||
2048 | static inline void l2cap_ertm_init(struct l2cap_chan *chan) | 2165 | static inline int l2cap_ertm_init(struct l2cap_chan *chan) |
2049 | { | 2166 | { |
2167 | int err; | ||
2168 | |||
2050 | chan->expected_ack_seq = 0; | 2169 | chan->expected_ack_seq = 0; |
2051 | chan->unacked_frames = 0; | 2170 | chan->unacked_frames = 0; |
2052 | chan->buffer_seq = 0; | 2171 | chan->buffer_seq = 0; |
@@ -2060,6 +2179,11 @@ static inline void l2cap_ertm_init(struct l2cap_chan *chan) | |||
2060 | skb_queue_head_init(&chan->srej_q); | 2179 | skb_queue_head_init(&chan->srej_q); |
2061 | 2180 | ||
2062 | INIT_LIST_HEAD(&chan->srej_l); | 2181 | INIT_LIST_HEAD(&chan->srej_l); |
2182 | err = l2cap_seq_list_init(&chan->srej_list, chan->tx_win); | ||
2183 | if (err < 0) | ||
2184 | return err; | ||
2185 | |||
2186 | return l2cap_seq_list_init(&chan->retrans_list, chan->remote_tx_win); | ||
2063 | } | 2187 | } |
2064 | 2188 | ||
2065 | static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) | 2189 | static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) |
@@ -2853,7 +2977,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
2853 | u16 dcid, flags; | 2977 | u16 dcid, flags; |
2854 | u8 rsp[64]; | 2978 | u8 rsp[64]; |
2855 | struct l2cap_chan *chan; | 2979 | struct l2cap_chan *chan; |
2856 | int len; | 2980 | int len, err = 0; |
2857 | 2981 | ||
2858 | dcid = __le16_to_cpu(req->dcid); | 2982 | dcid = __le16_to_cpu(req->dcid); |
2859 | flags = __le16_to_cpu(req->flags); | 2983 | flags = __le16_to_cpu(req->flags); |
@@ -2924,9 +3048,13 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
2924 | chan->expected_tx_seq = 0; | 3048 | chan->expected_tx_seq = 0; |
2925 | skb_queue_head_init(&chan->tx_q); | 3049 | skb_queue_head_init(&chan->tx_q); |
2926 | if (chan->mode == L2CAP_MODE_ERTM) | 3050 | if (chan->mode == L2CAP_MODE_ERTM) |
2927 | l2cap_ertm_init(chan); | 3051 | err = l2cap_ertm_init(chan); |
3052 | |||
3053 | if (err < 0) | ||
3054 | l2cap_send_disconn_req(chan->conn, chan, -err); | ||
3055 | else | ||
3056 | l2cap_chan_ready(chan); | ||
2928 | 3057 | ||
2929 | l2cap_chan_ready(chan); | ||
2930 | goto unlock; | 3058 | goto unlock; |
2931 | } | 3059 | } |
2932 | 3060 | ||
@@ -2954,7 +3082,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
2954 | 3082 | ||
2955 | unlock: | 3083 | unlock: |
2956 | l2cap_chan_unlock(chan); | 3084 | l2cap_chan_unlock(chan); |
2957 | return 0; | 3085 | return err; |
2958 | } | 3086 | } |
2959 | 3087 | ||
2960 | static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) | 3088 | static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) |
@@ -2963,6 +3091,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
2963 | u16 scid, flags, result; | 3091 | u16 scid, flags, result; |
2964 | struct l2cap_chan *chan; | 3092 | struct l2cap_chan *chan; |
2965 | int len = le16_to_cpu(cmd->len) - sizeof(*rsp); | 3093 | int len = le16_to_cpu(cmd->len) - sizeof(*rsp); |
3094 | int err = 0; | ||
2966 | 3095 | ||
2967 | scid = __le16_to_cpu(rsp->scid); | 3096 | scid = __le16_to_cpu(rsp->scid); |
2968 | flags = __le16_to_cpu(rsp->flags); | 3097 | flags = __le16_to_cpu(rsp->flags); |
@@ -3054,14 +3183,17 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
3054 | chan->expected_tx_seq = 0; | 3183 | chan->expected_tx_seq = 0; |
3055 | skb_queue_head_init(&chan->tx_q); | 3184 | skb_queue_head_init(&chan->tx_q); |
3056 | if (chan->mode == L2CAP_MODE_ERTM) | 3185 | if (chan->mode == L2CAP_MODE_ERTM) |
3057 | l2cap_ertm_init(chan); | 3186 | err = l2cap_ertm_init(chan); |
3058 | 3187 | ||
3059 | l2cap_chan_ready(chan); | 3188 | if (err < 0) |
3189 | l2cap_send_disconn_req(chan->conn, chan, -err); | ||
3190 | else | ||
3191 | l2cap_chan_ready(chan); | ||
3060 | } | 3192 | } |
3061 | 3193 | ||
3062 | done: | 3194 | done: |
3063 | l2cap_chan_unlock(chan); | 3195 | l2cap_chan_unlock(chan); |
3064 | return 0; | 3196 | return err; |
3065 | } | 3197 | } |
3066 | 3198 | ||
3067 | static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) | 3199 | static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) |
@@ -3805,6 +3937,7 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) | |||
3805 | BT_DBG("chan %p, Enter local busy", chan); | 3937 | BT_DBG("chan %p, Enter local busy", chan); |
3806 | 3938 | ||
3807 | set_bit(CONN_LOCAL_BUSY, &chan->conn_state); | 3939 | set_bit(CONN_LOCAL_BUSY, &chan->conn_state); |
3940 | l2cap_seq_list_clear(&chan->srej_list); | ||
3808 | 3941 | ||
3809 | __set_ack_timer(chan); | 3942 | __set_ack_timer(chan); |
3810 | } | 3943 | } |
@@ -3897,6 +4030,7 @@ static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) | |||
3897 | while (tx_seq != chan->expected_tx_seq) { | 4030 | while (tx_seq != chan->expected_tx_seq) { |
3898 | control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); | 4031 | control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); |
3899 | control |= __set_reqseq(chan, chan->expected_tx_seq); | 4032 | control |= __set_reqseq(chan, chan->expected_tx_seq); |
4033 | l2cap_seq_list_append(&chan->srej_list, chan->expected_tx_seq); | ||
3900 | l2cap_send_sframe(chan, control); | 4034 | l2cap_send_sframe(chan, control); |
3901 | 4035 | ||
3902 | new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); | 4036 | new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); |