diff -ur --new-file old/linux/Documentation/Configure.help new/linux/Documentation/Configure.help --- old/linux/Documentation/Configure.help Thu Sep 24 20:29:24 1998 +++ new/linux/Documentation/Configure.help Thu Sep 24 20:30:08 1998 @@ -3187,11 +3187,19 @@ if you suspect problems with single-copy. Classical IP over ATM -CONFIG_ATM_ATMARP +CONFIG_ATM_CLIP Classical IP over ATM for PVCs and SVCs, supporting InARP and ATMARP. Typically you will either use LAN Emulation (LANE) or Classical IP to communicate with other IP hosts on your ATM network. +Do NOT send ICMP if no neighbour +CONFIG_ATM_CLIP_NO_ICMP + Normally, an ICMP host unreachable message is sent if a neighbour cannot + be reached because there is no VC to it in the kernel's ATMARP table. + This may cause problems when ATMARP table entries are briefly removed + during revalidation. If this configuration option is set to "yes", + packets to such neighbours are silently discarded instead. + Application REQUested IP over ATM CONFIG_AREQUIPA Arequipa is a mechanism to create ATM connections under application @@ -3238,6 +3246,12 @@ measurements need to be periodically synchronized with the normal system time. Enabling this feature will add some general overhead for timer synchronization and also per-packet overhead for time conversion. + +IDT 77201 (NICStAR) +CONFIG_ATM_NICSTAR + The NICStAR chipset family is used in a large number of ATM NICs for + 25 and for 155 Mbps, including IDT cards and the Fore ForeRunnerLE + series. SCSI support? CONFIG_SCSI diff -ur --new-file old/linux/drivers/atm/eni.c new/linux/drivers/atm/eni.c --- old/linux/drivers/atm/eni.c Thu Sep 24 20:29:25 1998 +++ new/linux/drivers/atm/eni.c Thu Sep 24 20:30:08 1998 @@ -1049,7 +1049,8 @@ (size/(ATM_CELL_PAYLOAD/4)); /*printk("dsc = 0x%08lx\n",tx->send[tx->tx_pos]);*/ tx->send[(tx->tx_pos+1) & (tx->words-1)] = (vcc->vci << - MID_SEG_VCI_SHIFT) | (aal5 ? 0 : (skb->data[3] & 0xf)); + MID_SEG_VCI_SHIFT) | (aal5 ? 0 : (skb->data[3] & 0xf)) | + (skb->atm.atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0); DPRINTK("size: %ld, len:%d\n",size,skb->len); if (aal5) tx->send[(tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1)] = diff -ur --new-file old/linux/drivers/atm/nicstar.c new/linux/drivers/atm/nicstar.c --- old/linux/drivers/atm/nicstar.c Thu Sep 24 20:29:25 1998 +++ new/linux/drivers/atm/nicstar.c Thu Sep 24 20:30:08 1998 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include "nicstar.h" @@ -137,6 +138,7 @@ static int ns_proc_read(struct atm_dev *dev, loff_t *pos, char *page); static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg); static void which_list(ns_dev *card, struct sk_buff *skb); +static void ns_poll(unsigned long arg); /* Global variables ***********************************************************/ @@ -161,6 +163,7 @@ NULL, /* free_rx_skb */ ns_proc_read /* proc_read */ }; +static struct timer_list ns_timer; /* Functions*******************************************************************/ @@ -214,6 +217,12 @@ #endif /* PHY_LOOPBACK */ XPRINTK("nicstar: init_module() returned.\n"); + ns_timer.next = NULL; + ns_timer.prev = NULL; + ns_timer.expires = jiffies + NS_POLL_PERIOD; + ns_timer.data = 0UL; + ns_timer.function = ns_poll; + add_timer(&ns_timer); return 0; } @@ -234,6 +243,8 @@ if (MOD_IN_USE) printk("nicstar: module in use, remove delayed.\n"); + del_timer(&ns_timer); + for (i = 0; i < NS_MAX_CARDS; i++) { if (cards[i] == NULL) @@ -784,7 +795,10 @@ card->hbpool.count++; } - + card->in_handler = 0; + card->in_poll = 0; + card->intcnt = 0; + /* Configure NICStAR */ if (card->rct_size == 4096) ns_cfg_rctsize = NS_CFG_RCTSIZE_4096_ENTRIES; @@ -1102,8 +1116,23 @@ ns_dev *card; card = (ns_dev *) dev_id; + card->intcnt++; PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index); + + if (card->in_handler) + { + printk("nicstar%d: Re-entering ns_irq_handler()???\n", card->index); + return; + } + card->in_handler = 1; + if (card->in_poll) + { + card->in_handler = 0; + printk("nicstar%d: Called irq handler while in ns_poll()!?\n", + card->index); + return; + } stat_r = readl(card->membase + STAT); @@ -1112,9 +1141,7 @@ { TXPRINTK("nicstar%d: TSI interrupt\n", card->index); process_tsq(card); - /* If there are no entries to process, clear the interrupt */ - if (readl(card->membase + TSQT) == (u32) virt_to_bus(card->tsq.next)) - writel(NS_STAT_TSIF, card->membase + STAT); + writel(NS_STAT_TSIF, card->membase + STAT); } /* Incomplete CS-PDU has been transmitted */ @@ -1174,9 +1201,7 @@ { RXPRINTK("nicstar%d: End of CS-PDU received.\n", card->index); process_rsq(card); - /* If there are no entries to process, clear the interrupt */ - if (readl(card->membase + RSQT) == (u32) virt_to_bus(card->rsq.next)) - writel(NS_STAT_EOPDU, card->membase + STAT); + writel(NS_STAT_EOPDU, card->membase + STAT); } /* Raw cell received */ @@ -1269,7 +1294,8 @@ process_rsq(card); } - XPRINTK("nicstar%d: end of interrupt service\n", card->index); + card->in_handler = 0; + PRINTK("nicstar%d: end of interrupt service\n", card->index); } @@ -1288,6 +1314,7 @@ warning. How I wish compilers were clever enough to tell which variables can truly be used uninitialized... */ + int inuse; /* tx or rx vc already in use by another vcc */ card = (ns_dev *) vcc->dev->dev_data; PRINTK("nicstar%d: opening vpi.vci %d.%d \n", card->index, (int) vpi, vci); @@ -1306,103 +1333,113 @@ vcc->vpi = vpi; vcc->vci = vci; vcc->dev_data = vc; + + inuse = 0; + if (vcc->qos.txtp.traffic_class != ATM_NONE && vc->tx) + inuse = 1; + if (vcc->qos.rxtp.traffic_class != ATM_NONE && vc->rx) + inuse += 2; + if (inuse) + { + printk("nicstar%d: %s vci already in use.\n", card->index, + inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx"); + return -EINVAL; + } + vcc->flags |= ATM_VF_ADDR; - /* Check requested cell rate and availability of SCD if CBR */ - if (vcc->qos.txtp.traffic_class == ATM_CBR) + /* NOTE: You are not allowed to modify an open connection's QOS. To change + that, remove the ATM_VF_PARTIAL flag checking. There may be other changes + needed to do that. */ + if (!(vcc->flags & ATM_VF_PARTIAL)) { - if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0 && - vcc->qos.txtp.min_pcr == 0) + scq_info *scq; + + vcc->flags |= ATM_VF_PARTIAL; + if (vcc->qos.txtp.traffic_class == ATM_CBR) { - PRINTK("nicstar%d: trying to open a CBR vc with cell rate = 0 \n", - card->index); - vcc->flags &= ~(ATM_VF_ADDR); - return -EINVAL; - } + /* Check requested cell rate and availability of SCD */ + if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0 && + vcc->qos.txtp.min_pcr == 0) + { + PRINTK("nicstar%d: trying to open a CBR vc with cell rate = 0 \n", + card->index); + vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL); + return -EINVAL; + } - tcr = atm_pcr_goal(&(vcc->qos.txtp)); - tcra = tcr >= 0 ? tcr : -tcr; + tcr = atm_pcr_goal(&(vcc->qos.txtp)); + tcra = tcr >= 0 ? tcr : -tcr; - PRINTK("nicstar%d: target cell rate = %d.\n", card->index, - vcc->qos.txtp.max_pcr); + PRINTK("nicstar%d: target cell rate = %d.\n", card->index, + vcc->qos.txtp.max_pcr); - tmpd = ((double) tcra) * ((double) NS_TST_NUM_ENTRIES) / - ((double) card->max_pcr); + tmpd = ((double) tcra) * ((double) NS_TST_NUM_ENTRIES) / + ((double) card->max_pcr); - n = (int) tmpd; - if (tcr > 0) - { - if (tmpd > (double) n) n++; - } - else if (tcr < 0) - { - if (tmpd < (double) n) n--; - } - else /* tcr == 0 */ - { - if ((n = (card->tst_free_entries - NS_TST_RESERVED)) <= 0) - { - PRINTK("nicstar%d: no CBR bandwidth free.\n", card->index); - vcc->flags &= ~(ATM_VF_ADDR); - return -EINVAL; - } - } + n = (int) tmpd; + if (tcr > 0) + { + if (tmpd > (double) n) n++; + } + else if (tcr < 0) + { + if (tmpd < (double) n) n--; + } + else /* tcr == 0 */ + { + if ((n = (card->tst_free_entries - NS_TST_RESERVED)) <= 0) + { + PRINTK("nicstar%d: no CBR bandwidth free.\n", card->index); + vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL); + return -EINVAL; + } + } - if (n == 0) - { - printk("nicstar%d: selected bandwidth < granularity.\n", card->index); - vcc->flags &= ~(ATM_VF_ADDR); + if (n == 0) + { + printk("nicstar%d: selected bandwidth < granularity.\n", card->index); + vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL); return -EINVAL; - } + } - if (vcc->qos.txtp.max_pcr > 0) - { - tmpd = (double) n * (double) card->max_pcr / - (double) NS_TST_NUM_ENTRIES; - if (tmpd > PCR_TOLERANCE * (double) vcc->qos.txtp.max_pcr) + if (vcc->qos.txtp.max_pcr > 0) { - PRINTK("nicstar%d: target cell rate exceeded requested max_pcr.\n", - card->index); + tmpd = (double) n * (double) card->max_pcr / + (double) NS_TST_NUM_ENTRIES; + if (tmpd > PCR_TOLERANCE * (double) vcc->qos.txtp.max_pcr) + { + PRINTK("nicstar%d: target cell rate exceeded requested max_pcr.\n", + card->index); + } } - } - if (n > (card->tst_free_entries - NS_TST_RESERVED)) - { - PRINTK("nicstar%d: not enough free CBR bandwidth.\n", card->index); - vcc->flags &= ~(ATM_VF_ADDR); - return -EINVAL; - } - else - card->tst_free_entries -= n; + if (n > (card->tst_free_entries - NS_TST_RESERVED)) + { + PRINTK("nicstar%d: not enough free CBR bandwidth.\n", card->index); + vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL); + return -EINVAL; + } + else + card->tst_free_entries -= n; - XPRINTK("nicstar%d: writing %d tst entries.\n", card->index, n); - for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++) - { - if (card->scd2vc[frscdi] == NULL) - { - card->scd2vc[frscdi] = vc; - break; - } - } - if (frscdi == NS_FRSCD_NUM) - { - PRINTK("nicstar%d: no SCD available for CBR channel.\n", card->index); - card->tst_free_entries += n; - vcc->flags &= ~(ATM_VF_ADDR); - return -EBUSY; - } - } + XPRINTK("nicstar%d: writing %d tst entries.\n", card->index, n); + for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++) + { + if (card->scd2vc[frscdi] == NULL) + { + card->scd2vc[frscdi] = vc; + break; + } + } + if (frscdi == NS_FRSCD_NUM) + { + PRINTK("nicstar%d: no SCD available for CBR channel.\n", card->index); + card->tst_free_entries += n; + vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL); + return -EBUSY; + } - /* NOTE: You are not allowed to modify an open connection's QOS. To change - that, remove the ATM_VF_PARTIAL flag checking. There may be other changes - needed to do that. */ - if (!(vcc->flags & ATM_VF_PARTIAL)) - { - scq_info *scq; - - vcc->flags |= ATM_VF_PARTIAL; - if (vcc->qos.txtp.traffic_class == ATM_CBR) - { vc->cbr_scd = NS_FRSCD + frscdi * NS_FRSCD_SIZE; scq = get_scq(CBR_SCQSIZE, vc->cbr_scd); @@ -1429,13 +1466,13 @@ vc->scq = card->scq0; } - if (vcc->qos.txtp.traffic_class != ATM_NONE && !vc->tx) + if (vcc->qos.txtp.traffic_class != ATM_NONE) { vc->tx = 1; vc->tx_vcc = vcc; vc->tbd_count = 0; } - if (vcc->qos.rxtp.traffic_class != ATM_NONE && !vc->rx) + if (vcc->qos.rxtp.traffic_class != ATM_NONE) { u32 status; @@ -1467,7 +1504,6 @@ { vc_map *vc; ns_dev *card; - u32 u32d[4]; u32 data; int i; @@ -1546,6 +1582,7 @@ ns_scqe tsr; u32 scdi, scqi; u32 data; + int index; tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE); scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE; @@ -1554,6 +1591,8 @@ tsr.word_3 = 0x00000000; tsr.word_4 = 0x00000000; *scq->next = tsr; + index = (int) scqi; + scq->skb[index] = NULL; if (scq->next == scq->last) scq->next = scq->base; else @@ -1565,11 +1604,6 @@ restore_flags(flags); } - /* Re-initialize SCD to stop transmission */ - u32d[1] = ns_read_sram(card, vc->cbr_scd) & NS_SCD_BASE_MASK_FIX; - u32d[2] = 0x00000000; - ns_write_sram(card, vc->cbr_scd, u32d, 2); - /* Free all TST entries */ data = NS_TST_OPCODE_VARIABLE; for (i = 0; i < NS_TST_NUM_ENTRIES; i++) @@ -1736,7 +1770,7 @@ flags |= NS_TBD_EOPDU; scqe.word_4 = *((u32 *) skb->data) & ~NS_TBD_VC_MASK; /* Force the VPI/VCI to be the same as in VCC struct */ - scqe.word_4 |= (((u32) vcc->vpi) << NS_TBD_VPI_SHIFT & + scqe.word_4 |= (((u32) vcc->vpi) << NS_TBD_VPI_SHIFT | ((u32) vcc->vci) << NS_TBD_VCI_SHIFT) & NS_TBD_VC_MASK; } @@ -1810,16 +1844,6 @@ if (vc->tbd_count >= MAX_TBD_PER_VC || scq->tbd_count >= MAX_TBD_PER_SCQ) { - tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE); - if (scq_is_vbr) - scdi = NS_TSR_SCDISVBR; - else - scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE; - scqi = scq->next - scq->base; - tsr.word_2 = ns_tsr_mkword_2(scdi, scqi); - tsr.word_3 = 0x00000000; - tsr.word_4 = 0x00000000; - if (scq->tail == scq->next) { save_flags(flags); cli(); @@ -1831,8 +1855,18 @@ if (!scq->full) { + tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE); + if (scq_is_vbr) + scdi = NS_TSR_SCDISVBR; + else + scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE; + scqi = scq->next - scq->base; + tsr.word_2 = ns_tsr_mkword_2(scdi, scqi); + tsr.word_3 = 0x00000000; + tsr.word_4 = 0x00000000; + *scq->next = tsr; - index = (int) (scq->next - scq->base); + index = (int) scqi; scq->skb[index] = NULL; XPRINTK("nicstar%d: TSR written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%x.\n", card->index, tsr.word_1, tsr.word_2, tsr.word_3, tsr.word_4, @@ -1877,6 +1911,7 @@ { printk("nicstar%d: could not find VC from SCD index.\n", card->index); + ns_tsi_init(card->tsq.next); return; } scq = card->scd2vc[scdi]->scq; @@ -2023,11 +2058,11 @@ break; } /* Rebuild the header */ - skb_push(sb, NS_AAL0_HEADER); *((u32 *) sb->data) = 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(sb->tail, cell, ATM_CELL_PAYLOAD); skb_put(sb, ATM_CELL_PAYLOAD); sb->atm.vcc = vcc; @@ -2543,6 +2578,13 @@ if (!left--) return sprintf(page, "Iovec %5d %5d %5d %5d \n", card->iovpool.count, card->iovnr.min, card->iovnr.init, card->iovnr.max); + if (!left--) + { + int retval; + retval = sprintf(page, "Interrupt counter: %u \n", card->intcnt); + card->intcnt = 0; + return retval; + } /* Dump 25.6 Mbps PHY registers */ if (card->max_pcr == IDT_25_PCR && !left--) { @@ -2792,4 +2834,50 @@ "small" : skb->list == &card->lbpool.queue ? "large" : skb->list == &card->hbpool.queue ? "huge" : skb->list == &card->iovpool.queue ? "iovec" : "unknown"); +} + + + +static void ns_poll(unsigned long arg) +{ + int i; + ns_dev *card; + unsigned long flags; + u32 stat_r, stat_w; + + PRINTK("nicstar: Entering ns_poll().\n"); + for (i = 0; i < num_cards; i++) + { + card = cards[i]; + save_flags(flags); cli(); + if (card->in_poll) + { + printk("nicstar: Re-entering ns_poll()???\n"); + continue; + } + card->in_poll = 1; + if (card->in_handler) + { + card->in_poll = 0; + printk("nicstar%d: ns_poll called while in interrupt handler!?\n", + card->index); + continue; + } + + stat_w = 0; + stat_r = readl(card->membase + STAT); + if (stat_r & NS_STAT_TSIF) + stat_w |= NS_STAT_TSIF; + if (stat_r & NS_STAT_EOPDU) + stat_w |= NS_STAT_EOPDU; + + process_tsq(card); + process_rsq(card); + + writel(card->membase + STAT, stat_w); + card->in_poll = 0; + restore_flags(flags); + } + mod_timer(&ns_timer, jiffies + NS_POLL_PERIOD); + PRINTK("nicstar: Leaving ns_poll().\n"); } diff -ur --new-file old/linux/drivers/atm/nicstar.h new/linux/drivers/atm/nicstar.h --- old/linux/drivers/atm/nicstar.h Thu Sep 24 20:29:25 1998 +++ new/linux/drivers/atm/nicstar.h Thu Sep 24 20:30:08 1998 @@ -84,7 +84,9 @@ #define SCQFULL_TIMEOUT (5 * HZ) -#define PCR_TOLERANCE 1.0001 +#define NS_POLL_PERIOD (HZ) + +#define PCR_TOLERANCE (1.0001) @@ -732,6 +734,9 @@ u32 lg_addr; struct sk_buff *rcbuf; /* Current raw cell buffer */ u32 rawch; /* Raw cell queue head */ + unsigned intcnt; /* Interrupt counter */ + volatile int in_handler: 1; + volatile int in_poll: 1; } ns_dev; diff -ur --new-file old/linux/drivers/atm/zatm.c new/linux/drivers/atm/zatm.c --- old/linux/drivers/atm/zatm.c Thu Sep 24 20:29:25 1998 +++ new/linux/drivers/atm/zatm.c Thu Sep 24 20:30:08 1998 @@ -839,7 +839,8 @@ mb(); dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM | (skb->atm.vcc->qos.aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : - 0); + 0 | (skb->atm.atm_options & ATM_ATMOPT_CLP ? + uPD98401_CLPM_1 : uPD98401_CLPM_0)); EVENT("dsc (0x%lx)\n",(unsigned long) dsc,0); } else { @@ -859,7 +860,9 @@ /* @@@ should check alignment */ put = dsc+8; dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP | - (skb->atm.vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0); + (skb->atm.vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 | + (skb->atm.atm_options & ATM_ATMOPT_CLP ? uPD98401_CLPM_1 : + uPD98401_CLPM_0)); dsc[1] = 0; dsc[2] = skb->atm.iovcnt*uPD98401_TXBD_SIZE; dsc[3] = virt_to_bus(put); @@ -897,9 +900,11 @@ "txing\n",vcc->dev->number); return; } +#if 0 /* @@@ would fail on CLP */ if (*ZATM_PRV_DSC(skb) != (uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM | uPD98401_TXPD_AAL5)) printk("@#*$!!!! (%08x)\n", *ZATM_PRV_DSC(skb)); +#endif *ZATM_PRV_DSC(skb) = 0; /* mark as invalid */ zatm_vcc->txing--; if (vcc->pop) vcc->pop(vcc,skb); diff -ur --new-file old/linux/include/linux/atmdev.h new/linux/include/linux/atmdev.h --- old/linux/include/linux/atmdev.h Thu Sep 24 20:29:25 1998 +++ new/linux/include/linux/atmdev.h Thu Sep 24 20:30:08 1998 @@ -155,6 +155,8 @@ #define ATM_PHY_SIG_UNKNOWN 1 /* carrier/light status is unknown */ #define ATM_PHY_SIG_FOUND 2 /* carrier/light okay */ +#define ATM_ATMOPT_CLP 1 /* set CLP bit */ + struct atm_vcc { unsigned short flags; /* VCC flags (ATM_VF_*) */ diff -ur --new-file old/linux/include/linux/pkt_sched.h new/linux/include/linux/pkt_sched.h --- old/linux/include/linux/pkt_sched.h Thu Sep 24 20:29:25 1998 +++ new/linux/include/linux/pkt_sched.h Thu Sep 24 20:30:08 1998 @@ -281,7 +281,7 @@ TCA_ATM_FD, /* file/socket descriptor */ TCA_ATM_PTR, /* pointer to descriptor - later */ TCA_ATM_HDR, /* LL header */ - TCA_ATM_POLICE, /* policing - later */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ TCA_ATM_ADDR /* PVC address (for output only) */ }; diff -ur --new-file old/linux/include/linux/skbuff.h new/linux/include/linux/skbuff.h --- old/linux/include/linux/skbuff.h Thu Sep 24 20:29:25 1998 +++ new/linux/include/linux/skbuff.h Thu Sep 24 20:30:08 1998 @@ -110,6 +110,7 @@ struct { struct atm_vcc *vcc; /* ATM VCC */ int iovcnt; /* 0 for "normal" operation */ + unsigned long atm_options; /* ATM layer options */ #ifdef CONFIG_AREQUIPA int generation; /* generation number */ #endif diff -ur --new-file old/linux/net/Config.in new/linux/net/Config.in --- old/linux/net/Config.in Thu Sep 24 20:29:25 1998 +++ new/linux/net/Config.in Thu Sep 24 20:30:08 1998 @@ -32,6 +32,9 @@ # fi if [ "$CONFIG_INET" = "y" ]; then bool ' Classical IP over ATM' CONFIG_ATM_CLIP y + if [ "$CONFIG_ATM_CLIP" = "y" ]; then + bool ' Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP n + fi # bool ' Application REQUested IP over ATM' CONFIG_AREQUIPA y fi tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE y diff -ur --new-file old/linux/net/atm/clip.c new/linux/net/atm/clip.c --- old/linux/net/atm/clip.c Thu Sep 24 20:29:25 1998 +++ new/linux/net/atm/clip.c Thu Sep 24 20:30:09 1998 @@ -3,6 +3,7 @@ /* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ +#include #include #include #include /* for UINT_MAX */ @@ -233,7 +234,9 @@ static void clip_neigh_error(struct neighbour *neigh,struct sk_buff *skb) { +#ifndef CONFIG_ATM_CLIP_NO_ICMP icmp_send(skb,ICMP_DEST_UNREACH,ICMP_HOST_UNREACH,0); +#endif kfree_skb(skb); } @@ -371,6 +374,7 @@ } atomic_add(skb->truesize,&skb->atm.vcc->tx_inuse); skb->atm.iovcnt = 0; + skb->atm.atm_options = skb->atm.vcc->atm_options; entry->vccs->last_use = jiffies; DPRINTK("skb(%p)->atm.vcc(%p)->dev(%p)\n",skb,skb->atm.vcc, skb->atm.vcc->dev); diff -ur --new-file old/linux/net/atm/common.c new/linux/net/atm/common.c --- old/linux/net/atm/common.c Thu Sep 24 20:29:25 1998 +++ new/linux/net/atm/common.c Thu Sep 24 20:30:09 1998 @@ -464,6 +464,7 @@ if (!(vcc->flags & ATM_VF_READY)) return -EPIPE; } skb->atm.iovcnt = 0; + skb->atm.atm_options = vcc->atm_options; if (copy_from_user(skb_put(skb,size),buff,size)) { kfree_skb(skb); return -EFAULT; @@ -856,6 +857,12 @@ vcc->flags |= ATM_VF_HASQOS; return 0; } + case SO_SETCLP: + if (get_user(value,(unsigned long *) optval)) + return -EFAULT; + if (value) vcc->atm_options |= ATM_ATMOPT_CLP; + else vcc->atm_options &= ~ATM_ATMOPT_CLP; + return 0; default: if (level == SOL_SOCKET) return -EINVAL; break; @@ -886,6 +893,9 @@ if (!(vcc->flags & ATM_VF_HASQOS)) return -EINVAL; return copy_to_user(optval,&vcc->qos,sizeof(vcc->qos)) ? -EFAULT : 0; + case SO_SETCLP: + return put_user(vcc->atm_options & ATM_ATMOPT_CLP ? 1 : + 0,(unsigned long *) optval) ? -EFAULT : 0; default: if (level == SOL_SOCKET) return -EINVAL; break; diff -ur --new-file old/linux/net/atm/lec.c new/linux/net/atm/lec.c --- old/linux/net/atm/lec.c Thu Sep 24 20:29:25 1998 +++ new/linux/net/atm/lec.c Thu Sep 24 20:30:09 1998 @@ -204,6 +204,7 @@ } atomic_add(skb->truesize, &send_vcc->tx_inuse); skb->atm.iovcnt = 0; + skb->atm.atm_options = send_vcc->atm_options; send_vcc->dev->ops->send(send_vcc, skb); priv->stats.tx_packets++; } diff -ur --new-file old/linux/net/atm/misc.c new/linux/net/atm/misc.c --- old/linux/net/atm/misc.c Thu Sep 24 20:29:25 1998 +++ new/linux/net/atm/misc.c Thu Sep 24 20:30:09 1998 @@ -1,8 +1,10 @@ /* net/atm/misc.c - Various functions for use by ATM drivers */ -/* Written 1995-1997 by Werner Almesberger, EPFL ICA */ +/* Written 1995-1998 by Werner Almesberger, EPFL ICA */ +#include +#include #include #include #include diff -ur --new-file old/linux/net/atm/mpc.c new/linux/net/atm/mpc.c --- old/linux/net/atm/mpc.c Thu Sep 24 20:29:25 1998 +++ new/linux/net/atm/mpc.c Thu Sep 24 20:30:09 1998 @@ -492,6 +492,8 @@ } atomic_add(skb->truesize, &entry->shortcut->tx_inuse); + skb->atm.iovcnt = 0; /* just to be safe ... */ + skb->atm.atm_options = entry->shortcut->atm_options; entry->shortcut->dev->ops->send(entry->shortcut, skb); entry->packets_fwded++; diff -ur --new-file old/linux/net/sched/sch_atm.c new/linux/net/sched/sch_atm.c --- old/linux/net/sched/sch_atm.c Thu Sep 24 20:29:25 1998 +++ new/linux/net/sched/sch_atm.c Thu Sep 24 20:30:09 1998 @@ -3,11 +3,6 @@ /* Written 1998 by Werner Almesberger, EPFL ICA */ -/* - * NOTE: There are still essential parts (e.g. policing) missing. So don't - * depend too much on the existing interfaces ... - */ - #include #include #include @@ -21,7 +16,8 @@ extern struct socket *sockfd_lookup(int fd, int *err); /* @@@ fix this */ -#define sockfd_put(sock) fput((sock)->file) +#define sockfd_put(sock) fput((sock)->file) /* @@@ copied because it's + __inline__ in socket.c */ #if 1 /* control */ @@ -40,8 +36,8 @@ /* * The ATM queuing discipline provides a framework for invoking classifiers * (aka "filters"), which in turn select classes of this queuing discipline. - * Each class maps the flow(s) it is handling to a given VC. More than one - * class may use the same VC. + * Each class maps the flow(s) it is handling to a given VC. Multiple classes + * may share the same VC. * * When creating a class, VCs are specified by passing the number of the open * socket descriptor by which the calling process references the VC. The kernel @@ -53,11 +49,9 @@ * * Known bugs: * - sometimes messes up the IP stack - * - no policing yet - * - may panic if there's not enough space in an skb to add the CLIP header * - any manipulations besides the few operations described in the README, are * untested and likely to crash the system - * - takes a few dirty shortcuts on rtnetlink + * - should lock the flow while there is data in the queue (?) */ @@ -69,12 +63,14 @@ struct tcf_proto *filter_list; struct atm_vcc *vcc; /* VCC; NULL if VCC is closed */ struct socket *sock; /* for closing */ - unsigned long classid; /* x:y type ID */ + u32 classid; /* x:y type ID */ int ref; /* reference count */ struct tc_stats stats; struct atm_flow_data *next; + struct atm_flow_data *excess; /* flow for excess traffic; + NULL to set CLP instead */ int hdr_len; - unsigned char hdr[0]; /* header data */ + unsigned char hdr[0]; /* header data; MUST BE LAST */ }; struct atm_qdisc_data { @@ -119,8 +115,7 @@ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; - DPRINTK("atm_tc_get(sch %p,[qdisc %p],classid %p)\n",sch,p, - (void *) classid); + DPRINTK("atm_tc_get(sch %p,[qdisc %p],classid %x)\n",sch,p,classid); for (flow = PRIV(sch)->flows; flow; flow = flow->next) if (flow->classid == classid) break; if (flow) flow->ref++; @@ -148,7 +143,7 @@ for (prev = &p->flows; *prev; prev = &(*prev)->next) if (*prev == flow) break; if (!*prev) { - printk(KERN_ERR "atm_tc_put: class %p not found\n",flow); + printk(KERN_CRIT "atm_tc_put: class %p not found\n",flow); return; } *prev = flow->next; @@ -169,6 +164,7 @@ * If flow == &p->link, the qdisc no longer works at this point and * needs to be removed. (By the caller of atm_tc_put.) */ + if (flow->excess) atm_tc_put(sch,(unsigned long) flow->excess); } @@ -177,6 +173,7 @@ { struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow = (struct atm_flow_data *) *arg; + struct atm_flow_data *excess; struct rtattr *opt = tca[TCA_OPTIONS-1]; struct rtattr *tb[TCA_ATM_MAX]; struct socket *sock; @@ -193,7 +190,8 @@ * ATM classes cannot be changed. In order to change properties of the * ATM connection, that socket needs to be modified directly (via the * native ATM API. In order to send a flow to a different VC, the old - * class needs to be removed and a new one added. + * class needs to be removed and a new one added. (This may be changed + * later.) */ if (flow) return -EBUSY; if (opt == NULL || rtattr_parse(tb,TCA_ATM_MAX,RTA_DATA(opt), @@ -210,6 +208,14 @@ hdr_len = RFC1483LLC_LEN; hdr = NULL; /* default LLC/SNAP for IP */ } + if (!tb[TCA_ATM_EXCESS-1]) excess = NULL; + else { + if (RTA_PAYLOAD(tb[TCA_ATM_EXCESS-1]) != sizeof(u32)) + return -EINVAL; + excess = (struct atm_flow_data *) atm_tc_get(sch, + *(u32 *) RTA_DATA(tb[TCA_ATM_EXCESS-1])); + if (!excess) return -ENOENT; + } DPRINTK("atm_tc_change: type %d, payload %d, hdr_len %d\n", opt->rta_type,RTA_PAYLOAD(opt),hdr_len); if (!(sock = sockfd_lookup(fd,&error))) return error; /* f_count++ */ @@ -258,6 +264,7 @@ DPRINTK("atm_tc_change: vcc %p\n",flow->vcc); flow->classid = classid; flow->ref = 1; + flow->excess = excess; flow->next = p->link.next; p->link.next = flow; flow->hdr_len = hdr_len; @@ -282,6 +289,7 @@ DPRINTK("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n",sch,p,flow); if (!find_flow(PRIV(sch),flow)) return -EINVAL; if (flow->filter_list || flow == &p->link) return -EBUSY; + if (flow->ref > 1) return -EBUSY; /* catch references via excess, etc.*/ atm_tc_put(sch,arg); return 0; } @@ -323,13 +331,45 @@ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; struct tcf_result res; + int result; D2PRINTK("atm_tc_enqueue(skb %p,sch %p,[qdisc %p])\n",skb,sch,p); - for (flow = p->link.next; flow; flow = flow->next) - if (flow->filter_list && tc_classify(skb,flow->filter_list, - &res) >= 0) break; + result = TC_POLICE_OK; /* be nice to gcc */ + if (TC_H_MAJ(skb->priority) != sch->handle || + !(flow = (struct atm_flow_data *) atm_tc_get(sch,skb->priority))) + for (flow = p->link.next; flow; flow = flow->next) + if (flow->filter_list) { + result = tc_classify(skb,flow->filter_list, + &res); + if (result >= 0) break; + } if (!flow) flow = &p->link; - if (flow->q->enqueue(skb,flow->q) != 1) { + else { + if (flow->vcc) skb->atm.atm_options = flow->vcc->atm_options; +#ifdef CONFIG_NET_CLS_POLICE + switch (result) { + case TC_POLICE_SHOT: + kfree_skb(skb); + break; + case TC_POLICE_RECLASSIFY: + if (flow->excess) flow = flow->excess; + else { + skb->atm.atm_options |= ATM_ATMOPT_CLP; + break; + } + /* fall through */ + case TC_POLICE_OK: + /* fall through */ + default: + break; + } + } +#endif + if ( +#ifdef CONFIG_NET_CLS_POLICE + result == TC_POLICE_SHOT || +#endif + flow->q->enqueue(skb,flow->q) != 1) { sch->stats.drops++; if (flow) flow->stats.drops++; return 0; @@ -375,6 +415,7 @@ flow->hdr_len); atomic_add(skb->truesize,&flow->vcc->tx_inuse); skb->atm.iovcnt = 0; + /* atm.atm_options are already set by atm_tc_enqueue */ (void) flow->vcc->dev->ops->send(flow->vcc,skb); } skb = p->link.q->dequeue(p->link.q); @@ -480,6 +521,13 @@ pvc.sap_addr.vci = flow->vcc->vci; RTA_PUT(skb,TCA_ATM_ADDR,sizeof(pvc),&pvc); } + if (flow->excess) + RTA_PUT(skb,TCA_ATM_EXCESS,sizeof(u32),&flow->classid); + else { + static u32 zero = 0; + + RTA_PUT(skb,TCA_ATM_EXCESS,sizeof(zero),&zero); + } rta->rta_len = skb->tail-b; return skb->len; @@ -523,7 +571,7 @@ atm_tc_enqueue, /* enqueue */ atm_tc_dequeue, /* dequeue */ - atm_tc_enqueue, /* requeue */ + atm_tc_enqueue, /* requeue; we're cheating a little */ atm_tc_drop, /* drop */ atm_tc_init, /* init */ .