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

mISDN: Reduce RX buffer allocation for transparent data

We did allways allocate maxsize buffers, but for transparent data we know
the actual size.
Use a common function to calculate size and detect overflows.
Signed-off-by: default avatarKarsten Keil <kkeil@linux-pingi.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 37952cfa
...@@ -404,21 +404,14 @@ hdlc_empty_fifo(struct bchannel *bch, int count) ...@@ -404,21 +404,14 @@ hdlc_empty_fifo(struct bchannel *bch, int count)
u32 *ptr; u32 *ptr;
u8 *p; u8 *p;
u32 val, addr; u32 val, addr;
int cnt = 0; int cnt;
struct fritzcard *fc = bch->hw; struct fritzcard *fc = bch->hw;
pr_debug("%s: %s %d\n", fc->name, __func__, count); pr_debug("%s: %s %d\n", fc->name, __func__, count);
if (!bch->rx_skb) { cnt = bchannel_get_rxbuf(bch, count);
bch->rx_skb = mI_alloc_skb(bch->maxlen, GFP_ATOMIC); if (cnt < 0) {
if (!bch->rx_skb) { pr_warning("%s.B%d: No bufferspace for %d bytes\n",
pr_info("%s: B receive out of memory\n", fc->name, bch->nr, count);
fc->name);
return;
}
}
if ((bch->rx_skb->len + count) > bch->maxlen) {
pr_debug("%s: overrun %d\n", fc->name,
bch->rx_skb->len + count);
return; return;
} }
p = skb_put(bch->rx_skb, count); p = skb_put(bch->rx_skb, count);
...@@ -430,6 +423,7 @@ hdlc_empty_fifo(struct bchannel *bch, int count) ...@@ -430,6 +423,7 @@ hdlc_empty_fifo(struct bchannel *bch, int count)
addr = fc->addr + CHIP_WINDOW; addr = fc->addr + CHIP_WINDOW;
outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr);
} }
cnt = 0;
while (cnt < count) { while (cnt < count) {
val = le32_to_cpu(inl(addr)); val = le32_to_cpu(inl(addr));
put_unaligned(val, ptr); put_unaligned(val, ptr);
......
...@@ -2196,24 +2196,20 @@ hfcmulti_rx(struct hfc_multi *hc, int ch) ...@@ -2196,24 +2196,20 @@ hfcmulti_rx(struct hfc_multi *hc, int ch)
int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ int f1 = 0, f2 = 0; /* = 0, to make GCC happy */
int again = 0; int again = 0;
struct bchannel *bch; struct bchannel *bch;
struct dchannel *dch; struct dchannel *dch = NULL;
struct sk_buff *skb, **sp = NULL; struct sk_buff *skb, **sp = NULL;
int maxlen; int maxlen;
bch = hc->chan[ch].bch; bch = hc->chan[ch].bch;
dch = hc->chan[ch].dch; if (bch) {
if ((!dch) && (!bch)) if (!test_bit(FLG_ACTIVE, &bch->Flags))
return; return;
if (dch) { } else if (hc->chan[ch].dch) {
dch = hc->chan[ch].dch;
if (!test_bit(FLG_ACTIVE, &dch->Flags)) if (!test_bit(FLG_ACTIVE, &dch->Flags))
return; return;
sp = &dch->rx_skb;
maxlen = dch->maxlen;
} else { } else {
if (!test_bit(FLG_ACTIVE, &bch->Flags))
return; return;
sp = &bch->rx_skb;
maxlen = bch->maxlen;
} }
next_frame: next_frame:
/* on first AND before getting next valid frame, R_FIFO must be written /* on first AND before getting next valid frame, R_FIFO must be written
...@@ -2260,14 +2256,27 @@ hfcmulti_rx(struct hfc_multi *hc, int ch) ...@@ -2260,14 +2256,27 @@ hfcmulti_rx(struct hfc_multi *hc, int ch)
if (Zsize <= 0) if (Zsize <= 0)
return; return;
if (bch) {
maxlen = bchannel_get_rxbuf(bch, Zsize);
if (maxlen < 0) {
pr_warning("card%d.B%d: No bufferspace for %d bytes\n",
hc->id + 1, bch->nr, Zsize);
return;
}
sp = &bch->rx_skb;
maxlen = bch->maxlen;
} else { /* Dchannel */
sp = &dch->rx_skb;
maxlen = dch->maxlen + 3;
if (*sp == NULL) { if (*sp == NULL) {
*sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC); *sp = mI_alloc_skb(maxlen, GFP_ATOMIC);
if (*sp == NULL) { if (*sp == NULL) {
printk(KERN_DEBUG "%s: No mem for rx_skb\n", pr_warning("card%d: No mem for dch rx_skb\n",
__func__); hc->id + 1);
return; return;
} }
} }
}
/* show activity */ /* show activity */
if (dch) if (dch)
hc->activity_rx |= 1 << hc->chan[ch].port; hc->activity_rx |= 1 << hc->chan[ch].port;
...@@ -2281,7 +2290,7 @@ hfcmulti_rx(struct hfc_multi *hc, int ch) ...@@ -2281,7 +2290,7 @@ hfcmulti_rx(struct hfc_multi *hc, int ch)
Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE",
f1, f2, Zsize + (*sp)->len, again); f1, f2, Zsize + (*sp)->len, again);
/* HDLC */ /* HDLC */
if ((Zsize + (*sp)->len) > (maxlen + 3)) { if ((Zsize + (*sp)->len) > maxlen) {
if (debug & DEBUG_HFCMULTI_FIFO) if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG printk(KERN_DEBUG
"%s(card %d): hdlc-frame too large.\n", "%s(card %d): hdlc-frame too large.\n",
...@@ -2351,24 +2360,7 @@ hfcmulti_rx(struct hfc_multi *hc, int ch) ...@@ -2351,24 +2360,7 @@ hfcmulti_rx(struct hfc_multi *hc, int ch)
/* there is an incomplete frame */ /* there is an incomplete frame */
} else { } else {
/* transparent */ /* transparent */
if (Zsize > skb_tailroom(*sp))
Zsize = skb_tailroom(*sp);
hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
if (((*sp)->len) < MISDN_COPY_SIZE) {
skb = *sp;
*sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
if (*sp) {
memcpy(skb_put(*sp, skb->len),
skb->data, skb->len);
skb_trim(skb, 0);
} else {
printk(KERN_DEBUG "%s: No mem\n", __func__);
*sp = skb;
skb = NULL;
}
} else {
skb = NULL;
}
if (debug & DEBUG_HFCMULTI_FIFO) if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG printk(KERN_DEBUG
"%s(card %d): fifo(%d) reading %d bytes " "%s(card %d): fifo(%d) reading %d bytes "
...@@ -2376,7 +2368,6 @@ hfcmulti_rx(struct hfc_multi *hc, int ch) ...@@ -2376,7 +2368,6 @@ hfcmulti_rx(struct hfc_multi *hc, int ch)
__func__, hc->id + 1, ch, Zsize, z1, z2); __func__, hc->id + 1, ch, Zsize, z1, z2);
/* only bch is transparent */ /* only bch is transparent */
recv_Bchannel(bch, hc->chan[ch].Zfill); recv_Bchannel(bch, hc->chan[ch].Zfill);
*sp = skb;
} }
} }
......
...@@ -577,8 +577,11 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz, ...@@ -577,8 +577,11 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz,
fcnt_tx = B_FIFO_SIZE - fcnt_tx; fcnt_tx = B_FIFO_SIZE - fcnt_tx;
/* remaining bytes to send (bytes in tx-fifo) */ /* remaining bytes to send (bytes in tx-fifo) */
bch->rx_skb = mI_alloc_skb(fcnt_rx, GFP_ATOMIC); maxlen = bchannel_get_rxbuf(bch, fcnt_rx);
if (bch->rx_skb) { if (maxlen < 0) {
pr_warning("B%d: No bufferspace for %d bytes\n",
bch->nr, fcnt_rx);
} else {
ptr = skb_put(bch->rx_skb, fcnt_rx); ptr = skb_put(bch->rx_skb, fcnt_rx);
if (le16_to_cpu(*z2r) + fcnt_rx <= B_FIFO_SIZE + B_SUB_VAL) if (le16_to_cpu(*z2r) + fcnt_rx <= B_FIFO_SIZE + B_SUB_VAL)
maxlen = fcnt_rx; /* complete transfer */ maxlen = fcnt_rx; /* complete transfer */
...@@ -597,9 +600,7 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz, ...@@ -597,9 +600,7 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz,
memcpy(ptr, ptr1, fcnt_rx); /* rest */ memcpy(ptr, ptr1, fcnt_rx); /* rest */
} }
recv_Bchannel(bch, fcnt_tx); /* bch, id */ recv_Bchannel(bch, fcnt_tx); /* bch, id */
} else }
printk(KERN_WARNING "HFCPCI: receive out of memory\n");
*z2r = cpu_to_le16(new_z2); /* new position */ *z2r = cpu_to_le16(new_z2); /* new position */
} }
......
...@@ -860,7 +860,16 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, ...@@ -860,7 +860,16 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
hdlc = 1; hdlc = 1;
} }
if (fifo->bch) { if (fifo->bch) {
maxlen = bchannel_get_rxbuf(fifo->bch, len);
rx_skb = fifo->bch->rx_skb; rx_skb = fifo->bch->rx_skb;
if (maxlen < 0) {
if (rx_skb)
skb_trim(rx_skb, 0);
pr_warning("%s.B%d: No bufferspace for %d bytes\n",
hw->name, fifo->bch->nr, len);
spin_unlock(&hw->lock);
return;
}
maxlen = fifo->bch->maxlen; maxlen = fifo->bch->maxlen;
hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags);
} }
...@@ -870,13 +879,12 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, ...@@ -870,13 +879,12 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
hdlc = 1; hdlc = 1;
} }
if (fifo->dch || fifo->ech) {
if (!rx_skb) { if (!rx_skb) {
rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC);
if (rx_skb) { if (rx_skb) {
if (fifo->dch) if (fifo->dch)
fifo->dch->rx_skb = rx_skb; fifo->dch->rx_skb = rx_skb;
if (fifo->bch)
fifo->bch->rx_skb = rx_skb;
if (fifo->ech) if (fifo->ech)
fifo->ech->rx_skb = rx_skb; fifo->ech->rx_skb = rx_skb;
skb_trim(rx_skb, 0); skb_trim(rx_skb, 0);
...@@ -887,8 +895,6 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, ...@@ -887,8 +895,6 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
return; return;
} }
} }
if (fifo->dch || fifo->ech) {
/* D/E-Channel SKB range check */ /* D/E-Channel SKB range check */
if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) {
printk(KERN_DEBUG "%s: %s: sbk mem exceeded " printk(KERN_DEBUG "%s: %s: sbk mem exceeded "
...@@ -898,16 +904,6 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, ...@@ -898,16 +904,6 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
spin_unlock(&hw->lock); spin_unlock(&hw->lock);
return; return;
} }
} else if (fifo->bch) {
/* B-Channel SKB range check */
if ((rx_skb->len + len) >= (MAX_BCH_SIZE + 3)) {
printk(KERN_DEBUG "%s: %s: sbk mem exceeded "
"for fifo(%d) HFCUSB_B_RX\n",
hw->name, __func__, fifon);
skb_trim(rx_skb, 0);
spin_unlock(&hw->lock);
return;
}
} }
memcpy(skb_put(rx_skb, len), data, len); memcpy(skb_put(rx_skb, len), data, len);
......
...@@ -933,22 +933,16 @@ static void ...@@ -933,22 +933,16 @@ static void
hscx_empty_fifo(struct hscx_hw *hscx, u8 count) hscx_empty_fifo(struct hscx_hw *hscx, u8 count)
{ {
u8 *p; u8 *p;
int maxlen;
pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count); pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count);
if (!hscx->bch.rx_skb) { maxlen = bchannel_get_rxbuf(&hscx->bch, count);
hscx->bch.rx_skb = mI_alloc_skb(hscx->bch.maxlen, GFP_ATOMIC); if (maxlen < 0) {
if (!hscx->bch.rx_skb) {
pr_info("%s: B receive out of memory\n",
hscx->ip->name);
hscx_cmdr(hscx, 0x80); /* RMC */ hscx_cmdr(hscx, 0x80); /* RMC */
return; if (hscx->bch.rx_skb)
}
}
if ((hscx->bch.rx_skb->len + count) > hscx->bch.maxlen) {
pr_debug("%s: overrun %d\n", hscx->ip->name,
hscx->bch.rx_skb->len + count);
skb_trim(hscx->bch.rx_skb, 0); skb_trim(hscx->bch.rx_skb, 0);
hscx_cmdr(hscx, 0x80); /* RMC */ pr_warning("%s.B%d: No bufferspace for %d bytes\n",
hscx->ip->name, hscx->bch.nr, count);
return; return;
} }
p = skb_put(hscx->bch.rx_skb, count); p = skb_put(hscx->bch.rx_skb, count);
......
...@@ -422,6 +422,7 @@ static inline void ...@@ -422,6 +422,7 @@ static inline void
isar_rcv_frame(struct isar_ch *ch) isar_rcv_frame(struct isar_ch *ch)
{ {
u8 *ptr; u8 *ptr;
int maxlen;
if (!ch->is->clsb) { if (!ch->is->clsb) {
pr_debug("%s; ISAR zero len frame\n", ch->is->name); pr_debug("%s; ISAR zero len frame\n", ch->is->name);
...@@ -437,36 +438,22 @@ isar_rcv_frame(struct isar_ch *ch) ...@@ -437,36 +438,22 @@ isar_rcv_frame(struct isar_ch *ch)
case ISDN_P_B_RAW: case ISDN_P_B_RAW:
case ISDN_P_B_L2DTMF: case ISDN_P_B_L2DTMF:
case ISDN_P_B_MODEM_ASYNC: case ISDN_P_B_MODEM_ASYNC:
if (!ch->bch.rx_skb) { maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb);
ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen, if (maxlen < 0) {
GFP_ATOMIC); pr_warning("%s.B%d: No bufferspace for %d bytes\n",
if (unlikely(!ch->bch.rx_skb)) { ch->is->name, ch->bch.nr, ch->is->clsb);
pr_info("%s: B receive out of memory\n",
ch->is->name);
ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
break; break;
} }
}
rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb)); rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb));
recv_Bchannel(&ch->bch, 0); recv_Bchannel(&ch->bch, 0);
break; break;
case ISDN_P_B_HDLC: case ISDN_P_B_HDLC:
if (!ch->bch.rx_skb) { maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb);
ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen, if (maxlen < 0) {
GFP_ATOMIC); pr_warning("%s.B%d: No bufferspace for %d bytes\n",
if (unlikely(!ch->bch.rx_skb)) { ch->is->name, ch->bch.nr, ch->is->clsb);
pr_info("%s: B receive out of memory\n",
ch->is->name);
ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
break;
}
}
if ((ch->bch.rx_skb->len + ch->is->clsb) >
(ch->bch.maxlen + 2)) {
pr_debug("%s: incoming packet too large\n",
ch->is->name);
ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
skb_trim(ch->bch.rx_skb, 0);
break; break;
} }
if (ch->is->cmsb & HDLC_ERROR) { if (ch->is->cmsb & HDLC_ERROR) {
......
...@@ -386,24 +386,16 @@ read_dma(struct tiger_ch *bc, u32 idx, int cnt) ...@@ -386,24 +386,16 @@ read_dma(struct tiger_ch *bc, u32 idx, int cnt)
bc->bch.nr, idx); bc->bch.nr, idx);
} }
bc->lastrx = idx; bc->lastrx = idx;
if (!bc->bch.rx_skb) { stat = bchannel_get_rxbuf(&bc->bch, cnt);
bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen, GFP_ATOMIC); /* only transparent use the count here, HDLC overun is detected later */
if (!bc->bch.rx_skb) { if (stat == ENOMEM) {
pr_info("%s: B%1d receive out of memory\n", pr_warning("%s.B%d: No memory for %d bytes\n",
card->name, bc->bch.nr); card->name, bc->bch.nr, cnt);
return;
}
}
if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) {
if ((bc->bch.rx_skb->len + cnt) > bc->bch.maxlen) {
pr_debug("%s: B%1d overrun %d\n", card->name,
bc->bch.nr, bc->bch.rx_skb->len + cnt);
skb_trim(bc->bch.rx_skb, 0);
return; return;
} }
if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags))
p = skb_put(bc->bch.rx_skb, cnt); p = skb_put(bc->bch.rx_skb, cnt);
} else else
p = bc->hrbuf; p = bc->hrbuf;
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
...@@ -414,48 +406,45 @@ read_dma(struct tiger_ch *bc, u32 idx, int cnt) ...@@ -414,48 +406,45 @@ read_dma(struct tiger_ch *bc, u32 idx, int cnt)
idx = 0; idx = 0;
p[i] = val & 0xff; p[i] = val & 0xff;
} }
if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) {
recv_Bchannel(&bc->bch, 0);
return;
}
pn = bc->hrbuf; pn = bc->hrbuf;
next_frame: while (cnt > 0) {
if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i, stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i,
bc->bch.rx_skb->data, bc->bch.maxlen); bc->bch.rx_skb->data, bc->bch.maxlen);
if (stat > 0) /* valid frame received */ if (stat > 0) { /* valid frame received */
p = skb_put(bc->bch.rx_skb, stat); p = skb_put(bc->bch.rx_skb, stat);
else if (stat == -HDLC_CRC_ERROR) if (debug & DEBUG_HW_BFIFO) {
snprintf(card->log, LOG_SIZE,
"B%1d-recv %s %d ", bc->bch.nr,
card->name, stat);
print_hex_dump_bytes(card->log,
DUMP_PREFIX_OFFSET, p,
stat);
}
recv_Bchannel(&bc->bch, 0);
stat = bchannel_get_rxbuf(&bc->bch, bc->bch.maxlen);
if (stat < 0) {
pr_warning("%s.B%d: No memory for %d bytes\n",
card->name, bc->bch.nr, cnt);
return;
}
} else if (stat == -HDLC_CRC_ERROR) {
pr_info("%s: B%1d receive frame CRC error\n", pr_info("%s: B%1d receive frame CRC error\n",
card->name, bc->bch.nr); card->name, bc->bch.nr);
else if (stat == -HDLC_FRAMING_ERROR) } else if (stat == -HDLC_FRAMING_ERROR) {
pr_info("%s: B%1d receive framing error\n", pr_info("%s: B%1d receive framing error\n",
card->name, bc->bch.nr); card->name, bc->bch.nr);
else if (stat == -HDLC_LENGTH_ERROR) } else if (stat == -HDLC_LENGTH_ERROR) {
pr_info("%s: B%1d receive frame too long (> %d)\n", pr_info("%s: B%1d receive frame too long (> %d)\n",
card->name, bc->bch.nr, bc->bch.maxlen); card->name, bc->bch.nr, bc->bch.maxlen);
} else
stat = cnt;
if (stat > 0) {
if (debug & DEBUG_HW_BFIFO) {
snprintf(card->log, LOG_SIZE, "B%1d-recv %s %d ",
bc->bch.nr, card->name, stat);
print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET,
p, stat);
} }
recv_Bchannel(&bc->bch, 0);
}
if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
pn += i; pn += i;
cnt -= i; cnt -= i;
if (!bc->bch.rx_skb) {
bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen,
GFP_ATOMIC);
if (!bc->bch.rx_skb) {
pr_info("%s: B%1d receive out of memory\n",
card->name, bc->bch.nr);
return;
}
}
if (cnt > 0)
goto next_frame;
} }
} }
......
...@@ -465,6 +465,7 @@ W6692_empty_Bfifo(struct w6692_ch *wch, int count) ...@@ -465,6 +465,7 @@ W6692_empty_Bfifo(struct w6692_ch *wch, int count)
{ {
struct w6692_hw *card = wch->bch.hw; struct w6692_hw *card = wch->bch.hw;
u8 *ptr; u8 *ptr;
int maxlen;
pr_debug("%s: empty_Bfifo %d\n", card->name, count); pr_debug("%s: empty_Bfifo %d\n", card->name, count);
if (unlikely(wch->bch.state == ISDN_P_NONE)) { if (unlikely(wch->bch.state == ISDN_P_NONE)) {
...@@ -474,20 +475,13 @@ W6692_empty_Bfifo(struct w6692_ch *wch, int count) ...@@ -474,20 +475,13 @@ W6692_empty_Bfifo(struct w6692_ch *wch, int count)
skb_trim(wch->bch.rx_skb, 0); skb_trim(wch->bch.rx_skb, 0);
return; return;
} }
if (!wch->bch.rx_skb) { maxlen = bchannel_get_rxbuf(&wch->bch, count);
wch->bch.rx_skb = mI_alloc_skb(wch->bch.maxlen, GFP_ATOMIC); if (maxlen < 0) {
if (unlikely(!wch->bch.rx_skb)) {
pr_info("%s: B receive out of memory\n", card->name);
WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
W_B_CMDR_RACT);
return;
}
}
if (wch->bch.rx_skb->len + count > wch->bch.maxlen) {
pr_debug("%s: empty_Bfifo incoming packet too large\n",
card->name);
WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
if (wch->bch.rx_skb)
skb_trim(wch->bch.rx_skb, 0); skb_trim(wch->bch.rx_skb, 0);
pr_warning("%s.B%d: No bufferspace for %d bytes\n",
card->name, wch->bch.nr, count);
return; return;
} }
ptr = skb_put(wch->bch.rx_skb, count); ptr = skb_put(wch->bch.rx_skb, count);
......
...@@ -201,20 +201,30 @@ recv_Bchannel(struct bchannel *bch, unsigned int id) ...@@ -201,20 +201,30 @@ recv_Bchannel(struct bchannel *bch, unsigned int id)
{ {
struct mISDNhead *hh; struct mISDNhead *hh;
/* if allocation did fail upper functions still may call us */
if (unlikely(!bch->rx_skb))
return;
if (unlikely(!bch->rx_skb->len)) {
/* we have no data to send - this may happen after recovery
* from overflow or too small allocation.
* We need to free the buffer here */
dev_kfree_skb(bch->rx_skb);
bch->rx_skb = NULL;
} else {
hh = mISDN_HEAD_P(bch->rx_skb); hh = mISDN_HEAD_P(bch->rx_skb);
hh->prim = PH_DATA_IND; hh->prim = PH_DATA_IND;
hh->id = id; hh->id = id;
if (bch->rcount >= 64) { if (bch->rcount >= 64) {
printk(KERN_WARNING "B-channel %p receive queue overflow, " printk(KERN_WARNING
"flushing!\n", bch); "B%d receive queue overflow - flushing!\n",
bch->nr);
skb_queue_purge(&bch->rqueue); skb_queue_purge(&bch->rqueue);
bch->rcount = 0;
return;
} }
bch->rcount++; bch->rcount++;
skb_queue_tail(&bch->rqueue, bch->rx_skb); skb_queue_tail(&bch->rqueue, bch->rx_skb);
bch->rx_skb = NULL; bch->rx_skb = NULL;
schedule_event(bch, FLG_RECVQUEUE); schedule_event(bch, FLG_RECVQUEUE);
}
} }
EXPORT_SYMBOL(recv_Bchannel); EXPORT_SYMBOL(recv_Bchannel);
...@@ -399,3 +409,44 @@ bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) ...@@ -399,3 +409,44 @@ bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
} }
} }
EXPORT_SYMBOL(bchannel_senddata); EXPORT_SYMBOL(bchannel_senddata);
/* The function allocates a new receive skb on demand with a size for the
* requirements of the current protocol. It returns the tailroom of the
* receive skb or an error.
*/
int
bchannel_get_rxbuf(struct bchannel *bch, int reqlen)
{
int len;
if (bch->rx_skb) {
len = skb_tailroom(bch->rx_skb);
if (len < reqlen) {
pr_warning("B%d no space for %d (only %d) bytes\n",
bch->nr, reqlen, len);
if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
/* send what we have now and try a new buffer */
recv_Bchannel(bch, 0);
} else {
/* on HDLC we have to drop too big frames */
return -EMSGSIZE;
}
} else {
return len;
}
}
if (unlikely(reqlen > bch->maxlen))
return -EMSGSIZE;
if (test_bit(FLG_TRANSPARENT, &bch->Flags))
len = reqlen;
else /* with HDLC we do not know the length yet */
len = bch->maxlen;
bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC);
if (!bch->rx_skb) {
pr_warning("B%d receive no memory for %d bytes\n",
bch->nr, len);
len = -ENOMEM;
}
return len;
}
EXPORT_SYMBOL(bchannel_get_rxbuf);
...@@ -177,6 +177,7 @@ extern void queue_ch_frame(struct mISDNchannel *, u_int, ...@@ -177,6 +177,7 @@ extern void queue_ch_frame(struct mISDNchannel *, u_int,
int, struct sk_buff *); int, struct sk_buff *);
extern int dchannel_senddata(struct dchannel *, struct sk_buff *); extern int dchannel_senddata(struct dchannel *, struct sk_buff *);
extern int bchannel_senddata(struct bchannel *, struct sk_buff *); extern int bchannel_senddata(struct bchannel *, struct sk_buff *);
extern int bchannel_get_rxbuf(struct bchannel *, int);
extern void recv_Dchannel(struct dchannel *); extern void recv_Dchannel(struct dchannel *);
extern void recv_Echannel(struct dchannel *, struct dchannel *); extern void recv_Echannel(struct dchannel *, struct dchannel *);
extern void recv_Bchannel(struct bchannel *, unsigned int id); extern void recv_Bchannel(struct bchannel *, unsigned int id);
......
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