mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-09 14:50:19 +00:00
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: Karsten Keil <keil@b1-systems.de> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
c7a7c95e8e
commit
c96356a9ba
@ -1476,7 +1476,7 @@ static void
|
||||
l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct layer2 *l2 = fi->userdata;
|
||||
struct sk_buff *skb, *nskb, *oskb;
|
||||
struct sk_buff *skb, *nskb;
|
||||
u_char header[MAX_L2HEADER_LEN];
|
||||
u_int i, p1;
|
||||
|
||||
@ -1486,11 +1486,26 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
|
||||
skb = skb_dequeue(&l2->i_queue);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
if (test_bit(FLG_MOD128, &l2->flag))
|
||||
i = sethdraddr(l2, header, CMD);
|
||||
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;
|
||||
else
|
||||
l2->vs = (l2->vs + 1) % 128;
|
||||
} else {
|
||||
p1 = (l2->vs - l2->va) % 8;
|
||||
l2->vs = (l2->vs + 1) % 8;
|
||||
}
|
||||
p1 = (p1 + l2->sow) % l2->window;
|
||||
if (l2->windowar[p1]) {
|
||||
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)
|
||||
dev_kfree_skb(l2->windowar[p1]);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
memcpy(skb_push(nskb, i), header, i);
|
||||
l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb);
|
||||
test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
|
||||
if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user