in nicstar.c [1980:2336]
static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe)
{
u32 vpi, vci;
vc_map *vc;
struct sk_buff *iovb;
struct iovec *iov;
struct atm_vcc *vcc;
struct sk_buff *skb;
unsigned short aal5_len;
int len;
u32 stat;
u32 id;
stat = readl(card->membase + STAT);
card->sbfqc = ns_stat_sfbqc_get(stat);
card->lbfqc = ns_stat_lfbqc_get(stat);
id = le32_to_cpu(rsqe->buffer_handle);
skb = idr_remove(&card->idr, id);
if (!skb) {
RXPRINTK(KERN_ERR
"nicstar%d: skb not found!\n", card->index);
return;
}
dma_sync_single_for_cpu(&card->pcidev->dev,
NS_PRV_DMA(skb),
(NS_PRV_BUFTYPE(skb) == BUF_SM
? NS_SMSKBSIZE : NS_LGSKBSIZE),
DMA_FROM_DEVICE);
dma_unmap_single(&card->pcidev->dev,
NS_PRV_DMA(skb),
(NS_PRV_BUFTYPE(skb) == BUF_SM
? NS_SMSKBSIZE : NS_LGSKBSIZE),
DMA_FROM_DEVICE);
vpi = ns_rsqe_vpi(rsqe);
vci = ns_rsqe_vci(rsqe);
if (vpi >= 1UL << card->vpibits || vci >= 1UL << card->vcibits) {
printk("nicstar%d: SDU received for out-of-range vc %d.%d.\n",
card->index, vpi, vci);
recycle_rx_buf(card, skb);
return;
}
vc = &(card->vcmap[vpi << card->vcibits | vci]);
if (!vc->rx) {
RXPRINTK("nicstar%d: SDU received on non-rx vc %d.%d.\n",
card->index, vpi, vci);
recycle_rx_buf(card, skb);
return;
}
vcc = vc->rx_vcc;
if (vcc->qos.aal == ATM_AAL0) {
struct sk_buff *sb;
unsigned char *cell;
int i;
cell = skb->data;
for (i = ns_rsqe_cellcount(rsqe); i; i--) {
sb = dev_alloc_skb(NS_SMSKBSIZE);
if (!sb) {
printk
("nicstar%d: Can't allocate buffers for aal0.\n",
card->index);
atomic_add(i, &vcc->stats->rx_drop);
break;
}
if (!atm_charge(vcc, sb->truesize)) {
RXPRINTK
("nicstar%d: atm_charge() dropped aal0 packets.\n",
card->index);
atomic_add(i - 1, &vcc->stats->rx_drop); /* already increased by 1 */
dev_kfree_skb_any(sb);
break;
}
/* Rebuild the header */
*((u32 *) sb->data) = le32_to_cpu(rsqe->word_1) << 4 |
(ns_rsqe_clp(rsqe) ? 0x00000001 : 0x00000000);
if (i == 1 && ns_rsqe_eopdu(rsqe))
*((u32 *) sb->data) |= 0x00000002;
skb_put(sb, NS_AAL0_HEADER);
memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD);
skb_put(sb, ATM_CELL_PAYLOAD);
ATM_SKB(sb)->vcc = vcc;
__net_timestamp(sb);
vcc->push(vcc, sb);
atomic_inc(&vcc->stats->rx);
cell += ATM_CELL_PAYLOAD;
}
recycle_rx_buf(card, skb);
return;
}
/* To reach this point, the AAL layer can only be AAL5 */
if ((iovb = vc->rx_iov) == NULL) {
iovb = skb_dequeue(&(card->iovpool.queue));
if (iovb == NULL) { /* No buffers in the queue */
iovb = alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC);
if (iovb == NULL) {
printk("nicstar%d: Out of iovec buffers.\n",
card->index);
atomic_inc(&vcc->stats->rx_drop);
recycle_rx_buf(card, skb);
return;
}
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
} else if (--card->iovpool.count < card->iovnr.min) {
struct sk_buff *new_iovb;
if ((new_iovb =
alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC)) != NULL) {
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
skb_queue_tail(&card->iovpool.queue, new_iovb);
card->iovpool.count++;
}
}
vc->rx_iov = iovb;
NS_PRV_IOVCNT(iovb) = 0;
iovb->len = 0;
iovb->data = iovb->head;
skb_reset_tail_pointer(iovb);
/* IMPORTANT: a pointer to the sk_buff containing the small or large
buffer is stored as iovec base, NOT a pointer to the
small or large buffer itself. */
} else if (NS_PRV_IOVCNT(iovb) >= NS_MAX_IOVECS) {
printk("nicstar%d: received too big AAL5 SDU.\n", card->index);
atomic_inc(&vcc->stats->rx_err);
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
NS_MAX_IOVECS);
NS_PRV_IOVCNT(iovb) = 0;
iovb->len = 0;
iovb->data = iovb->head;
skb_reset_tail_pointer(iovb);
}
iov = &((struct iovec *)iovb->data)[NS_PRV_IOVCNT(iovb)++];
iov->iov_base = (void *)skb;
iov->iov_len = ns_rsqe_cellcount(rsqe) * 48;
iovb->len += iov->iov_len;
#ifdef EXTRA_DEBUG
if (NS_PRV_IOVCNT(iovb) == 1) {
if (NS_PRV_BUFTYPE(skb) != BUF_SM) {
printk
("nicstar%d: Expected a small buffer, and this is not one.\n",
card->index);
which_list(card, skb);
atomic_inc(&vcc->stats->rx_err);
recycle_rx_buf(card, skb);
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb);
return;
}
} else { /* NS_PRV_IOVCNT(iovb) >= 2 */
if (NS_PRV_BUFTYPE(skb) != BUF_LG) {
printk
("nicstar%d: Expected a large buffer, and this is not one.\n",
card->index);
which_list(card, skb);
atomic_inc(&vcc->stats->rx_err);
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
NS_PRV_IOVCNT(iovb));
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb);
return;
}
}
#endif /* EXTRA_DEBUG */
if (ns_rsqe_eopdu(rsqe)) {
/* This works correctly regardless of the endianness of the host */
unsigned char *L1L2 = (unsigned char *)
(skb->data + iov->iov_len - 6);
aal5_len = L1L2[0] << 8 | L1L2[1];
len = (aal5_len == 0x0000) ? 0x10000 : aal5_len;
if (ns_rsqe_crcerr(rsqe) ||
len + 8 > iovb->len || len + (47 + 8) < iovb->len) {
printk("nicstar%d: AAL5 CRC error", card->index);
if (len + 8 > iovb->len || len + (47 + 8) < iovb->len)
printk(" - PDU size mismatch.\n");
else
printk(".\n");
atomic_inc(&vcc->stats->rx_err);
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
NS_PRV_IOVCNT(iovb));
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb);
return;
}
/* By this point we (hopefully) have a complete SDU without errors. */
if (NS_PRV_IOVCNT(iovb) == 1) { /* Just a small buffer */
/* skb points to a small buffer */
if (!atm_charge(vcc, skb->truesize)) {
push_rxbufs(card, skb);
atomic_inc(&vcc->stats->rx_drop);
} else {
skb_put(skb, len);
dequeue_sm_buf(card, skb);
ATM_SKB(skb)->vcc = vcc;
__net_timestamp(skb);
vcc->push(vcc, skb);
atomic_inc(&vcc->stats->rx);
}
} else if (NS_PRV_IOVCNT(iovb) == 2) { /* One small plus one large buffer */
struct sk_buff *sb;
sb = (struct sk_buff *)(iov - 1)->iov_base;
/* skb points to a large buffer */
if (len <= NS_SMBUFSIZE) {
if (!atm_charge(vcc, sb->truesize)) {
push_rxbufs(card, sb);
atomic_inc(&vcc->stats->rx_drop);
} else {
skb_put(sb, len);
dequeue_sm_buf(card, sb);
ATM_SKB(sb)->vcc = vcc;
__net_timestamp(sb);
vcc->push(vcc, sb);
atomic_inc(&vcc->stats->rx);
}
push_rxbufs(card, skb);
} else { /* len > NS_SMBUFSIZE, the usual case */
if (!atm_charge(vcc, skb->truesize)) {
push_rxbufs(card, skb);
atomic_inc(&vcc->stats->rx_drop);
} else {
dequeue_lg_buf(card, skb);
skb_push(skb, NS_SMBUFSIZE);
skb_copy_from_linear_data(sb, skb->data,
NS_SMBUFSIZE);
skb_put(skb, len - NS_SMBUFSIZE);
ATM_SKB(skb)->vcc = vcc;
__net_timestamp(skb);
vcc->push(vcc, skb);
atomic_inc(&vcc->stats->rx);
}
push_rxbufs(card, sb);
}
} else { /* Must push a huge buffer */
struct sk_buff *hb, *sb, *lb;
int remaining, tocopy;
int j;
hb = skb_dequeue(&(card->hbpool.queue));
if (hb == NULL) { /* No buffers in the queue */
hb = dev_alloc_skb(NS_HBUFSIZE);
if (hb == NULL) {
printk
("nicstar%d: Out of huge buffers.\n",
card->index);
atomic_inc(&vcc->stats->rx_drop);
recycle_iovec_rx_bufs(card,
(struct iovec *)
iovb->data,
NS_PRV_IOVCNT(iovb));
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb);
return;
} else if (card->hbpool.count < card->hbnr.min) {
struct sk_buff *new_hb;
if ((new_hb =
dev_alloc_skb(NS_HBUFSIZE)) !=
NULL) {
skb_queue_tail(&card->hbpool.
queue, new_hb);
card->hbpool.count++;
}
}
NS_PRV_BUFTYPE(hb) = BUF_NONE;
} else if (--card->hbpool.count < card->hbnr.min) {
struct sk_buff *new_hb;
if ((new_hb =
dev_alloc_skb(NS_HBUFSIZE)) != NULL) {
NS_PRV_BUFTYPE(new_hb) = BUF_NONE;
skb_queue_tail(&card->hbpool.queue,
new_hb);
card->hbpool.count++;
}
if (card->hbpool.count < card->hbnr.min) {
if ((new_hb =
dev_alloc_skb(NS_HBUFSIZE)) !=
NULL) {
NS_PRV_BUFTYPE(new_hb) =
BUF_NONE;
skb_queue_tail(&card->hbpool.
queue, new_hb);
card->hbpool.count++;
}
}
}
iov = (struct iovec *)iovb->data;
if (!atm_charge(vcc, hb->truesize)) {
recycle_iovec_rx_bufs(card, iov,
NS_PRV_IOVCNT(iovb));
if (card->hbpool.count < card->hbnr.max) {
skb_queue_tail(&card->hbpool.queue, hb);
card->hbpool.count++;
} else
dev_kfree_skb_any(hb);
atomic_inc(&vcc->stats->rx_drop);
} else {
/* Copy the small buffer to the huge buffer */
sb = (struct sk_buff *)iov->iov_base;
skb_copy_from_linear_data(sb, hb->data,
iov->iov_len);
skb_put(hb, iov->iov_len);
remaining = len - iov->iov_len;
iov++;
/* Free the small buffer */
push_rxbufs(card, sb);
/* Copy all large buffers to the huge buffer and free them */
for (j = 1; j < NS_PRV_IOVCNT(iovb); j++) {
lb = (struct sk_buff *)iov->iov_base;
tocopy =
min_t(int, remaining, iov->iov_len);
skb_copy_from_linear_data(lb,
skb_tail_pointer
(hb), tocopy);
skb_put(hb, tocopy);
iov++;
remaining -= tocopy;
push_rxbufs(card, lb);
}
#ifdef EXTRA_DEBUG
if (remaining != 0 || hb->len != len)
printk
("nicstar%d: Huge buffer len mismatch.\n",
card->index);
#endif /* EXTRA_DEBUG */
ATM_SKB(hb)->vcc = vcc;
__net_timestamp(hb);
vcc->push(vcc, hb);
atomic_inc(&vcc->stats->rx);
}
}
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb);
}
}