Commit 5ac16be8 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/davem/net-2.6

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 2ce6c0f5 c2d26a96
......@@ -94,6 +94,10 @@ static struct crypto_alg cipher_null = {
.cia_decrypt = null_decrypt } }
};
MODULE_ALIAS("compress_null");
MODULE_ALIAS("digest_null");
MODULE_ALIAS("cipher_null");
static int __init init(void)
{
int ret = 0;
......
......@@ -385,6 +385,15 @@ config ATM_FORE200E_SBA_FW
not have to supply an alternative one. They just say Y to "Use
default SBA-200E firmware", above.
config ATM_FORE200E_USE_TASKLET
bool "Defer interrupt work to a tasklet"
depends on (PCI || SBUS) && (ATM_FORE200E_PCA || ATM_FORE200E_SBA)
default n
help
This defers work to be done by the interrupt handler to a
tasklet instead of hanlding everything at interrupt time. This
may improve the responsive of the host.
config ATM_FORE200E_TX_RETRY
int "Maximum number of tx retries"
depends on (PCI || SBUS) && (ATM_FORE200E_PCA || ATM_FORE200E_SBA)
......
......@@ -2,7 +2,7 @@
$Id: fore200e.c,v 1.5 2000/04/14 10:10:34 davem Exp $
A FORE Systems 200E-series driver for ATM on Linux.
Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2000.
Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003.
Based on the PCA-200E driver from Uwe Dannowski (Uwe.Dannowski@inf.tu-dresden.de).
......@@ -33,6 +33,8 @@
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/atmdev.h>
#include <linux/sonet.h>
#include <linux/atm_suni.h>
......@@ -44,7 +46,6 @@
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/pci.h>
#ifdef CONFIG_ATM_FORE200E_SBA
#include <asm/idprom.h>
......@@ -54,25 +55,33 @@
#include <asm/pgtable.h>
#endif
#include <linux/module.h>
#if defined(CONFIG_ATM_FORE200E_USE_TASKLET) /* defer interrupt work to a tasklet */
#define FORE200E_USE_TASKLET
#endif
#include "fore200e.h"
#include "suni.h"
#if 0 /* enable the debugging code of the buffer supply queues */
#define FORE200E_BSQ_DEBUG
#endif
#if 1 /* ensure correct handling of 52-byte AAL0 SDUs used by atmdump-like apps */
#if 1 /* ensure correct handling of 52-byte AAL0 SDUs expected by atmdump-like apps */
#define FORE200E_52BYTE_AAL0_SDU
#endif
#define FORE200E_VERSION "0.2d"
#include "fore200e.h"
#include "suni.h"
#define FORE200E_VERSION "0.3e"
#define FORE200E "fore200e: "
#if 0 /* override .config */
#define CONFIG_ATM_FORE200E_DEBUG 1
#endif
#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG > 0)
#define DPRINTK(level, format, args...) do { if (CONFIG_ATM_FORE200E_DEBUG >= (level)) \
printk(FORE200E format, ##args); } while(0)
printk(FORE200E format, ##args); } while (0)
#else
#define DPRINTK(level, format, args...) while(0)
#define DPRINTK(level, format, args...) do {} while (0)
#endif
......@@ -89,6 +98,17 @@
#define MSECS(ms) (((ms)*HZ/1000)+1)
#if 1
#define ASSERT(expr) if (!(expr)) { \
printk(FORE200E "assertion failed! %s[%d]: %s\n", \
__FUNCTION__, __LINE__, #expr); \
panic(FORE200E "%s", __FUNCTION__); \
}
#else
#define ASSERT(expr) do {} while (0)
#endif
extern const struct atmdev_ops fore200e_ops;
extern const struct fore200e_bus fore200e_bus[];
......@@ -221,29 +241,6 @@ fore200e_chunk_free(struct fore200e* fore200e, struct chunk* chunk)
}
#if 0 /* currently unused */
static int
fore200e_checkup(struct fore200e* fore200e)
{
u32 hb1, hb2;
hb1 = fore200e->bus->read(&fore200e->cp_queues->heartbeat);
fore200e_spin(10);
hb2 = fore200e->bus->read(&fore200e->cp_queues->heartbeat);
if (hb2 <= hb1) {
printk(FORE200E "device %s heartbeat is not counting upwards, hb1 = %x; hb2 = %x\n",
fore200e->name, hb1, hb2);
return -EIO;
}
printk(FORE200E "device %s heartbeat is ok\n", fore200e->name);
return 0;
}
#endif
static void
fore200e_spin(int msecs)
{
......@@ -440,7 +437,6 @@ fore200e_shutdown(struct fore200e* fore200e)
}
#ifdef CONFIG_ATM_FORE200E_PCA
static u32 fore200e_pca_read(volatile u32* addr)
......@@ -505,20 +501,16 @@ static int
fore200e_pca_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk,
int size, int nbr, int alignment)
{
#if defined(__sparc_v9__)
/* returned chunks are page-aligned */
chunk->alloc_size = size * nbr;
chunk->alloc_addr = pci_alloc_consistent((struct pci_dev*)fore200e->bus_dev,
chunk->alloc_size,
&chunk->dma_addr);
if (chunk->alloc_addr == NULL || chunk->dma_addr == 0)
if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0))
return -ENOMEM;
chunk->align_addr = chunk->alloc_addr;
#else
if (fore200e_chunk_alloc(fore200e, chunk, size * nbr, alignment, FORE200E_DMA_BIDIRECTIONAL) < 0)
return -ENOMEM;
#endif
return 0;
}
......@@ -529,14 +521,10 @@ fore200e_pca_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk,
static void
fore200e_pca_dma_chunk_free(struct fore200e* fore200e, struct chunk* chunk)
{
#if defined(__sparc_v9__)
pci_free_consistent((struct pci_dev*)fore200e->bus_dev,
chunk->alloc_size,
chunk->alloc_addr,
chunk->dma_addr);
#else
fore200e_chunk_free(fore200e, chunk);
#endif
}
......@@ -544,7 +532,15 @@ static int
fore200e_pca_irq_check(struct fore200e* fore200e)
{
/* this is a 1 bit register */
return readl(fore200e->regs.pca.psr);
int irq_posted = readl(fore200e->regs.pca.psr);
#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG == 2)
if (irq_posted && (readl(fore200e->regs.pca.hcr) & PCA200E_HCR_OUTFULL)) {
DPRINTK(2,"FIFO OUT full, device %d\n", fore200e->atm_dev->number);
}
#endif
return irq_posted;
}
......@@ -578,7 +574,7 @@ fore200e_pca_map(struct fore200e* fore200e)
DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base);
/* gain access to the PCA-200E specific registers */
/* gain access to the PCA specific registers */
fore200e->regs.pca.hcr = (u32*)(fore200e->virt_base + PCA200E_HCR_OFFSET);
fore200e->regs.pca.imr = (u32*)(fore200e->virt_base + PCA200E_IMR_OFFSET);
fore200e->regs.pca.psr = (u32*)(fore200e->virt_base + PCA200E_PSR_OFFSET);
......@@ -593,8 +589,6 @@ fore200e_pca_unmap(struct fore200e* fore200e)
{
DPRINTK(2, "device %s being unmapped from memory\n", fore200e->name);
/* XXX iounmap() does nothing on PowerPC (at least in 2.2.12 and 2.3.41),
this leads to a kernel panic if the module is loaded and unloaded several times */
if (fore200e->virt_base != NULL)
iounmap(fore200e->virt_base);
}
......@@ -604,7 +598,7 @@ static int __init
fore200e_pca_configure(struct fore200e* fore200e)
{
struct pci_dev* pci_dev = (struct pci_dev*)fore200e->bus_dev;
u8 master_ctrl;
u8 master_ctrl, latency;
DPRINTK(2, "device %s being configured\n", fore200e->name);
......@@ -616,18 +610,26 @@ fore200e_pca_configure(struct fore200e* fore200e)
pci_read_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, &master_ctrl);
master_ctrl = master_ctrl
#if 0
| PCA200E_CTRL_DIS_CACHE_RD
| PCA200E_CTRL_DIS_WRT_INVAL
#endif
#if defined(__BIG_ENDIAN)
/* request the PCA board to convert the endianess of slave RAM accesses */
| PCA200E_CTRL_CONVERT_ENDIAN
#endif
#if 0
| PCA200E_CTRL_DIS_CACHE_RD
| PCA200E_CTRL_DIS_WRT_INVAL
| PCA200E_CTRL_ENA_CONT_REQ_MODE
| PCA200E_CTRL_2_CACHE_WRT_INVAL
#endif
| PCA200E_CTRL_LARGE_PCI_BURSTS;
pci_write_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, master_ctrl);
/* raise latency from 32 (default) to 192, as this seems to prevent NIC
lockups (under heavy rx loads) due to continuous 'FIFO OUT full' condition.
this may impact the performances of other PCI devices on the same bus, though */
latency = 192;
pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
fore200e->state = FORE200E_STATE_CONFIGURE;
return 0;
}
......@@ -656,11 +658,7 @@ fore200e_pca_detect(const struct fore200e_bus* bus, int index)
fore200e->bus = bus;
fore200e->bus_dev = pci_dev;
fore200e->irq = pci_dev->irq;
fore200e->phys_base = pci_resource_start (pci_dev, 0);
#if defined(__powerpc__)
fore200e->phys_base += KERNELBASE;
#endif
fore200e->phys_base = pci_resource_start(pci_dev, 0);
sprintf(fore200e->name, "%s-%d", bus->model_name, index - 1);
......@@ -728,8 +726,6 @@ fore200e_pca_proc_read(struct fore200e* fore200e, char *page)
#endif /* CONFIG_ATM_FORE200E_PCA */
#ifdef CONFIG_ATM_FORE200E_SBA
static u32
......@@ -799,7 +795,7 @@ fore200e_sba_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk,
chunk->alloc_size,
&chunk->dma_addr);
if (chunk->alloc_addr == NULL || chunk->dma_addr == 0)
if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0))
return -ENOMEM;
chunk->align_addr = chunk->alloc_addr;
......@@ -858,8 +854,7 @@ fore200e_sba_map(struct fore200e* fore200e)
struct sbus_dev* sbus_dev = (struct sbus_dev*)fore200e->bus_dev;
unsigned int bursts;
/* gain access to the SBA-200E specific registers */
/* gain access to the SBA specific registers */
fore200e->regs.sba.hcr = (u32*)sbus_ioremap(&sbus_dev->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR");
fore200e->regs.sba.bsr = (u32*)sbus_ioremap(&sbus_dev->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR");
fore200e->regs.sba.isr = (u32*)sbus_ioremap(&sbus_dev->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR");
......@@ -880,17 +875,6 @@ fore200e_sba_map(struct fore200e* fore200e)
if (sbus_can_dma_64bit(sbus_dev))
sbus_set_sbus64(sbus_dev, bursts);
#if 0
if (bursts & DMA_BURST16)
fore200e->bus->write(SBA200E_BSR_BURST16, fore200e->regs.sba.bsr);
else
if (bursts & DMA_BURST8)
fore200e->bus->write(SBA200E_BSR_BURST8, fore200e->regs.sba.bsr);
else
if (bursts & DMA_BURST4)
fore200e->bus->write(SBA200E_BSR_BURST4, fore200e->regs.sba.bsr);
#endif
fore200e->state = FORE200E_STATE_MAP;
return 0;
}
......@@ -935,13 +919,11 @@ fore200e_sba_detect(const struct fore200e_bus* bus, int index)
return NULL;
found:
#if 1
if (sbus_dev->num_registers != 4) {
printk(FORE200E "this %s device has %d instead of 4 registers\n",
bus->model_name, sbus_dev->num_registers);
return NULL;
}
#endif
fore200e = fore200e_kmalloc(sizeof(struct fore200e), GFP_KERNEL);
if (fore200e == NULL)
......@@ -994,18 +976,26 @@ fore200e_sba_proc_read(struct fore200e* fore200e, char *page)
static void
fore200e_irq_tx(struct fore200e* fore200e)
fore200e_tx_irq(struct fore200e* fore200e)
{
struct host_txq* txq = &fore200e->host_txq;
struct host_txq_entry* entry;
int i;
struct atm_vcc* vcc;
struct fore200e_vc_map* vc_map;
if (fore200e->host_txq.txing == 0)
return;
entry = fore200e->host_txq.host_entry;
for (;;) {
for (i = 0; i < QUEUE_SIZE_TX; i++) {
entry = &txq->host_entry[ txq->tail ];
if (*entry->status & STATUS_COMPLETE) {
if ((*entry->status & STATUS_COMPLETE) == 0) {
break;
}
DPRINTK(3, "TX COMPLETED: entry = %p, vcc = %p, skb = %p\n", entry, entry->vcc, entry->skb);
DPRINTK(3, "TX COMPLETED: entry = %p [tail = %d], vc_map = %p, skb = %p\n",
entry, txq->tail, entry->vc_map, entry->skb);
/* free copy of misaligned data */
if (entry->data)
......@@ -1015,25 +1005,114 @@ fore200e_irq_tx(struct fore200e* fore200e)
fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length,
FORE200E_DMA_TODEVICE);
/* notify tx completion */
if (entry->vcc->pop)
entry->vcc->pop(entry->vcc, entry->skb);
else
dev_kfree_skb_irq(entry->skb);
vc_map = entry->vc_map;
/* vcc closed since the time the entry was submitted for tx? */
if ((vc_map->vcc == NULL) ||
(test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) {
DPRINTK(1, "no ready vcc found for PDU sent on device %d\n",
fore200e->atm_dev->number);
dev_kfree_skb_any(entry->skb);
}
else {
ASSERT(vc_map->vcc);
/* vcc closed then immediately re-opened? */
if (vc_map->incarn != entry->incarn) {
/* when a vcc is closed, some PDUs may be still pending in the tx queue.
if the same vcc is immediately re-opened, those pending PDUs must
not be popped after the completion of their emission, as they refer
to the prior incarnation of that vcc. otherwise, vcc->sk->sk_wmem_alloc
would be decremented by the size of the (unrelated) skb, possibly
leading to a negative sk->sk_wmem_alloc count, ultimately freezing the vcc.
we thus bind the tx entry to the current incarnation of the vcc
when the entry is submitted for tx. When the tx later completes,
if the incarnation number of the tx entry does not match the one
of the vcc, then this implies that the vcc has been closed then re-opened.
we thus just drop the skb here. */
DPRINTK(1, "vcc closed-then-re-opened; dropping PDU sent on device %d\n",
fore200e->atm_dev->number);
dev_kfree_skb_any(entry->skb);
}
else {
vcc = vc_map->vcc;
ASSERT(vcc);
/* notify tx completion */
if (vcc->pop) {
vcc->pop(vcc, entry->skb);
}
else {
dev_kfree_skb_any(entry->skb);
}
#if 1
/* race fixed by the above incarnation mechanism, but... */
if (atomic_read(&vcc->sk->sk_wmem_alloc) < 0) {
atomic_set(&vcc->sk->sk_wmem_alloc, 0);
}
#endif
/* check error condition */
if (*entry->status & STATUS_ERROR)
atomic_inc(&entry->vcc->stats->tx_err);
atomic_inc(&vcc->stats->tx_err);
else
atomic_inc(&entry->vcc->stats->tx);
atomic_inc(&vcc->stats->tx);
}
}
*entry->status = STATUS_FREE;
fore200e->host_txq.txing--;
FORE200E_NEXT_ENTRY(txq->tail, QUEUE_SIZE_TX);
}
entry++;
}
#ifdef FORE200E_BSQ_DEBUG
int bsq_audit(int where, struct host_bsq* bsq, int scheme, int magn)
{
struct buffer* buffer;
int count = 0;
buffer = bsq->freebuf;
while (buffer) {
if (buffer->supplied) {
printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld supplied but in free list!\n",
where, scheme, magn, buffer->index);
}
if (buffer->magn != magn) {
printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected magn = %d\n",
where, scheme, magn, buffer->index, buffer->magn);
}
if (buffer->scheme != scheme) {
printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected scheme = %d\n",
where, scheme, magn, buffer->index, buffer->scheme);
}
if ((buffer->index < 0) || (buffer->index >= fore200e_rx_buf_nbr[ scheme ][ magn ])) {
printk(FORE200E "bsq_audit(%d): queue %d.%d, out of range buffer index = %ld !\n",
where, scheme, magn, buffer->index);
}
count++;
buffer = buffer->next;
}
if (count != bsq->freebuf_count) {
printk(FORE200E "bsq_audit(%d): queue %d.%d, %d bufs in free list, but freebuf_count = %d\n",
where, scheme, magn, count, bsq->freebuf_count);
}
return 0;
}
#endif
static void
......@@ -1050,27 +1129,41 @@ fore200e_supply(struct fore200e* fore200e)
bsq = &fore200e->host_bsq[ scheme ][ magn ];
if (fore200e_rx_buf_nbr[ scheme ][ magn ] - bsq->count > RBD_BLK_SIZE) {
#ifdef FORE200E_BSQ_DEBUG
bsq_audit(1, bsq, scheme, magn);
#endif
while (bsq->freebuf_count >= RBD_BLK_SIZE) {
DPRINTK(2, "supplying rx buffers to queue %d / %d, count = %d\n",
scheme, magn, bsq->count);
DPRINTK(2, "supplying %d rx buffers to queue %d / %d, freebuf_count = %d\n",
RBD_BLK_SIZE, scheme, magn, bsq->freebuf_count);
entry = &bsq->host_entry[ bsq->head ];
FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS);
for (i = 0; i < RBD_BLK_SIZE; i++) {
buffer = &bsq->buffer[ bsq->free ];
FORE200E_NEXT_ENTRY(bsq->free, fore200e_rx_buf_nbr[ scheme ][ magn ]);
/* take the first buffer in the free buffer list */
buffer = bsq->freebuf;
if (!buffer) {
printk(FORE200E "no more free bufs in queue %d.%d, but freebuf_count = %d\n",
scheme, magn, bsq->freebuf_count);
return;
}
bsq->freebuf = buffer->next;
#ifdef FORE200E_BSQ_DEBUG
if (buffer->supplied)
printk(FORE200E "queue %d.%d, buffer %lu already supplied\n",
scheme, magn, buffer->index);
buffer->supplied = 1;
#endif
entry->rbd_block->rbd[ i ].buffer_haddr = buffer->data.dma_addr;
entry->rbd_block->rbd[ i ].handle = FORE200E_BUF2HDL(buffer);
}
/* increase the number of supplied rx buffers */
bsq->count += RBD_BLK_SIZE;
FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS);
/* decrease accordingly the number of free rx buffers */
bsq->freebuf_count -= RBD_BLK_SIZE;
*entry->status = STATUS_PENDING;
fore200e->bus->write(entry->rbd_block_dma, &entry->cp_entry->rbd_block_haddr);
......@@ -1080,35 +1173,9 @@ fore200e_supply(struct fore200e* fore200e)
}
static struct atm_vcc*
fore200e_find_vcc(struct fore200e* fore200e, struct rpd* rpd)
{
struct sock *s;
struct atm_vcc* vcc;
struct hlist_node *node;
read_lock(&vcc_sklist_lock);
sk_for_each(s, node, &vcc_hash[rpd->atm_header.vci & (VCC_HTABLE_SIZE-1)]) {
vcc = atm_sk(s);
if (vcc->dev != fore200e->atm_dev)
continue;
if (vcc->vpi == rpd->atm_header.vpi && vcc->vci == rpd->atm_header.vci) {
read_unlock(&vcc_sklist_lock);
return vcc;
}
}
read_unlock(&vcc_sklist_lock);
return NULL;
}
static void
fore200e_push_rpd(struct fore200e* fore200e, struct rpd* rpd)
static int
fore200e_push_rpd(struct fore200e* fore200e, struct atm_vcc* vcc, struct rpd* rpd)
{
struct atm_vcc* vcc;
struct sk_buff* skb;
struct buffer* buffer;
struct fore200e_vcc* fore200e_vcc;
......@@ -1117,15 +1184,10 @@ fore200e_push_rpd(struct fore200e* fore200e, struct rpd* rpd)
u32 cell_header = 0;
#endif
vcc = fore200e_find_vcc(fore200e, rpd);
if (vcc == NULL) {
printk(FORE200E "no vcc found for PDU received on %d.%d.%d\n",
fore200e->atm_dev->number, rpd->atm_header.vpi, rpd->atm_header.vci);
return;
}
ASSERT(vcc);
fore200e_vcc = FORE200E_VCC(vcc);
ASSERT(fore200e_vcc);
#ifdef FORE200E_52BYTE_AAL0_SDU
if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.rxtp.max_sdu == ATM_AAL0_SDU)) {
......@@ -1145,10 +1207,10 @@ fore200e_push_rpd(struct fore200e* fore200e, struct rpd* rpd)
skb = alloc_skb(pdu_len, GFP_ATOMIC);
if (skb == NULL) {
DPRINTK(2, "unable to alloc new skb, rx PDU length = %d\n", pdu_len);
printk(FORE200E "unable to alloc new skb, rx PDU length = %d\n", pdu_len);
atomic_inc(&vcc->stats->rx_drop);
return;
return -ENOMEM;
}
do_gettimeofday(&skb->stamp);
......@@ -1180,6 +1242,7 @@ fore200e_push_rpd(struct fore200e* fore200e, struct rpd* rpd)
fore200e_vcc->rx_min_pdu = pdu_len;
if (pdu_len > fore200e_vcc->rx_max_pdu)
fore200e_vcc->rx_max_pdu = pdu_len;
fore200e_vcc->rx_pdu++;
/* push PDU */
if (atm_charge(vcc, skb->truesize) == 0) {
......@@ -1187,18 +1250,27 @@ fore200e_push_rpd(struct fore200e* fore200e, struct rpd* rpd)
DPRINTK(2, "receive buffers saturated for %d.%d.%d - PDU dropped\n",
vcc->itf, vcc->vpi, vcc->vci);
dev_kfree_skb_irq(skb);
return;
dev_kfree_skb_any(skb);
atomic_inc(&vcc->stats->rx_drop);
return -ENOMEM;
}
ASSERT(atomic_read(&vcc->sk->sk_wmem_alloc) >= 0);
vcc->push(vcc, skb);
atomic_inc(&vcc->stats->rx);
ASSERT(atomic_read(&vcc->sk->sk_wmem_alloc) >= 0);
return 0;
}
static void
fore200e_collect_rpd(struct fore200e* fore200e, struct rpd* rpd)
{
struct host_bsq* bsq;
struct buffer* buffer;
int i;
......@@ -1207,17 +1279,34 @@ fore200e_collect_rpd(struct fore200e* fore200e, struct rpd* rpd)
/* rebuild rx buffer address from rsd handle */
buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle);
/* decrease the number of supplied rx buffers */
fore200e->host_bsq[ buffer->scheme ][ buffer->magn ].count--;
bsq = &fore200e->host_bsq[ buffer->scheme ][ buffer->magn ];
#ifdef FORE200E_BSQ_DEBUG
bsq_audit(2, bsq, buffer->scheme, buffer->magn);
if (buffer->supplied == 0)
printk(FORE200E "queue %d.%d, buffer %ld was not supplied\n",
buffer->scheme, buffer->magn, buffer->index);
buffer->supplied = 0;
#endif
/* re-insert the buffer into the free buffer list */
buffer->next = bsq->freebuf;
bsq->freebuf = buffer;
/* then increment the number of free rx buffers */
bsq->freebuf_count++;
}
}
static void
fore200e_irq_rx(struct fore200e* fore200e)
fore200e_rx_irq(struct fore200e* fore200e)
{
struct host_rxq* rxq = &fore200e->host_rxq;
struct host_rxq_entry* entry;
struct atm_vcc* vcc;
struct fore200e_vc_map* vc_map;
for (;;) {
......@@ -1227,28 +1316,61 @@ fore200e_irq_rx(struct fore200e* fore200e)
if ((*entry->status & STATUS_COMPLETE) == 0)
break;
FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX);
vc_map = FORE200E_VC_MAP(fore200e, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
if ((vc_map->vcc == NULL) ||
(test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) {
DPRINTK(1, "no ready VC found for PDU received on %d.%d.%d\n",
fore200e->atm_dev->number,
entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
}
else {
vcc = vc_map->vcc;
ASSERT(vcc);
if ((*entry->status & STATUS_ERROR) == 0) {
fore200e_push_rpd(fore200e, entry->rpd);
fore200e_push_rpd(fore200e, vcc, entry->rpd);
}
else {
printk(FORE200E "damaged PDU on %d.%d.%d\n",
fore200e->atm_dev->number, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
DPRINTK(2, "damaged PDU on %d.%d.%d\n",
fore200e->atm_dev->number,
entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
atomic_inc(&vcc->stats->rx_err);
}
}
fore200e_collect_rpd(fore200e, entry->rpd);
FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX);
fore200e_supply(fore200e);
fore200e_collect_rpd(fore200e, entry->rpd);
/* rewrite the rpd address to ack the received PDU */
fore200e->bus->write(entry->rpd_dma, &entry->cp_entry->rpd_haddr);
*entry->status = STATUS_FREE;
fore200e_supply(fore200e);
}
}
#ifndef FORE200E_USE_TASKLET
static void
fore200e_irq(struct fore200e* fore200e)
{
unsigned long flags;
spin_lock_irqsave(&fore200e->q_lock, flags);
fore200e_rx_irq(fore200e);
spin_unlock_irqrestore(&fore200e->q_lock, flags);
spin_lock_irqsave(&fore200e->q_lock, flags);
fore200e_tx_irq(fore200e);
spin_unlock_irqrestore(&fore200e->q_lock, flags);
}
#endif
static irqreturn_t
fore200e_interrupt(int irq, void* dev, struct pt_regs* regs)
{
......@@ -1256,58 +1378,66 @@ fore200e_interrupt(int irq, void* dev, struct pt_regs* regs)
if (fore200e->bus->irq_check(fore200e) == 0) {
DPRINTK(3, "unexpected interrupt on device %c\n", fore200e->name[9]);
DPRINTK(3, "interrupt NOT triggered by device %d\n", fore200e->atm_dev->number);
return IRQ_NONE;
}
DPRINTK(3, "valid interrupt on device %c\n", fore200e->name[9]);
DPRINTK(3, "interrupt triggered by device %d\n", fore200e->atm_dev->number);
tasklet_schedule(&fore200e->tasklet);
#ifdef FORE200E_USE_TASKLET
tasklet_schedule(&fore200e->tx_tasklet);
tasklet_schedule(&fore200e->rx_tasklet);
#else
fore200e_irq(fore200e);
#endif
fore200e->bus->irq_ack(fore200e);
return IRQ_HANDLED;
}
#ifdef FORE200E_USE_TASKLET
static void
fore200e_tasklet(unsigned long data)
fore200e_tx_tasklet(unsigned long data)
{
struct fore200e* fore200e = (struct fore200e*) data;
unsigned long flags;
fore200e_irq_rx(fore200e);
DPRINTK(3, "tx tasklet scheduled for device %d\n", fore200e->atm_dev->number);
if (fore200e->host_txq.txing)
fore200e_irq_tx(fore200e);
spin_lock_irqsave(&fore200e->q_lock, flags);
fore200e_tx_irq(fore200e);
spin_unlock_irqrestore(&fore200e->q_lock, flags);
}
static void
fore200e_rx_tasklet(unsigned long data)
{
struct fore200e* fore200e = (struct fore200e*) data;
unsigned long flags;
DPRINTK(3, "rx tasklet scheduled for device %d\n", fore200e->atm_dev->number);
spin_lock_irqsave(&fore200e->q_lock, flags);
fore200e_rx_irq((struct fore200e*) data);
spin_unlock_irqrestore(&fore200e->q_lock, flags);
}
#endif
static int
fore200e_select_scheme(struct atm_vcc* vcc)
{
int scheme;
/* fairly balance the VCs over (identical) buffer schemes */
int scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO;
#if 1
/* fairly balance VCs over (identical) buffer schemes */
scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO;
#else
/* bit 7 of VPI magically selects the second buffer scheme */
if (vcc->vpi & (1<<7)) {
vcc->vpi &= ((1<<7) - 1); /* reset the magic bit */
scheme = BUFFER_SCHEME_TWO;
}
else {
scheme = BUFFER_SCHEME_ONE;
}
#endif
DPRINTK(1, "vpvc %d.%d.%d uses the %s buffer scheme\n",
vcc->itf, vcc->vpi, vcc->vci, scheme == BUFFER_SCHEME_ONE ? "first" : "second");
DPRINTK(1, "VC %d.%d.%d uses buffer scheme %d\n",
vcc->itf, vcc->vpi, vcc->vci, scheme);
return scheme;
}
static int
fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc* vcc, int mtu)
{
......@@ -1344,7 +1474,7 @@ fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc*
#ifdef FORE200E_52BYTE_AAL0_SDU
mtu = 48;
#endif
/* the MTU is unused by the cp, except in the case of AAL0 */
/* the MTU is not used by the cp, except in the case of AAL0 */
fore200e->bus->write(mtu, &entry->cp_entry->cmd.activate_block.mtu);
fore200e->bus->write(*(u32*)&vpvc, (u32*)&entry->cp_entry->cmd.activate_block.vpvc);
fore200e->bus->write(*(u32*)&activ_opcode, (u32*)&entry->cp_entry->cmd.activate_block.opcode);
......@@ -1359,13 +1489,13 @@ fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc*
*entry->status = STATUS_FREE;
if (ok == 0) {
printk(FORE200E "unable to %s vpvc %d.%d on device %s\n",
activate ? "open" : "close", vcc->vpi, vcc->vci, fore200e->name);
printk(FORE200E "unable to %s VC %d.%d.%d\n",
activate ? "open" : "close", vcc->itf, vcc->vpi, vcc->vci);
return -EIO;
}
DPRINTK(1, "vpvc %d.%d %sed on device %s\n", vcc->vpi, vcc->vci,
activate ? "open" : "clos", fore200e->name);
DPRINTK(1, "VC %d.%d.%d %sed\n", vcc->itf, vcc->vpi, vcc->vci,
activate ? "open" : "clos");
return 0;
}
......@@ -1378,7 +1508,7 @@ fore200e_rate_ctrl(struct atm_qos* qos, struct tpd_rate* rate)
{
if (qos->txtp.max_pcr < ATM_OC3_PCR) {
/* compute the data cells to idle cells ratio from the PCR */
/* compute the data cells to idle cells ratio from the tx PCR */
rate->data_cells = qos->txtp.max_pcr * FORE200E_MAX_BACK2BACK_CELLS / ATM_OC3_PCR;
rate->idle_cells = FORE200E_MAX_BACK2BACK_CELLS - rate->data_cells;
}
......@@ -1394,15 +1524,36 @@ fore200e_open(struct atm_vcc *vcc)
{
struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
struct fore200e_vcc* fore200e_vcc;
short vpi = vcc->vpi;
struct fore200e_vc_map* vc_map;
unsigned long flags;
int vci = vcc->vci;
short vpi = vcc->vpi;
/* ressource checking only? */
if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC)
return 0;
ASSERT((vpi >= 0) && (vpi < 1<<FORE200E_VPI_BITS));
ASSERT((vci >= 0) && (vci < 1<<FORE200E_VCI_BITS));
set_bit(ATM_VF_ADDR, &vcc->flags);
vcc->itf = vcc->dev->number;
spin_lock_irqsave(&fore200e->q_lock, flags);
vc_map = FORE200E_VC_MAP(fore200e, vpi, vci);
if (vc_map->vcc) {
spin_unlock_irqrestore(&fore200e->q_lock, flags);
printk(FORE200E "VC %d.%d.%d already in use\n",
fore200e->atm_dev->number, vpi, vci);
return -EINVAL;
}
vc_map->vcc = vcc;
spin_unlock_irqrestore(&fore200e->q_lock, flags);
fore200e_vcc = fore200e_kmalloc(sizeof(struct fore200e_vcc), GFP_ATOMIC);
if (fore200e_vcc == NULL) {
vc_map->vcc = NULL;
return -ENOMEM;
}
DPRINTK(2, "opening %d.%d.%d:%d QoS = (tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; "
"rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d)\n",
......@@ -1412,44 +1563,50 @@ fore200e_open(struct atm_vcc *vcc)
fore200e_traffic_class[ vcc->qos.rxtp.traffic_class ],
vcc->qos.rxtp.min_pcr, vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_cdv, vcc->qos.rxtp.max_sdu);
/* pseudo-CBR bandwidth requested? */
if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
down(&fore200e->rate_sf);
if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) {
up(&fore200e->rate_sf);
fore200e_kfree(fore200e_vcc);
vc_map->vcc = NULL;
return -EAGAIN;
}
/* reserving the pseudo-CBR bandwidth at this point grants us
to reduce the length of the critical section protected
by 'rate_sf'. in counterpart, we have to reset the available
bandwidth if we later encounter an error */
/* reserve bandwidth */
fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr;
up(&fore200e->rate_sf);
}
fore200e_vcc = fore200e_kmalloc(sizeof(struct fore200e_vcc), GFP_KERNEL);
if (fore200e_vcc == NULL) {
down(&fore200e->rate_sf);
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
up(&fore200e->rate_sf);
return -ENOMEM;
}
vcc->itf = vcc->dev->number;
set_bit(ATM_VF_PARTIAL,&vcc->flags);
set_bit(ATM_VF_ADDR, &vcc->flags);
vcc->dev_data = fore200e_vcc;
if (fore200e_activate_vcin(fore200e, 1, vcc, vcc->qos.rxtp.max_sdu) < 0) {
kfree(fore200e_vcc);
down(&fore200e->rate_sf);
vc_map->vcc = NULL;
clear_bit(ATM_VF_ADDR, &vcc->flags);
clear_bit(ATM_VF_PARTIAL,&vcc->flags);
vcc->dev_data = NULL;
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
up(&fore200e->rate_sf);
return -EBUSY;
fore200e_kfree(fore200e_vcc);
return -EINVAL;
}
/* compute rate control parameters */
if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
fore200e_rate_ctrl(&vcc->qos, &fore200e_vcc->rate);
set_bit(ATM_VF_HASQOS, &vcc->flags);
DPRINTK(3, "tx on %d.%d.%d:%d, tx PCR = %d, rx PCR = %d, data_cells = %u, idle_cells = %u\n",
vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal),
......@@ -1457,39 +1614,68 @@ fore200e_open(struct atm_vcc *vcc)
fore200e_vcc->rate.data_cells, fore200e_vcc->rate.idle_cells);
}
fore200e_vcc->tx_min_pdu = fore200e_vcc->rx_min_pdu = 65536;
fore200e_vcc->tx_min_pdu = fore200e_vcc->rx_min_pdu = MAX_PDU_SIZE + 1;
fore200e_vcc->tx_max_pdu = fore200e_vcc->rx_max_pdu = 0;
fore200e_vcc->tx_pdu = fore200e_vcc->rx_pdu = 0;
/* new incarnation of the vcc */
vc_map->incarn = ++fore200e->incarn_count;
/* VC unusable before this flag is set */
set_bit(ATM_VF_READY, &vcc->flags);
return 0;
}
static void
fore200e_close(struct atm_vcc* vcc)
{
struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
struct fore200e_vcc* fore200e_vcc;
struct fore200e_vc_map* vc_map;
unsigned long flags;
ASSERT(vcc);
ASSERT((vcc->vpi >= 0) && (vcc->vpi < 1<<FORE200E_VPI_BITS));
ASSERT((vcc->vci >= 0) && (vcc->vci < 1<<FORE200E_VCI_BITS));
DPRINTK(2, "closing %d.%d.%d:%d\n", vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal));
clear_bit(ATM_VF_READY, &vcc->flags);
fore200e_activate_vcin(fore200e, 0, vcc, 0);
kfree(FORE200E_VCC(vcc));
spin_lock_irqsave(&fore200e->q_lock, flags);
vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci);
/* the vc is no longer considered as "in use" by fore200e_open() */
vc_map->vcc = NULL;
vcc->itf = vcc->vci = vcc->vpi = 0;
fore200e_vcc = FORE200E_VCC(vcc);
FORE200E_VCC(vcc) = NULL;
spin_unlock_irqrestore(&fore200e->q_lock, flags);
/* release reserved bandwidth, if any */
if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
down(&fore200e->rate_sf);
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
up(&fore200e->rate_sf);
}
clear_bit(ATM_VF_READY, &vcc->flags);
}
clear_bit(ATM_VF_HASQOS, &vcc->flags);
}
clear_bit(ATM_VF_ADDR, &vcc->flags);
clear_bit(ATM_VF_PARTIAL,&vcc->flags);
#if 0
#define FORE200E_SYNC_SEND /* wait tx completion before returning */
#endif
ASSERT(fore200e_vcc);
fore200e_kfree(fore200e_vcc);
}
static int
......@@ -1497,17 +1683,30 @@ fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb)
{
struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc);
struct fore200e_vc_map* vc_map;
struct host_txq* txq = &fore200e->host_txq;
struct host_txq_entry* entry;
struct tpd* tpd;
struct tpd_haddr tpd_haddr;
//unsigned long flags;
int retry = CONFIG_ATM_FORE200E_TX_RETRY;
int tx_copy = 0;
int tx_len = skb->len;
u32* cell_header = NULL;
unsigned char* skb_data;
int skb_len;
unsigned char* data;
unsigned long flags;
ASSERT(vcc);
ASSERT(atomic_read(&vcc->sk->sk_wmem_alloc) >= 0);
ASSERT(fore200e);
ASSERT(fore200e_vcc);
if (!test_bit(ATM_VF_READY, &vcc->flags)) {
DPRINTK(1, "VC %d.%d.%d not ready for tx\n", vcc->itf, vcc->vpi, vcc->vpi);
dev_kfree_skb_any(skb);
return -EINVAL;
}
#ifdef FORE200E_52BYTE_AAL0_SDU
if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.txtp.max_sdu == ATM_AAL0_SDU)) {
......@@ -1515,7 +1714,7 @@ fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb)
skb_data = skb->data + 4; /* skip 4-byte cell header */
skb_len = tx_len = skb->len - 4;
DPRINTK(3, "skipping user-supplied cell header 0x%08x", *cell_header);
DPRINTK(3, "user-supplied cell header = 0x%08x\n", *cell_header);
}
else
#endif
......@@ -1524,85 +1723,96 @@ fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb)
skb_len = skb->len;
}
if (((unsigned long)skb_data) & 0x3) {
DPRINTK(2, "misaligned tx PDU on device %s\n", fore200e->name);
tx_copy = 1;
tx_len = skb_len;
}
if ((vcc->qos.aal == ATM_AAL0) && (skb_len % ATM_CELL_PAYLOAD)) {
/* this simply NUKES the PCA board */
DPRINTK(2, "incomplete tx AAL0 PDU on device %s\n", fore200e->name);
tx_copy = 1;
tx_len = ((skb_len / ATM_CELL_PAYLOAD) + 1) * ATM_CELL_PAYLOAD;
}
if (tx_copy) {
data = kmalloc(tx_len, GFP_ATOMIC | GFP_DMA);
if (data == NULL) {
if (vcc->pop) {
vcc->pop(vcc, skb);
}
else {
dev_kfree_skb_any(skb);
}
return -ENOMEM;
}
memcpy(data, skb_data, skb_len);
if (skb_len < tx_len)
memset(data + skb_len, 0x00, tx_len - skb_len);
}
else {
data = skb_data;
}
vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci);
ASSERT(vc_map->vcc == vcc);
retry_here:
tasklet_disable(&fore200e->tasklet);
spin_lock_irqsave(&fore200e->q_lock, flags);
entry = &txq->host_entry[ txq->head ];
if (*entry->status != STATUS_FREE) {
if ((*entry->status != STATUS_FREE) || (txq->txing >= QUEUE_SIZE_TX - 2)) {
/* try to free completed tx queue entries */
fore200e_irq_tx(fore200e);
fore200e_tx_irq(fore200e);
if (*entry->status != STATUS_FREE) {
tasklet_enable(&fore200e->tasklet);
spin_unlock_irqrestore(&fore200e->q_lock, flags);
/* retry once again? */
if(--retry > 0)
if (--retry > 0) {
schedule();
goto retry_here;
}
atomic_inc(&vcc->stats->tx_err);
printk(FORE200E "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n",
fore200e->tx_sat++;
DPRINTK(2, "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n",
fore200e->name, fore200e->cp_queues->heartbeat);
if (vcc->pop)
if (vcc->pop) {
vcc->pop(vcc, skb);
else
dev_kfree_skb(skb);
return -EIO;
}
else {
dev_kfree_skb_any(skb);
}
tpd = entry->tpd;
if (((unsigned long)skb_data) & 0x3) {
DPRINTK(2, "misaligned tx PDU on device %s\n", fore200e->name);
tx_copy = 1;
tx_len = skb_len;
}
if ((vcc->qos.aal == ATM_AAL0) && (skb_len % ATM_CELL_PAYLOAD)) {
if (tx_copy)
kfree(data);
/* this simply NUKES the PCA-200E board */
DPRINTK(2, "incomplete tx AAL0 PDU on device %s\n", fore200e->name);
tx_copy = 1;
tx_len = ((skb_len / ATM_CELL_PAYLOAD) + 1) * ATM_CELL_PAYLOAD;
return -ENOBUFS;
}
if (tx_copy) {
entry->data = kmalloc(tx_len, GFP_ATOMIC | GFP_DMA);
if (entry->data == NULL) {
tasklet_enable(&fore200e->tasklet);
if (vcc->pop)
vcc->pop(vcc, skb);
else
dev_kfree_skb(skb);
return -ENOMEM;
}
memcpy(entry->data, skb_data, skb_len);
if (skb_len < tx_len)
memset(entry->data + skb_len, 0x00, tx_len - skb_len);
tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, entry->data, tx_len, FORE200E_DMA_TODEVICE);
}
else {
entry->data = NULL;
tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, skb_data, tx_len, FORE200E_DMA_TODEVICE);
}
entry->incarn = vc_map->incarn;
entry->vc_map = vc_map;
entry->skb = skb;
entry->data = tx_copy ? data : NULL;
tpd = entry->tpd;
tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, data, tx_len, FORE200E_DMA_TODEVICE);
tpd->tsd[ 0 ].length = tx_len;
FORE200E_NEXT_ENTRY(txq->head, QUEUE_SIZE_TX);
txq->txing++;
tasklet_enable(&fore200e->tasklet);
/* The dma_map call above implies a dma_sync so the device can use it,
* thus no explicit dma_sync call is necessary here.
*/
......@@ -1615,9 +1825,7 @@ fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb)
fore200e_vcc->tx_min_pdu = skb_len;
if (skb_len > fore200e_vcc->tx_max_pdu)
fore200e_vcc->tx_max_pdu = skb_len;
entry->vcc = vcc;
entry->skb = skb;
fore200e_vcc->tx_pdu++;
/* set tx rate control information */
tpd->rate.data_cells = fore200e_vcc->rate.data_cells;
......@@ -1642,49 +1850,16 @@ fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb)
tpd->spec.length = tx_len;
tpd->spec.nseg = 1;
tpd->spec.aal = fore200e_atm2fore_aal(vcc->qos.aal);
#ifdef FORE200E_SYNC_SEND
tpd->spec.intr = 0;
#else
tpd->spec.intr = 1;
#endif
tpd_haddr.size = sizeof(struct tpd) / 32; /* size is expressed in 32 byte blocks */
tpd_haddr.size = sizeof(struct tpd) / (1<<TPD_HADDR_SHIFT); /* size is expressed in 32 byte blocks */
tpd_haddr.pad = 0;
tpd_haddr.haddr = entry->tpd_dma >> 5; /* shift the address, as we are in a bitfield */
tpd_haddr.haddr = entry->tpd_dma >> TPD_HADDR_SHIFT; /* shift the address, as we are in a bitfield */
*entry->status = STATUS_PENDING;
fore200e->bus->write(*(u32*)&tpd_haddr, (u32*)&entry->cp_entry->tpd_haddr);
#ifdef FORE200E_SYNC_SEND
{
int ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 10);
fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length,
FORE200E_DMA_TODEVICE);
/* free tmp copy of misaligned data */
if (entry->data)
kfree(entry->data);
/* notify tx completion */
if (vcc->pop)
vcc->pop(vcc, skb);
else
dev_kfree_skb(skb);
if (ok == 0) {
printk(FORE200E "synchronous tx on %d:%d:%d failed\n", vcc->itf, vcc->vpi, vcc->vci);
atomic_inc(&entry->vcc->stats->tx_err);
return -EIO;
}
atomic_inc(&entry->vcc->stats->tx);
DPRINTK(3, "synchronous tx on %d:%d:%d succeeded\n", vcc->itf, vcc->vpi, vcc->vci);
}
#endif
spin_unlock_irqrestore(&fore200e->q_lock, flags);
return 0;
}
......@@ -1705,7 +1880,8 @@ fore200e_getstats(struct fore200e* fore200e)
return -ENOMEM;
}
stats_dma_addr = fore200e->bus->dma_map(fore200e, fore200e->stats, sizeof(struct stats), FORE200E_DMA_FROMDEVICE);
stats_dma_addr = fore200e->bus->dma_map(fore200e, fore200e->stats,
sizeof(struct stats), FORE200E_DMA_FROMDEVICE);
FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
......@@ -1734,9 +1910,9 @@ fore200e_getstats(struct fore200e* fore200e)
static int
fore200e_getsockopt (struct atm_vcc* vcc, int level, int optname, void* optval, int optlen)
fore200e_getsockopt(struct atm_vcc* vcc, int level, int optname, void* optval, int optlen)
{
// struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
/* struct fore200e* fore200e = FORE200E_DEV(vcc->dev); */
DPRINTK(2, "getsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n",
vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen);
......@@ -1748,7 +1924,7 @@ fore200e_getsockopt (struct atm_vcc* vcc, int level, int optname, void* optval,
static int
fore200e_setsockopt(struct atm_vcc* vcc, int level, int optname, void* optval, int optlen)
{
// struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
/* struct fore200e* fore200e = FORE200E_DEV(vcc->dev); */
DPRINTK(2, "setsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n",
vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen);
......@@ -1806,6 +1982,8 @@ fore200e_set_oc3(struct fore200e* fore200e, u32 reg, u32 value, u32 mask)
struct oc3_opcode opcode;
int ok;
DPRINTK(2, "set OC-3 reg = 0x%02x, value = 0x%02x, mask = 0x%02x\n", reg, value, mask);
FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
opcode.opcode = OPCODE_SET_OC3;
......@@ -1861,7 +2039,7 @@ fore200e_setloop(struct fore200e* fore200e, int loop_mode)
}
error = fore200e_set_oc3(fore200e, SUNI_MCT, mct_value, mct_mask);
if ( error == 0)
if (error == 0)
fore200e->loop_mode = loop_mode;
return error;
......@@ -1943,6 +2121,11 @@ fore200e_change_qos(struct atm_vcc* vcc,struct atm_qos* qos, int flags)
struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc);
struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
if (!test_bit(ATM_VF_READY, &vcc->flags)) {
DPRINTK(1, "VC %d.%d.%d not ready for QoS change\n", vcc->itf, vcc->vpi, vcc->vpi);
return -EINVAL;
}
DPRINTK(2, "change_qos %d.%d.%d, "
"(tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; "
"rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d), flags = 0x%x\n"
......@@ -1964,6 +2147,7 @@ fore200e_change_qos(struct atm_vcc* vcc,struct atm_qos* qos, int flags)
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
fore200e->available_cell_rate -= qos->txtp.max_pcr;
up(&fore200e->rate_sf);
memcpy(&vcc->qos, qos, sizeof(struct atm_qos));
......@@ -1972,6 +2156,7 @@ fore200e_change_qos(struct atm_vcc* vcc,struct atm_qos* qos, int flags)
fore200e_rate_ctrl(qos, &fore200e_vcc->rate);
set_bit(ATM_VF_HASQOS, &vcc->flags);
return 0;
}
......@@ -1992,7 +2177,10 @@ fore200e_irq_request(struct fore200e* fore200e)
printk(FORE200E "IRQ %s reserved for device %s\n",
fore200e_irq_itoa(fore200e->irq), fore200e->name);
tasklet_init(&fore200e->tasklet, fore200e_tasklet, (unsigned long)fore200e);
#ifdef FORE200E_USE_TASKLET
tasklet_init(&fore200e->tx_tasklet, fore200e_tx_tasklet, (unsigned long)fore200e);
tasklet_init(&fore200e->rx_tasklet, fore200e_rx_tasklet, (unsigned long)fore200e);
#endif
fore200e->state = FORE200E_STATE_IRQ;
return 0;
......@@ -2007,6 +2195,7 @@ fore200e_get_esi(struct fore200e* fore200e)
if (!prom)
return -ENOMEM;
ok = fore200e->bus->prom_read(fore200e, prom);
if (ok < 0) {
fore200e_kfree(prom);
......@@ -2054,10 +2243,16 @@ fore200e_alloc_rx_buf(struct fore200e* fore200e)
if (buffer == NULL)
return -ENOMEM;
bsq->freebuf = NULL;
for (i = 0; i < nbr; i++) {
buffer[ i ].scheme = scheme;
buffer[ i ].magn = magn;
#ifdef FORE200E_BSQ_DEBUG
buffer[ i ].index = i;
buffer[ i ].supplied = 0;
#endif
/* allocate the receive buffer body */
if (fore200e_chunk_alloc(fore200e,
......@@ -2070,9 +2265,17 @@ fore200e_alloc_rx_buf(struct fore200e* fore200e)
return -ENOMEM;
}
/* insert the buffer into the free buffer list */
buffer[ i ].next = bsq->freebuf;
bsq->freebuf = &buffer[ i ];
}
/* set next free buffer index */
bsq->free = 0;
/* all the buffers are free, initially */
bsq->freebuf_count = nbr;
#ifdef FORE200E_BSQ_DEBUG
bsq_audit(3, bsq, scheme, magn);
#endif
}
}
......@@ -2258,10 +2461,11 @@ fore200e_init_tx_queue(struct fore200e* fore200e)
we do not write here the DMA (physical) base address of each tpd into
the related cp resident entry, because the cp relies on this write
operation to detect that a new pdu has been submitted for tx */
}
}
/* set the head entry of the queue */
/* set the head and tail entries of the queue */
txq->head = 0;
txq->tail = 0;
fore200e->state = FORE200E_STATE_INIT_TXQ;
return 0;
......@@ -2318,12 +2522,6 @@ fore200e_param_bs_queue(struct fore200e* fore200e,
{
struct bs_spec* bs_spec = &fore200e->cp_queues->init.bs_spec[ scheme ][ magn ];
/* dumb value; the firmware doesn't allow us to activate a VC while
selecting a buffer scheme with zero-sized rbd pools */
if (pool_size == 0)
pool_size = 64;
fore200e->bus->write(queue_length, &bs_spec->queue_length);
fore200e->bus->write(fore200e_rx_buf_size[ scheme ][ magn ], &bs_spec->buffer_size);
fore200e->bus->write(pool_size, &bs_spec->pool_size);
......@@ -2340,6 +2538,7 @@ fore200e_initialize(struct fore200e* fore200e)
DPRINTK(2, "device %s being initialized\n", fore200e->name);
init_MUTEX(&fore200e->rate_sf);
spin_lock_init(&fore200e->q_lock);
cpq = fore200e->cp_queues = (struct cp_queues*) (fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET);
......@@ -2422,7 +2621,7 @@ fore200e_monitor_getc(struct fore200e* fore200e)
static void __init
fore200e_monitor_puts(struct fore200e* fore200e, char* str)
{
while(*str) {
while (*str) {
/* the i960 monitor doesn't accept any new character if it has something to say */
while (fore200e_monitor_getc(fore200e) >= 0);
......@@ -2443,6 +2642,11 @@ fore200e_start_fw(struct fore200e* fore200e)
DPRINTK(2, "device %s firmware being started\n", fore200e->name);
#if defined(__sparc_v9__)
/* reported to be required by SBA cards on some sparc64 hosts */
fore200e_spin(100);
#endif
sprintf(cmd, "\rgo %x\r", le32_to_cpu(fw_header->start_offset));
fore200e_monitor_puts(fore200e, cmd);
......@@ -2473,12 +2677,10 @@ fore200e_load_fw(struct fore200e* fore200e)
DPRINTK(2, "device %s firmware being loaded at 0x%p (%d words)\n",
fore200e->name, load_addr, fw_size);
#if 1
if (le32_to_cpu(fw_header->magic) != FW_HEADER_MAGIC) {
printk(FORE200E "corrupted %s firmware image\n", fore200e->bus->model_name);
return -ENODEV;
}
#endif
for (; fw_size--; fw_data++, load_addr++)
fore200e->bus->write(le32_to_cpu(*fw_data), load_addr);
......@@ -2505,8 +2707,8 @@ fore200e_register(struct fore200e* fore200e)
atm_dev->dev_data = fore200e;
fore200e->atm_dev = atm_dev;
atm_dev->ci_range.vpi_bits = 8;
atm_dev->ci_range.vci_bits = 10;
atm_dev->ci_range.vpi_bits = FORE200E_VPI_BITS;
atm_dev->ci_range.vci_bits = FORE200E_VCI_BITS;
fore200e->available_cell_rate = ATM_OC3_PCR;
......@@ -2574,7 +2776,7 @@ fore200e_module_init(void)
struct fore200e* fore200e;
int index, link;
printk(FORE200E "FORE Systems 200E-series driver - version " FORE200E_VERSION "\n");
printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n");
/* for each configured bus interface */
for (link = 0, bus = fore200e_bus; bus->model_name; bus++) {
......@@ -2622,12 +2824,13 @@ fore200e_module_cleanup(void)
static int
fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page)
{
struct sock *s;
struct hlist_node *node;
struct fore200e* fore200e = FORE200E_DEV(dev);
struct fore200e_vcc* fore200e_vcc;
struct atm_vcc* vcc;
int i, len, left = *pos;
unsigned long flags;
if (!left--) {
......@@ -2660,14 +2863,15 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
if (!left--)
return sprintf(page,
" supplied small bufs (1):\t%d\n"
" supplied large bufs (1):\t%d\n"
" supplied small bufs (2):\t%d\n"
" supplied large bufs (2):\t%d\n",
fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].count,
fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].count,
fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].count,
fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].count);
" free small bufs, scheme 1:\t%d\n"
" free large bufs, scheme 1:\t%d\n"
" free small bufs, scheme 2:\t%d\n"
" free large bufs, scheme 2:\t%d\n",
fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].freebuf_count,
fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].freebuf_count,
fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].freebuf_count,
fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].freebuf_count);
if (!left--) {
u32 hb = fore200e->bus->read(&fore200e->cp_queues->heartbeat);
......@@ -2706,7 +2910,7 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
u32 media_index = FORE200E_MEDIA_INDEX(fore200e->bus->read(&fore200e->cp_queues->media_type));
u32 oc3_index;
if (media_index < 0 || media_index > 4)
if ((media_index < 0) || (media_index > 4))
media_index = 5;
switch (fore200e->loop_mode) {
......@@ -2853,12 +3057,14 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
" large b1:\t\t\t%10u\n"
" small b2:\t\t\t%10u\n"
" large b2:\t\t\t%10u\n"
" RX PDUs:\t\t\t%10u\n",
" RX PDUs:\t\t\t%10u\n"
" TX PDUs:\t\t\t%10lu\n",
fore200e_swap(fore200e->stats->aux.small_b1_failed),
fore200e_swap(fore200e->stats->aux.large_b1_failed),
fore200e_swap(fore200e->stats->aux.small_b2_failed),
fore200e_swap(fore200e->stats->aux.large_b2_failed),
fore200e_swap(fore200e->stats->aux.rpd_alloc_failed));
fore200e_swap(fore200e->stats->aux.rpd_alloc_failed),
fore200e->tx_sat);
if (!left--)
return sprintf(page,"\n"
......@@ -2866,40 +3072,43 @@ fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page)
fore200e->stats->aux.receive_carrier ? "ON" : "OFF!");
if (!left--) {
struct atm_vcc *vcc;
struct fore200e_vcc* fore200e_vcc;
len = sprintf(page,"\n"
" VCCs:\n address\tVPI.VCI:AAL\t(min/max tx PDU size) (min/max rx PDU size)\n");
return sprintf(page,"\n"
" VCCs:\n address VPI VCI AAL "
"TX PDUs TX min/max size RX PDUs RX min/max size\n");
}
read_lock(&vcc_sklist_lock);
for(i = 0; i < VCC_HTABLE_SIZE; ++i) {
struct hlist_head *head = &vcc_hash[i];
for (i = 0; i < NBR_CONNECT; i++) {
sk_for_each(s, node, head) {
vcc = atm_sk(s);
vcc = fore200e->vc_map[i].vcc;
if (vcc->dev != fore200e->atm_dev)
if (vcc == NULL)
continue;
spin_lock_irqsave(&fore200e->q_lock, flags);
if (vcc && test_bit(ATM_VF_READY, &vcc->flags) && !left--) {
fore200e_vcc = FORE200E_VCC(vcc);
ASSERT(fore200e_vcc);
len += sprintf(page + len,
" %x\t%d.%d:%d\t\t(%d/%d)\t(%d/%d)\n",
len = sprintf(page,
" %08x %03d %05d %1d %09lu %05d/%05d %09lu %05d/%05d\n",
(u32)(unsigned long)vcc,
vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal),
fore200e_vcc->tx_pdu,
fore200e_vcc->tx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->tx_min_pdu,
fore200e_vcc->tx_max_pdu,
fore200e_vcc->rx_pdu,
fore200e_vcc->rx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->rx_min_pdu,
fore200e_vcc->rx_max_pdu
);
}
}
read_unlock(&vcc_sklist_lock);
fore200e_vcc->rx_max_pdu);
spin_unlock_irqrestore(&fore200e->q_lock, flags);
return len;
}
spin_unlock_irqrestore(&fore200e->q_lock, flags);
}
return 0;
}
......@@ -2917,7 +3126,7 @@ static const struct atmdev_ops fore200e_ops =
.send = fore200e_send,
.change_qos = fore200e_change_qos,
.proc_read = fore200e_proc_read,
.owner = THIS_MODULE,
.owner = THIS_MODULE
};
......@@ -2980,4 +3189,6 @@ static const struct fore200e_bus fore200e_bus[] = {
{}
};
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
......@@ -23,19 +23,21 @@
#define BUFFER_S2_SIZE SMALL_BUFFER_SIZE /* size of small buffers, scheme 2 */
#define BUFFER_L2_SIZE LARGE_BUFFER_SIZE /* size of large buffers, scheme 2 */
#define BUFFER_S1_NBR (RBD_BLK_SIZE * 2)
#define BUFFER_L1_NBR (RBD_BLK_SIZE * 2)
#define BUFFER_S1_NBR (RBD_BLK_SIZE * 6)
#define BUFFER_L1_NBR (RBD_BLK_SIZE * 4)
#define BUFFER_S2_NBR (RBD_BLK_SIZE * 2)
#define BUFFER_L2_NBR (RBD_BLK_SIZE * 2)
#define BUFFER_S2_NBR (RBD_BLK_SIZE * 6)
#define BUFFER_L2_NBR (RBD_BLK_SIZE * 4)
#define QUEUE_SIZE_CMD 16 /* command queue capacity */
#define QUEUE_SIZE_RX 64 /* receive queue capacity */
#define QUEUE_SIZE_TX 256 /* transmit queue capacity */
#define QUEUE_SIZE_BS 16 /* buffer supply queue capacity */
#define QUEUE_SIZE_BS 32 /* buffer supply queue capacity */
#define NBR_CONNECT 1024 /* number of ATM connections */
#define FORE200E_VPI_BITS 0
#define FORE200E_VCI_BITS 10
#define NBR_CONNECT (1 << (FORE200E_VPI_BITS + FORE200E_VCI_BITS)) /* number of connections */
#define TSD_FIXED 2
......@@ -207,6 +209,7 @@ typedef struct tpd_haddr {
)
} tpd_haddr_t;
#define TPD_HADDR_SHIFT 5 /* addr aligned on 32 byte boundary */
/* cp resident transmit queue entry */
......@@ -522,8 +525,10 @@ typedef struct host_txq_entry {
struct tpd* tpd; /* addr of transmit PDU descriptor */
u32 tpd_dma; /* DMA address of tpd */
struct sk_buff* skb; /* related skb */
struct atm_vcc* vcc; /* related vcc */
void* data; /* copy of misaligned data */
unsigned long incarn; /* vc_map incarnation when submitted for tx */
struct fore200e_vc_map* vc_map;
} host_txq_entry_t;
......@@ -576,6 +581,10 @@ typedef struct buffer {
enum buffer_scheme scheme; /* buffer scheme */
enum buffer_magn magn; /* buffer magnitude */
struct chunk data; /* data buffer */
#ifdef FORE200E_BSQ_DEBUG
unsigned long index; /* buffer # in queue */
int supplied; /* 'buffer supplied' flag */
#endif
} buffer_t;
......@@ -602,6 +611,7 @@ typedef struct host_cmdq {
typedef struct host_txq {
struct host_txq_entry host_entry[ QUEUE_SIZE_TX ]; /* host resident tx queue entries */
int head; /* head of tx queue */
int tail; /* tail of tx queue */
struct chunk tpd; /* array of tpds */
struct chunk status; /* arry of completion status */
int txing; /* number of pending PDUs in tx queue */
......@@ -626,8 +636,8 @@ typedef struct host_bsq {
struct chunk rbd_block; /* array of rbds */
struct chunk status; /* array of completion status */
struct buffer* buffer; /* array of rx buffers */
int free; /* index of first free rx buffer */
volatile int count; /* count of supplied rx buffers */
struct buffer* freebuf; /* list of free rx buffers */
volatile int freebuf_count; /* count of free rx buffers */
} host_bsq_t;
......@@ -847,6 +857,17 @@ typedef struct fore200e_bus {
#endif
/* vc mapping */
typedef struct fore200e_vc_map {
struct atm_vcc* vcc; /* vcc entry */
unsigned long incarn; /* vcc incarnation number */
} fore200e_vc_map_t;
#define FORE200E_VC_MAP(fore200e, vpi, vci) \
(& (fore200e)->vc_map[ ((vpi) << FORE200E_VCI_BITS) | (vci) ])
/* per-device data */
typedef struct fore200e {
......@@ -880,8 +901,15 @@ typedef struct fore200e {
struct stats* stats; /* last snapshot of the stats */
struct semaphore rate_sf; /* protects rate reservation ops */
struct tasklet_struct tasklet; /* performs interrupt work */
spinlock_t q_lock; /* protects queue ops */
#ifdef FORE200E_USE_TASKLET
struct tasklet_struct tx_tasklet; /* performs tx interrupt work */
struct tasklet_struct rx_tasklet; /* performs rx interrupt work */
#endif
unsigned long tx_sat; /* tx queue saturation count */
unsigned long incarn_count;
struct fore200e_vc_map vc_map[ NBR_CONNECT ]; /* vc mapping */
} fore200e_t;
......@@ -894,6 +922,8 @@ typedef struct fore200e_vcc {
int rx_max_pdu; /* size of largest PDU received */
int tx_min_pdu; /* size of smallest PDU transmitted */
int tx_max_pdu; /* size of largest PDU transmitted */
unsigned long tx_pdu; /* nbr of tx pdus */
unsigned long rx_pdu; /* nbr of rx pdus */
} fore200e_vcc_t;
......
......@@ -21,6 +21,7 @@
#include <linux/types.h> /* for "__kernel_caddr_t" et al */
#include <linux/socket.h> /* for "struct sockaddr" et al */
#include <linux/compiler.h> /* for "__user" et al */
#define IFNAMSIZ 16
#include <linux/hdlc/ioctl.h>
......
......@@ -98,9 +98,10 @@ enum
TCPDIAG_NONE,
TCPDIAG_MEMINFO,
TCPDIAG_INFO,
TCPDIAG_VEGASINFO,
};
#define TCPDIAG_MAX TCPDIAG_INFO
#define TCPDIAG_MAX TCPDIAG_VEGASINFO
/* TCPDIAG_MEM */
......@@ -113,4 +114,14 @@ struct tcpdiag_meminfo
__u32 tcpdiag_tmem;
};
/* TCPDIAG_VEGASINFO */
struct tcpvegas_info {
__u32 tcpv_enabled;
__u32 tcpv_rttcnt;
__u32 tcpv_rtt;
__u32 tcpv_minrtt;
};
#endif /* _TCP_DIAG_H_ */
......@@ -50,6 +50,7 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk,
struct nlmsghdr *nlh;
struct tcp_info *info = NULL;
struct tcpdiag_meminfo *minfo = NULL;
struct tcpvegas_info *vinfo = NULL;
unsigned char *b = skb->tail;
nlh = NLMSG_PUT(skb, pid, seq, TCPDIAG_GETSOCK, sizeof(*r));
......@@ -59,6 +60,9 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk,
minfo = TCPDIAG_PUT(skb, TCPDIAG_MEMINFO, sizeof(*minfo));
if (ext & (1<<(TCPDIAG_INFO-1)))
info = TCPDIAG_PUT(skb, TCPDIAG_INFO, sizeof(*info));
if (tcp_is_vegas(tp) && (ext & (1<<(TCPDIAG_VEGASINFO-1))))
vinfo = TCPDIAG_PUT(skb, TCPDIAG_VEGASINFO, sizeof(*vinfo));
}
r->tcpdiag_family = sk->sk_family;
r->tcpdiag_state = sk->sk_state;
......@@ -196,6 +200,13 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk,
info->tcpi_reordering = tp->reordering;
}
if (vinfo) {
vinfo->tcpv_enabled = tp->vegas.doing_vegas_now;
vinfo->tcpv_rttcnt = tp->vegas.cntRTT;
vinfo->tcpv_rtt = tp->vegas.baseRTT;
vinfo->tcpv_minrtt = tp->vegas.minRTT;
}
nlh->nlmsg_len = skb->tail - b;
return skb->len;
......
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