Newer
Older
seq_printf(seq, " %3d %3d", entry->recv_vcc->vpi,
entry->recv_vcc->vci);
}
seq_putc(seq, '\n');
}
struct lec_state {
unsigned long flags;
struct lec_priv *locked;
struct net_device *dev;
int itf;
int arp_table;
int misc_table;
};
static void *lec_tbl_walk(struct lec_state *state, struct hlist_head *tbl,
struct hlist_node *e = state->node;
struct lec_arp_table *tmp;
hlist_for_each_entry_from(tmp, e, next) {
return (*l < 0) ? state : NULL;
}
static void *lec_arp_walk(struct lec_state *state, loff_t *l,
struct lec_priv *priv)
{
void *v = NULL;
int p;
for (p = state->arp_table; p < LEC_ARP_TABLE_SIZE; p++) {
v = lec_tbl_walk(state, &priv->lec_arp_tables[p], l);
if (v)
break;
}
state->arp_table = p;
return v;
}
static void *lec_misc_walk(struct lec_state *state, loff_t *l,
struct lec_priv *priv)
{
struct hlist_head *lec_misc_tables[] = {
&priv->lec_arp_empty_ones,
&priv->lec_no_forward,
&priv->mcast_fwds
};
void *v = NULL;
int q;
for (q = state->misc_table; q < ARRAY_SIZE(lec_misc_tables); q++) {
v = lec_tbl_walk(state, lec_misc_tables[q], l);
if (v)
break;
}
state->misc_table = q;
return v;
}
static void *lec_priv_walk(struct lec_state *state, loff_t *l,
struct lec_priv *priv)
{
if (!state->locked) {
state->locked = priv;
spin_lock_irqsave(&priv->lec_arp_lock, state->flags);
}
if (!lec_arp_walk(state, l, priv) && !lec_misc_walk(state, l, priv)) {
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
spin_unlock_irqrestore(&priv->lec_arp_lock, state->flags);
state->locked = NULL;
/* Partial state reset for the next time we get called */
state->arp_table = state->misc_table = 0;
}
return state->locked;
}
static void *lec_itf_walk(struct lec_state *state, loff_t *l)
{
struct net_device *dev;
void *v;
dev = state->dev ? state->dev : dev_lec[state->itf];
v = (dev && dev->priv) ? lec_priv_walk(state, l, dev->priv) : NULL;
if (!v && dev) {
dev_put(dev);
/* Partial state reset for the next time we get called */
dev = NULL;
}
state->dev = dev;
return v;
}
static void *lec_get_idx(struct lec_state *state, loff_t l)
{
void *v = NULL;
for (; state->itf < MAX_LEC_ITF; state->itf++) {
v = lec_itf_walk(state, &l);
if (v)
break;
}
}
static void *lec_seq_start(struct seq_file *seq, loff_t *pos)
{
struct lec_state *state = seq->private;
state->itf = 0;
state->dev = NULL;
state->locked = NULL;
state->arp_table = 0;
state->misc_table = 0;
return *pos ? lec_get_idx(state, *pos) : SEQ_START_TOKEN;
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
}
static void lec_seq_stop(struct seq_file *seq, void *v)
{
struct lec_state *state = seq->private;
if (state->dev) {
spin_unlock_irqrestore(&state->locked->lec_arp_lock,
state->flags);
dev_put(state->dev);
}
}
static void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct lec_state *state = seq->private;
v = lec_get_idx(state, 1);
*pos += !!PTR_ERR(v);
return v;
}
static int lec_seq_show(struct seq_file *seq, void *v)
{
static char lec_banner[] = "Itf MAC ATM destination"
" Status Flags "
"VPI/VCI Recv VPI/VCI\n";
seq_puts(seq, lec_banner);
else {
struct lec_state *state = seq->private;
struct net_device *dev = state->dev;
struct lec_arp_table *entry = hlist_entry(state->node, struct lec_arp_table, next);
static const struct seq_operations lec_seq_ops = {
.start = lec_seq_start,
.next = lec_seq_next,
.stop = lec_seq_stop,
.show = lec_seq_show,
};
static int lec_seq_open(struct inode *inode, struct file *file)
{
return seq_open_private(file, &lec_seq_ops, sizeof(struct lec_state));
static const struct file_operations lec_seq_fops = {
.owner = THIS_MODULE,
.open = lec_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#endif
static int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct atm_vcc *vcc = ATM_SD(sock);
int err = 0;
case ATMLEC_CTRL:
case ATMLEC_MCAST:
case ATMLEC_DATA:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
break;
default:
return -ENOIOCTLCMD;
case ATMLEC_CTRL:
err = lecd_attach(vcc, (int)arg);
if (err >= 0)
sock->state = SS_CONNECTED;
break;
case ATMLEC_MCAST:
err = lec_mcast_attach(vcc, (int)arg);
break;
case ATMLEC_DATA:
err = lec_vcc_attach(vcc, (void __user *)arg);
break;
}
return err;
}
static struct atm_ioctl lane_ioctl_ops = {
.owner = THIS_MODULE,
.ioctl = lane_ioctl,
};
static int __init lane_module_init(void)
{
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *p;
p = proc_create("lec", S_IRUGO, atm_proc_root, &lec_seq_fops);

Wang Chen
committed
if (!p) {
printk(KERN_ERR "Unable to initialize /proc/net/atm/lec\n");
return -ENOMEM;
}
#endif
register_atm_ioctl(&lane_ioctl_ops);
printk("lec.c: " __DATE__ " " __TIME__ " initialized\n");
return 0;
}
static void __exit lane_module_cleanup(void)
{
int i;
struct lec_priv *priv;
remove_proc_entry("lec", atm_proc_root);
deregister_atm_ioctl(&lane_ioctl_ops);
for (i = 0; i < MAX_LEC_ITF; i++) {
if (dev_lec[i] != NULL) {
priv = (struct lec_priv *)dev_lec[i]->priv;
free_netdev(dev_lec[i]);
dev_lec[i] = NULL;
}
}
}
module_init(lane_module_init);
module_exit(lane_module_cleanup);
/*
* LANE2: 3.1.3, LE_RESOLVE.request
* Non force allocates memory and fills in *tlvs, fills in *sizeoftlvs.
* If sizeoftlvs == NULL the default TLVs associated with with this
* lec will be used.
* If dst_mac == NULL, targetless LE_ARP will be sent
*/
static int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force,
u8 **tlvs, u32 *sizeoftlvs)
struct lec_priv *priv = (struct lec_priv *)dev->priv;
struct lec_arp_table *table;
struct sk_buff *skb;
int retval;
table = lec_arp_find(priv, dst_mac);
if (table == NULL)
return -1;
*tlvs = kmemdup(table->tlvs, table->sizeoftlvs, GFP_ATOMIC);
if (*tlvs == NULL)
return -1;
*sizeoftlvs = table->sizeoftlvs;
return 0;
}
if (sizeoftlvs == NULL)
retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, NULL);
else {
skb = alloc_skb(*sizeoftlvs, GFP_ATOMIC);
if (skb == NULL)
return -1;
skb->len = *sizeoftlvs;
skb_copy_to_linear_data(skb, *tlvs, *sizeoftlvs);
retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb);
}
return retval;
}
/*
* LANE2: 3.1.4, LE_ASSOCIATE.request
* Associate the *tlvs with the *lan_dst address.
* Will overwrite any previous association
* Returns 1 for success, 0 for failure (out of memory)
*
*/
static int lane2_associate_req(struct net_device *dev, const u8 *lan_dst,
const u8 *tlvs, u32 sizeoftlvs)
int retval;
struct sk_buff *skb;
struct lec_priv *priv = (struct lec_priv *)dev->priv;
if (compare_ether_addr(lan_dst, dev->dev_addr))
return (0); /* not our mac address */
kfree(priv->tlvs); /* NULL if there was no previous association */
priv->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL);
if (priv->tlvs == NULL)
return (0);
priv->sizeoftlvs = sizeoftlvs;
skb = alloc_skb(sizeoftlvs, GFP_ATOMIC);
if (skb == NULL)
return 0;
skb->len = sizeoftlvs;
skb_copy_to_linear_data(skb, tlvs, sizeoftlvs);
retval = send_to_lecd(priv, l_associate_req, NULL, NULL, skb);
if (retval != 0)
printk("lec.c: lane2_associate_req() failed\n");
/*
* If the previous association has changed we must
* somehow notify other LANE entities about the change
*/
return (1);
}
/*
* LANE2: 3.1.5, LE_ASSOCIATE.indication
*
*/
static void lane2_associate_ind(struct net_device *dev, const u8 *mac_addr,
const u8 *tlvs, u32 sizeoftlvs)
#endif
struct lec_priv *priv = (struct lec_priv *)dev->priv;
#if 0 /*
* Why have the TLVs in LE_ARP entries
* since we do not use them? When you
* uncomment this code, make sure the
* TLVs get freed when entry is killed
*/
struct lec_arp_table *entry = lec_arp_find(priv, mac_addr);
if (entry == NULL)
return; /* should not happen */
kfree(entry->tlvs);
entry->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL);
if (entry->tlvs == NULL)
return;
entry->sizeoftlvs = sizeoftlvs;
printk("lec.c: lane2_associate_ind()\n");
printk("dump of tlvs, sizeoftlvs=%d\n", sizeoftlvs);
while (i < sizeoftlvs)
printk("%02x ", tlvs[i++]);
printk("\n");
/* tell MPOA about the TLVs we saw */
if (priv->lane2_ops && priv->lane2_ops->associate_indicator) {
priv->lane2_ops->associate_indicator(dev, mac_addr,
tlvs, sizeoftlvs);
}
return;
}
/*
* Here starts what used to lec_arpc.c
*
* lec_arpc.c was added here when making
* lane client modular. October 1997
*/
#include <linux/types.h>
#include <linux/timer.h>
#include <asm/param.h>
#include <asm/atomic.h>
#include <linux/inetdevice.h>
#include <net/route.h>
#if 0
#define pr_debug(format,args...)
*/
#endif
#define DEBUG_ARP_TABLE 0
#define LEC_ARP_REFRESH_INTERVAL (3*HZ)
static void lec_arp_check_expire(struct work_struct *work);
static void lec_arp_expire_arp(unsigned long data);
* Arp table funcs
*/
#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE -1))
/*
* Initialization of arp-cache
*/
static void lec_arp_init(struct lec_priv *priv)
unsigned short i;
for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
INIT_HLIST_HEAD(&priv->lec_arp_tables[i]);
INIT_HLIST_HEAD(&priv->lec_arp_empty_ones);
INIT_HLIST_HEAD(&priv->lec_no_forward);
INIT_HLIST_HEAD(&priv->mcast_fwds);
INIT_DELAYED_WORK(&priv->lec_arp_work, lec_arp_check_expire);
schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL);
static void lec_arp_clear_vccs(struct lec_arp_table *entry)
if (entry->vcc) {
struct atm_vcc *vcc = entry->vcc;
struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc);
struct net_device *dev = (struct net_device *)vcc->proto_data;
vcc->pop = vpriv->old_pop;
if (vpriv->xoff)
netif_wake_queue(dev);
kfree(vpriv);
vcc->user_back = NULL;
vcc->push = entry->old_push;
}
if (entry->recv_vcc) {
entry->recv_vcc->push = entry->old_recv_push;
entry->recv_vcc = NULL;
}
}
/*
* Insert entry to lec_arp_table
* LANE2: Add to the end of the list to satisfy 8.1.13
*/
static inline void
lec_arp_add(struct lec_priv *priv, struct lec_arp_table *entry)
tmp = &priv->lec_arp_tables[HASH(entry->mac_addr[ETH_ALEN - 1])];
hlist_add_head(&entry->next, tmp);
pr_debug("LEC_ARP: Added entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
0xff & entry->mac_addr[0], 0xff & entry->mac_addr[1],
0xff & entry->mac_addr[2], 0xff & entry->mac_addr[3],
0xff & entry->mac_addr[4], 0xff & entry->mac_addr[5]);
}
/*
* Remove entry from lec_arp_table
*/
static int
lec_arp_remove(struct lec_priv *priv, struct lec_arp_table *to_remove)
struct hlist_node *node;
struct lec_arp_table *entry;
int i, remove_vcc = 1;
if (!to_remove) {
return -1;
}
hlist_del(&to_remove->next);
del_timer(&to_remove->timer);
/* If this is the only MAC connected to this VCC, also tear down the VCC */
if (to_remove->status >= ESI_FLUSH_PENDING) {
/*
* ESI_FLUSH_PENDING, ESI_FORWARD_DIRECT
*/
for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
hlist_for_each_entry(entry, node, &priv->lec_arp_tables[i], next) {
if (memcmp(to_remove->atm_addr,
entry->atm_addr, ATM_ESA_LEN) == 0) {
remove_vcc = 0;
break;
}
}
}
if (remove_vcc)
lec_arp_clear_vccs(to_remove);
}
skb_queue_purge(&to_remove->tx_wait); /* FIXME: good place for this? */
pr_debug("LEC_ARP: Removed entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
0xff & to_remove->mac_addr[0], 0xff & to_remove->mac_addr[1],
0xff & to_remove->mac_addr[2], 0xff & to_remove->mac_addr[3],
0xff & to_remove->mac_addr[4], 0xff & to_remove->mac_addr[5]);
return 0;
static char *get_status_string(unsigned char st)
switch (st) {
case ESI_UNKNOWN:
return "ESI_UNKNOWN";
case ESI_ARP_PENDING:
return "ESI_ARP_PENDING";
case ESI_VC_PENDING:
return "ESI_VC_PENDING";
case ESI_FLUSH_PENDING:
return "ESI_FLUSH_PENDING";
case ESI_FORWARD_DIRECT:
return "ESI_FORWARD_DIRECT";
default:
return "<UNKNOWN>";
}
static void dump_arp_table(struct lec_priv *priv)
struct lec_arp_table *rulla;
char buf[256];
int i, j, offset;
printk("Dump %p:\n", priv);
for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
hlist_for_each_entry(rulla, node, &priv->lec_arp_tables[i], next) {
offset = 0;
offset += sprintf(buf, "%d: %p\n", i, rulla);
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
offset += sprintf(buf + offset, "Mac:");
for (j = 0; j < ETH_ALEN; j++) {
offset += sprintf(buf + offset,
"%2.2x ",
rulla->mac_addr[j] & 0xff);
}
offset += sprintf(buf + offset, "Atm:");
for (j = 0; j < ATM_ESA_LEN; j++) {
offset += sprintf(buf + offset,
"%2.2x ",
rulla->atm_addr[j] & 0xff);
}
offset += sprintf(buf + offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc ? rulla->vcc->vpi : 0,
rulla->vcc ? rulla->vcc->vci : 0,
rulla->recv_vcc ? rulla->recv_vcc->
vpi : 0,
rulla->recv_vcc ? rulla->recv_vcc->
vci : 0, rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset +=
sprintf(buf + offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
}
}
if (!hlist_empty(&priv->lec_no_forward))
printk("No forward\n");
hlist_for_each_entry(rulla, node, &priv->lec_no_forward, next) {
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
offset = 0;
offset += sprintf(buf + offset, "Mac:");
for (j = 0; j < ETH_ALEN; j++) {
offset += sprintf(buf + offset, "%2.2x ",
rulla->mac_addr[j] & 0xff);
}
offset += sprintf(buf + offset, "Atm:");
for (j = 0; j < ATM_ESA_LEN; j++) {
offset += sprintf(buf + offset, "%2.2x ",
rulla->atm_addr[j] & 0xff);
}
offset += sprintf(buf + offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc ? rulla->vcc->vpi : 0,
rulla->vcc ? rulla->vcc->vci : 0,
rulla->recv_vcc ? rulla->recv_vcc->vpi : 0,
rulla->recv_vcc ? rulla->recv_vcc->vci : 0,
rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset += sprintf(buf + offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
if (!hlist_empty(&priv->lec_arp_empty_ones))
printk("Empty ones\n");
hlist_for_each_entry(rulla, node, &priv->lec_arp_empty_ones, next) {
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
offset = 0;
offset += sprintf(buf + offset, "Mac:");
for (j = 0; j < ETH_ALEN; j++) {
offset += sprintf(buf + offset, "%2.2x ",
rulla->mac_addr[j] & 0xff);
}
offset += sprintf(buf + offset, "Atm:");
for (j = 0; j < ATM_ESA_LEN; j++) {
offset += sprintf(buf + offset, "%2.2x ",
rulla->atm_addr[j] & 0xff);
}
offset += sprintf(buf + offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc ? rulla->vcc->vpi : 0,
rulla->vcc ? rulla->vcc->vci : 0,
rulla->recv_vcc ? rulla->recv_vcc->vpi : 0,
rulla->recv_vcc ? rulla->recv_vcc->vci : 0,
rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset += sprintf(buf + offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
printk("%s", buf);
}
if (!hlist_empty(&priv->mcast_fwds))
printk("Multicast Forward VCCs\n");
hlist_for_each_entry(rulla, node, &priv->mcast_fwds, next) {
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
offset = 0;
offset += sprintf(buf + offset, "Mac:");
for (j = 0; j < ETH_ALEN; j++) {
offset += sprintf(buf + offset, "%2.2x ",
rulla->mac_addr[j] & 0xff);
}
offset += sprintf(buf + offset, "Atm:");
for (j = 0; j < ATM_ESA_LEN; j++) {
offset += sprintf(buf + offset, "%2.2x ",
rulla->atm_addr[j] & 0xff);
}
offset += sprintf(buf + offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc ? rulla->vcc->vpi : 0,
rulla->vcc ? rulla->vcc->vci : 0,
rulla->recv_vcc ? rulla->recv_vcc->vpi : 0,
rulla->recv_vcc ? rulla->recv_vcc->vci : 0,
rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset += sprintf(buf + offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
#else
#define dump_arp_table(priv) do { } while (0)
#endif
static void lec_arp_destroy(struct lec_priv *priv)
struct hlist_node *node, *next;
struct lec_arp_table *entry;
int i;
cancel_rearming_delayed_work(&priv->lec_arp_work);
/*
* Remove all entries
*/
for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
hlist_for_each_entry_safe(entry, node, next, &priv->lec_arp_tables[i], next) {
lec_arp_remove(priv, entry);
lec_arp_put(entry);
INIT_HLIST_HEAD(&priv->lec_arp_tables[i]);
hlist_for_each_entry_safe(entry, node, next, &priv->lec_arp_empty_ones, next) {
del_timer_sync(&entry->timer);
lec_arp_clear_vccs(entry);
lec_arp_put(entry);
INIT_HLIST_HEAD(&priv->lec_arp_empty_ones);
hlist_for_each_entry_safe(entry, node, next, &priv->lec_no_forward, next) {
del_timer_sync(&entry->timer);
lec_arp_clear_vccs(entry);
lec_arp_put(entry);
INIT_HLIST_HEAD(&priv->lec_no_forward);
hlist_for_each_entry_safe(entry, node, next, &priv->mcast_fwds, next) {
/* No timer, LANEv2 7.1.20 and 2.3.5.3 */
lec_arp_clear_vccs(entry);
lec_arp_put(entry);
INIT_HLIST_HEAD(&priv->mcast_fwds);
priv->mcast_vcc = NULL;
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
}
static struct lec_arp_table *lec_arp_find(struct lec_priv *priv,
struct hlist_node *node;
struct hlist_head *head;
struct lec_arp_table *entry;
pr_debug("LEC_ARP: lec_arp_find :%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
mac_addr[0] & 0xff, mac_addr[1] & 0xff, mac_addr[2] & 0xff,
mac_addr[3] & 0xff, mac_addr[4] & 0xff, mac_addr[5] & 0xff);
head = &priv->lec_arp_tables[HASH(mac_addr[ETH_ALEN - 1])];
hlist_for_each_entry(entry, node, head, next) {
if (!compare_ether_addr(mac_addr, entry->mac_addr)) {
return entry;
}
}
return NULL;
static struct lec_arp_table *make_entry(struct lec_priv *priv,
struct lec_arp_table *to_return;
to_return = kzalloc(sizeof(struct lec_arp_table), GFP_ATOMIC);
if (!to_return) {
printk("LEC: Arp entry kmalloc failed\n");
return NULL;
}
memcpy(to_return->mac_addr, mac_addr, ETH_ALEN);
INIT_HLIST_NODE(&to_return->next);
setup_timer(&to_return->timer, lec_arp_expire_arp,
(unsigned long)to_return);
to_return->last_used = jiffies;
to_return->priv = priv;
skb_queue_head_init(&to_return->tx_wait);
atomic_set(&to_return->usage, 1);
return to_return;
/* Arp sent timer expired */
static void lec_arp_expire_arp(unsigned long data)
struct lec_arp_table *entry;
entry = (struct lec_arp_table *)data;
pr_debug("lec_arp_expire_arp\n");
if (entry->status == ESI_ARP_PENDING) {
if (entry->no_tries <= entry->priv->max_retry_count) {
if (entry->is_rdesc)
send_to_lecd(entry->priv, l_rdesc_arp_xmt,
entry->mac_addr, NULL, NULL);
else
send_to_lecd(entry->priv, l_arp_xmt,
entry->mac_addr, NULL, NULL);
entry->no_tries++;
}
mod_timer(&entry->timer, jiffies + (1 * HZ));
}
/* Unknown/unused vcc expire, remove associated entry */
static void lec_arp_expire_vcc(unsigned long data)
struct lec_arp_table *to_remove = (struct lec_arp_table *)data;
struct lec_priv *priv = (struct lec_priv *)to_remove->priv;
del_timer(&to_remove->timer);
pr_debug("LEC_ARP %p %p: lec_arp_expire_vcc vpi:%d vci:%d\n",
to_remove, priv,
to_remove->vcc ? to_remove->recv_vcc->vpi : 0,
to_remove->vcc ? to_remove->recv_vcc->vci : 0);
hlist_del(&to_remove->next);
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
lec_arp_clear_vccs(to_remove);
lec_arp_put(to_remove);
}
/*
* Expire entries.
* 1. Re-set timer
* 2. For each entry, delete entries that have aged past the age limit.
* 3. For each entry, depending on the status of the entry, perform
* the following maintenance.
* a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the
* tick_count is above the max_unknown_frame_time, clear
* the tick_count to zero and clear the packets_flooded counter
* to zero. This supports the packet rate limit per address
* while flooding unknowns.
* b. If the status is ESI_FLUSH_PENDING and the tick_count is greater
* than or equal to the path_switching_delay, change the status
* to ESI_FORWARD_DIRECT. This causes the flush period to end
* regardless of the progress of the flush protocol.
*/
static void lec_arp_check_expire(struct work_struct *work)
struct lec_priv *priv =
container_of(work, struct lec_priv, lec_arp_work.work);
struct hlist_node *node, *next;
struct lec_arp_table *entry;
unsigned long now;
unsigned long time_to_check;
int i;
pr_debug("lec_arp_check_expire %p\n", priv);
restart:
for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
hlist_for_each_entry_safe(entry, node, next, &priv->lec_arp_tables[i], next) {
if ((entry->flags) & LEC_REMOTE_FLAG &&
priv->topology_change)
time_to_check = priv->forward_delay_time;
else
time_to_check = priv->aging_time;
pr_debug("About to expire: %lx - %lx > %lx\n",
now, entry->last_used, time_to_check);
if (time_after(now, entry->last_used + time_to_check)
&& !(entry->flags & LEC_PERMANENT_FLAG)
&& !(entry->mac_addr[0] & 0x01)) { /* LANE2: 7.1.20 */
pr_debug("LEC:Entry timed out\n");
lec_arp_put(entry);
} else {
/* Something else */
if ((entry->status == ESI_VC_PENDING ||
entry->status == ESI_ARP_PENDING)
entry->timestamp +
priv->
max_unknown_frame_time)) {
entry->timestamp = jiffies;
entry->packets_flooded = 0;
if (entry->status == ESI_VC_PENDING)
send_to_lecd(priv, l_svc_setup,
entry->mac_addr,
entry->atm_addr,
NULL);
if (entry->status == ESI_FLUSH_PENDING
&&
time_after_eq(now, entry->timestamp +
priv->path_switching_delay)) {
struct atm_vcc *vcc = entry->vcc;
lec_arp_hold(entry);
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
while ((skb = skb_dequeue(&entry->tx_wait)) != NULL)
lec_send(vcc, skb, entry->priv);
entry->status = ESI_FORWARD_DIRECT;
lec_arp_put(entry);
goto restart;
}
}
}
}
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL);
/*
* Try to find vcc where mac_address is attached.
static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv,
const unsigned char *mac_to_find, int is_rdesc,
struct lec_arp_table **ret_entry)
struct lec_arp_table *entry;
if (mac_to_find[0] & 0x01) {
switch (priv->lane_version) {
case 1:
return priv->mcast_vcc;
break;
case 2: /* LANE2 wants arp for multicast addresses */
if (!compare_ether_addr(mac_to_find, bus_mac))
return priv->mcast_vcc;
break;
default:
break;
}
}
entry = lec_arp_find(priv, mac_to_find);
if (entry) {
if (entry->status == ESI_FORWARD_DIRECT) {
/* Connection Ok */
entry->last_used = jiffies;
lec_arp_hold(entry);
*ret_entry = entry;
found = entry->vcc;
}
/*
* If the LE_ARP cache entry is still pending, reset count to 0
* so another LE_ARP request can be made for this frame.
*/
if (entry->status == ESI_ARP_PENDING) {
entry->no_tries = 0;
}
/*
* Data direct VC not yet set up, check to see if the unknown
* frame count is greater than the limit. If the limit has
* not been reached, allow the caller to send packet to
* BUS.
*/
if (entry->status != ESI_FLUSH_PENDING &&
entry->packets_flooded <
priv->maximum_unknown_frame_count) {
entry->packets_flooded++;
pr_debug("LEC_ARP: Flooding..\n");
found = priv->mcast_vcc;
}
/*
* We got here because entry->status == ESI_FLUSH_PENDING
* or BUS flood limit was reached for an entry which is
* in ESI_ARP_PENDING or ESI_VC_PENDING state.
*/
lec_arp_hold(entry);
*ret_entry = entry;
pr_debug("lec: entry->status %d entry->vcc %p\n", entry->status,
entry->vcc);
found = NULL;
} else {
/* No matching entry was found */
entry = make_entry(priv, mac_to_find);
pr_debug("LEC_ARP: Making entry\n");
if (!entry) {
found = priv->mcast_vcc;
}
lec_arp_add(priv, entry);
/* We want arp-request(s) to be sent */
entry->packets_flooded = 1;
entry->status = ESI_ARP_PENDING;
entry->no_tries = 1;
entry->last_used = entry->timestamp = jiffies;