Commit c96356a9 authored by Karsten Keil's avatar Karsten Keil Committed by David S. Miller

mISDN: fix OOM condition for sending queued I-Frames

The old code did not check the return value of skb_clone().
The extra skb_clone() is not needed at all, if using skb_realloc_headroom()
instead, which gives us a private copy with enough headroom as well.
We need to requeue the original skb if the call failed, because we cannot
inform upper layers about the data loss. Restructure the code to minimise
rollback effort if it happens.
This fix kernel bug #86091

Thanks to Insu Yun <wuninsu@gmail.com> to remind me on this issue.
Signed-off-by: default avatarKarsten Keil <keil@b1-systems.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c7a7c95e
...@@ -1476,7 +1476,7 @@ static void ...@@ -1476,7 +1476,7 @@ static void
l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
{ {
struct layer2 *l2 = fi->userdata; struct layer2 *l2 = fi->userdata;
struct sk_buff *skb, *nskb, *oskb; struct sk_buff *skb, *nskb;
u_char header[MAX_L2HEADER_LEN]; u_char header[MAX_L2HEADER_LEN];
u_int i, p1; u_int i, p1;
...@@ -1486,11 +1486,26 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) ...@@ -1486,11 +1486,26 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
skb = skb_dequeue(&l2->i_queue); skb = skb_dequeue(&l2->i_queue);
if (!skb) if (!skb)
return; return;
i = sethdraddr(l2, header, CMD);
if (test_bit(FLG_MOD128, &l2->flag)) if (test_bit(FLG_MOD128, &l2->flag)) {
header[i++] = l2->vs << 1;
header[i++] = l2->vr << 1;
} else
header[i++] = (l2->vr << 5) | (l2->vs << 1);
nskb = skb_realloc_headroom(skb, i);
if (!nskb) {
printk(KERN_WARNING "%s: no headroom(%d) copy for IFrame\n",
mISDNDevName4ch(&l2->ch), i);
skb_queue_head(&l2->i_queue, skb);
return;
}
if (test_bit(FLG_MOD128, &l2->flag)) {
p1 = (l2->vs - l2->va) % 128; p1 = (l2->vs - l2->va) % 128;
else l2->vs = (l2->vs + 1) % 128;
} else {
p1 = (l2->vs - l2->va) % 8; p1 = (l2->vs - l2->va) % 8;
l2->vs = (l2->vs + 1) % 8;
}
p1 = (p1 + l2->sow) % l2->window; p1 = (p1 + l2->sow) % l2->window;
if (l2->windowar[p1]) { if (l2->windowar[p1]) {
printk(KERN_WARNING "%s: l2 try overwrite ack queue entry %d\n", printk(KERN_WARNING "%s: l2 try overwrite ack queue entry %d\n",
...@@ -1498,36 +1513,7 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) ...@@ -1498,36 +1513,7 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
dev_kfree_skb(l2->windowar[p1]); dev_kfree_skb(l2->windowar[p1]);
} }
l2->windowar[p1] = skb; l2->windowar[p1] = skb;
i = sethdraddr(l2, header, CMD);
if (test_bit(FLG_MOD128, &l2->flag)) {
header[i++] = l2->vs << 1;
header[i++] = l2->vr << 1;
l2->vs = (l2->vs + 1) % 128;
} else {
header[i++] = (l2->vr << 5) | (l2->vs << 1);
l2->vs = (l2->vs + 1) % 8;
}
nskb = skb_clone(skb, GFP_ATOMIC);
p1 = skb_headroom(nskb);
if (p1 >= i)
memcpy(skb_push(nskb, i), header, i); memcpy(skb_push(nskb, i), header, i);
else {
printk(KERN_WARNING
"%s: L2 pull_iqueue skb header(%d/%d) too short\n",
mISDNDevName4ch(&l2->ch), i, p1);
oskb = nskb;
nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC);
if (!nskb) {
dev_kfree_skb(oskb);
printk(KERN_WARNING "%s: no skb mem in %s\n",
mISDNDevName4ch(&l2->ch), __func__);
return;
}
memcpy(skb_put(nskb, i), header, i);
memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len);
dev_kfree_skb(oskb);
}
l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb); l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb);
test_and_clear_bit(FLG_ACK_PEND, &l2->flag); test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment