aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorMat Martineau <mathewm@codeaurora.org>2011-07-22 17:54:00 -0400
committerGustavo F. Padovan <gustavo@padovan.org>2011-09-27 17:16:18 -0400
commit84084a3197a9fdec10fa542c0df11928a784e7fc (patch)
tree4da560352bb5af8418d22fc65e516004d2e666b5 /net
parent5b668eb3270f3f9c13ddf6e4fb57bf20c83dccff (diff)
Bluetooth: Perform L2CAP SDU reassembly without copying data
Use sk_buff fragment capabilities to link together incoming skbs instead of allocating a new skb for reassembly and copying. The new reassembly code works equally well for ERTM and streaming mode, so there is now one reassembly function instead of two. Signed-off-by: Mat Martineau <mathewm@codeaurora.org> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/l2cap_core.c243
1 files changed, 80 insertions, 163 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 1611b3544bb1..12b1e742d706 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3128,102 +3128,104 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
3128 return 0; 3128 return 0;
3129} 3129}
3130 3130
3131static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) 3131static void append_skb_frag(struct sk_buff *skb,
3132 struct sk_buff *new_frag, struct sk_buff **last_frag)
3132{ 3133{
3133 struct sk_buff *_skb; 3134 /* skb->len reflects data in skb as well as all fragments
3134 int err; 3135 * skb->data_len reflects only data in fragments
3136 */
3137 if (!skb_has_frag_list(skb))
3138 skb_shinfo(skb)->frag_list = new_frag;
3139
3140 new_frag->next = NULL;
3141
3142 (*last_frag)->next = new_frag;
3143 *last_frag = new_frag;
3144
3145 skb->len += new_frag->len;
3146 skb->data_len += new_frag->len;
3147 skb->truesize += new_frag->truesize;
3148}
3149
3150static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
3151{
3152 int err = -EINVAL;
3135 3153
3136 switch (control & L2CAP_CTRL_SAR) { 3154 switch (control & L2CAP_CTRL_SAR) {
3137 case L2CAP_SDU_UNSEGMENTED: 3155 case L2CAP_SDU_UNSEGMENTED:
3138 if (test_bit(CONN_SAR_SDU, &chan->conn_state)) 3156 if (chan->sdu)
3139 goto drop; 3157 break;
3140 3158
3141 return chan->ops->recv(chan->data, skb); 3159 err = chan->ops->recv(chan->data, skb);
3160 break;
3142 3161
3143 case L2CAP_SDU_START: 3162 case L2CAP_SDU_START:
3144 if (test_bit(CONN_SAR_SDU, &chan->conn_state)) 3163 if (chan->sdu)
3145 goto drop; 3164 break;
3146 3165
3147 chan->sdu_len = get_unaligned_le16(skb->data); 3166 chan->sdu_len = get_unaligned_le16(skb->data);
3167 skb_pull(skb, 2);
3148 3168
3149 if (chan->sdu_len > chan->imtu) 3169 if (chan->sdu_len > chan->imtu) {
3150 goto disconnect; 3170 err = -EMSGSIZE;
3151 3171 break;
3152 chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC); 3172 }
3153 if (!chan->sdu)
3154 return -ENOMEM;
3155 3173
3156 /* pull sdu_len bytes only after alloc, because of Local Busy 3174 if (skb->len >= chan->sdu_len)
3157 * condition we have to be sure that this will be executed 3175 break;
3158 * only once, i.e., when alloc does not fail */
3159 skb_pull(skb, 2);
3160 3176
3161 memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); 3177 chan->sdu = skb;
3178 chan->sdu_last_frag = skb;
3162 3179
3163 set_bit(CONN_SAR_SDU, &chan->conn_state); 3180 skb = NULL;
3164 chan->partial_sdu_len = skb->len; 3181 err = 0;
3165 break; 3182 break;
3166 3183
3167 case L2CAP_SDU_CONTINUE: 3184 case L2CAP_SDU_CONTINUE:
3168 if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
3169 goto disconnect;
3170
3171 if (!chan->sdu) 3185 if (!chan->sdu)
3172 goto disconnect; 3186 break;
3173 3187
3174 chan->partial_sdu_len += skb->len; 3188 append_skb_frag(chan->sdu, skb,
3175 if (chan->partial_sdu_len > chan->sdu_len) 3189 &chan->sdu_last_frag);
3176 goto drop; 3190 skb = NULL;
3177 3191
3178 memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); 3192 if (chan->sdu->len >= chan->sdu_len)
3193 break;
3179 3194
3195 err = 0;
3180 break; 3196 break;
3181 3197
3182 case L2CAP_SDU_END: 3198 case L2CAP_SDU_END:
3183 if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
3184 goto disconnect;
3185
3186 if (!chan->sdu) 3199 if (!chan->sdu)
3187 goto disconnect; 3200 break;
3188
3189 chan->partial_sdu_len += skb->len;
3190
3191 if (chan->partial_sdu_len > chan->imtu)
3192 goto drop;
3193 3201
3194 if (chan->partial_sdu_len != chan->sdu_len) 3202 append_skb_frag(chan->sdu, skb,
3195 goto drop; 3203 &chan->sdu_last_frag);
3204 skb = NULL;
3196 3205
3197 memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); 3206 if (chan->sdu->len != chan->sdu_len)
3207 break;
3198 3208
3199 _skb = skb_clone(chan->sdu, GFP_ATOMIC); 3209 err = chan->ops->recv(chan->data, chan->sdu);
3200 if (!_skb) {
3201 return -ENOMEM;
3202 }
3203 3210
3204 err = chan->ops->recv(chan->data, _skb); 3211 if (!err) {
3205 if (err < 0) { 3212 /* Reassembly complete */
3206 kfree_skb(_skb); 3213 chan->sdu = NULL;
3207 return err; 3214 chan->sdu_last_frag = NULL;
3215 chan->sdu_len = 0;
3208 } 3216 }
3209
3210 clear_bit(CONN_SAR_SDU, &chan->conn_state);
3211
3212 kfree_skb(chan->sdu);
3213 break; 3217 break;
3214 } 3218 }
3215 3219
3216 kfree_skb(skb); 3220 if (err) {
3217 return 0; 3221 kfree_skb(skb);
3218 3222 kfree_skb(chan->sdu);
3219drop: 3223 chan->sdu = NULL;
3220 kfree_skb(chan->sdu); 3224 chan->sdu_last_frag = NULL;
3221 chan->sdu = NULL; 3225 chan->sdu_len = 0;
3226 }
3222 3227
3223disconnect: 3228 return err;
3224 l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
3225 kfree_skb(skb);
3226 return 0;
3227} 3229}
3228 3230
3229static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) 3231static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
@@ -3277,99 +3279,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
3277 } 3279 }
3278} 3280}
3279 3281
3280static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
3281{
3282 struct sk_buff *_skb;
3283 int err = -EINVAL;
3284
3285 /*
3286 * TODO: We have to notify the userland if some data is lost with the
3287 * Streaming Mode.
3288 */
3289
3290 switch (control & L2CAP_CTRL_SAR) {
3291 case L2CAP_SDU_UNSEGMENTED:
3292 if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
3293 kfree_skb(chan->sdu);
3294 break;
3295 }
3296
3297 err = chan->ops->recv(chan->data, skb);
3298 if (!err)
3299 return 0;
3300
3301 break;
3302
3303 case L2CAP_SDU_START:
3304 if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
3305 kfree_skb(chan->sdu);
3306 break;
3307 }
3308
3309 chan->sdu_len = get_unaligned_le16(skb->data);
3310 skb_pull(skb, 2);
3311
3312 if (chan->sdu_len > chan->imtu) {
3313 err = -EMSGSIZE;
3314 break;
3315 }
3316
3317 chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
3318 if (!chan->sdu) {
3319 err = -ENOMEM;
3320 break;
3321 }
3322
3323 memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
3324
3325 set_bit(CONN_SAR_SDU, &chan->conn_state);
3326 chan->partial_sdu_len = skb->len;
3327 err = 0;
3328 break;
3329
3330 case L2CAP_SDU_CONTINUE:
3331 if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
3332 break;
3333
3334 memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
3335
3336 chan->partial_sdu_len += skb->len;
3337 if (chan->partial_sdu_len > chan->sdu_len)
3338 kfree_skb(chan->sdu);
3339 else
3340 err = 0;
3341
3342 break;
3343
3344 case L2CAP_SDU_END:
3345 if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
3346 break;
3347
3348 memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
3349
3350 clear_bit(CONN_SAR_SDU, &chan->conn_state);
3351 chan->partial_sdu_len += skb->len;
3352
3353 if (chan->partial_sdu_len > chan->imtu)
3354 goto drop;
3355
3356 if (chan->partial_sdu_len == chan->sdu_len) {
3357 _skb = skb_clone(chan->sdu, GFP_ATOMIC);
3358 err = chan->ops->recv(chan->data, _skb);
3359 if (err < 0)
3360 kfree_skb(_skb);
3361 }
3362 err = 0;
3363
3364drop:
3365 kfree_skb(chan->sdu);
3366 break;
3367 }
3368
3369 kfree_skb(skb);
3370 return err;
3371}
3372
3373static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) 3282static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
3374{ 3283{
3375 struct sk_buff *skb; 3284 struct sk_buff *skb;
@@ -3384,7 +3293,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
3384 3293
3385 skb = skb_dequeue(&chan->srej_q); 3294 skb = skb_dequeue(&chan->srej_q);
3386 control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; 3295 control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
3387 err = l2cap_ertm_reassembly_sdu(chan, skb, control); 3296 err = l2cap_reassemble_sdu(chan, skb, control);
3388 3297
3389 if (err < 0) { 3298 if (err < 0) {
3390 l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); 3299 l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3544,7 +3453,7 @@ expected:
3544 return 0; 3453 return 0;
3545 } 3454 }
3546 3455
3547 err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control); 3456 err = l2cap_reassemble_sdu(chan, skb, rx_control);
3548 chan->buffer_seq = (chan->buffer_seq + 1) % 64; 3457 chan->buffer_seq = (chan->buffer_seq + 1) % 64;
3549 if (err < 0) { 3458 if (err < 0) {
3550 l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); 3459 l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3860,12 +3769,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
3860 3769
3861 tx_seq = __get_txseq(control); 3770 tx_seq = __get_txseq(control);
3862 3771
3863 if (chan->expected_tx_seq == tx_seq) 3772 if (chan->expected_tx_seq != tx_seq) {
3864 chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; 3773 /* Frame(s) missing - must discard partial SDU */
3865 else 3774 kfree_skb(chan->sdu);
3866 chan->expected_tx_seq = (tx_seq + 1) % 64; 3775 chan->sdu = NULL;
3776 chan->sdu_last_frag = NULL;
3777 chan->sdu_len = 0;
3867 3778
3868 l2cap_streaming_reassembly_sdu(chan, skb, control); 3779 /* TODO: Notify userland of missing data */
3780 }
3781
3782 chan->expected_tx_seq = (tx_seq + 1) % 64;
3783
3784 if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE)
3785 l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
3869 3786
3870 goto done; 3787 goto done;
3871 3788