Newer
Older
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)) {
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 && netdev_priv(dev)) ?
lec_priv_walk(state, l, netdev_priv(dev)) : 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;
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
}
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 const 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) {
pr_err("Unable to initialize /proc/net/atm/lec\n");

Wang Chen
committed
return -ENOMEM;
}
#endif
register_atm_ioctl(&lane_ioctl_ops);
pr_info("lec.c: " __DATE__ " " __TIME__ " initialized\n");
}
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 = netdev_priv(dev_lec[i]);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
if (compare_ether_addr(lan_dst, dev->dev_addr))
kfree(priv->tlvs); /* NULL if there was no previous association */
priv->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL);
if (priv->tlvs == NULL)
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)
pr_info("lec.c: lane2_associate_req() failed\n");
/*
* If the previous association has changed we must
* somehow notify other LANE entities about the change
*/
}
/*
* 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)
struct lec_priv *priv = netdev_priv(dev);
#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;
pr_info("\n");
pr_info("dump of tlvs, sizeoftlvs=%d\n", sizeoftlvs);
while (i < sizeoftlvs)
/* 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/atomic.h>
#include <linux/inetdevice.h>
#include <net/route.h>
#if 0
*/
#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);
#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE - 1))
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("Added entry:%pM\n", entry->mac_addr);
}
/*
* 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;
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("Removed entry:%pM\n", to_remove->mac_addr);
return 0;
static const 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";
}
static void dump_arp_table(struct lec_priv *priv)
struct lec_arp_table *rulla;
char buf[256];
int i, j, offset;
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);
offset += sprintf(buf + offset, "Mac: %pM",
rulla->mac_addr);
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))
hlist_for_each_entry(rulla, node, &priv->lec_no_forward, next) {
offset = 0;
offset += sprintf(buf + offset, "Mac: %pM", rulla->mac_addr);
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))
hlist_for_each_entry(rulla, node, &priv->lec_arp_empty_ones, next) {
offset = 0;
offset += sprintf(buf + offset, "Mac: %pM", rulla->mac_addr);
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->mcast_fwds))
hlist_for_each_entry(rulla, node, &priv->mcast_fwds, next) {
offset = 0;
offset += sprintf(buf + offset, "Mac: %pM", rulla->mac_addr);
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;
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 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) {
pr_info("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;
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("%p %p: 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);
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
static bool __lec_arp_check_expire(struct lec_arp_table *entry,
unsigned long now,
struct lec_priv *priv)
{
unsigned long time_to_check;
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 */
/* Remove entry */
pr_debug("Entry timed out\n");
lec_arp_remove(priv, entry);
lec_arp_put(entry);
} else {
/* Something else */
if ((entry->status == ESI_VC_PENDING ||
entry->status == ESI_ARP_PENDING) &&
time_after_eq(now, 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)) {
lec_arp_hold(entry);
return true;
}
}
return false;
}
/*
* 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;
int i;
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 (__lec_arp_check_expire(entry, now, priv)) {
struct sk_buff *skb;
struct atm_vcc *vcc = entry->vcc;
spin_unlock_irqrestore(&priv->lec_arp_lock,
flags);
while ((skb = skb_dequeue(&entry->tx_wait)))
lec_send(vcc, skb);
entry->last_used = jiffies;
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;
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.
*/
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++;
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("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);
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;
entry->is_rdesc = is_rdesc;
if (entry->is_rdesc)
send_to_lecd(priv, l_rdesc_arp_xmt, mac_to_find, NULL,
NULL);
else
send_to_lecd(priv, l_arp_xmt, mac_to_find, NULL, NULL);
entry->timer.expires = jiffies + (1 * HZ);
entry->timer.function = lec_arp_expire_arp;
add_timer(&entry->timer);
found = priv->mcast_vcc;
}
out:
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
return found;
}
static int
lec_addr_delete(struct lec_priv *priv, const unsigned char *atm_addr,
unsigned long permanent)
struct hlist_node *node, *next;
struct lec_arp_table *entry;
for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
hlist_for_each_entry_safe(entry, node, next,
&priv->lec_arp_tables[i], next) {
if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN) &&
(permanent ||
!(entry->flags & LEC_PERMANENT_FLAG))) {
lec_arp_put(entry);
return 0;
}
}
return -1;
* Notifies: Response to arp_request (atm_addr != NULL)
lec_arp_update(struct lec_priv *priv, const unsigned char *mac_addr,
const unsigned char *atm_addr, unsigned long remoteflag,
unsigned int targetless_le_arp)
struct hlist_node *node, *next;
struct lec_arp_table *entry, *tmp;
int i;
pr_debug("%smac:%pM\n",
(targetless_le_arp) ? "targetless " : "", mac_addr);
entry = lec_arp_find(priv, mac_addr);
if (entry == NULL && targetless_le_arp)
goto out; /*
* LANE2: ignore targetless LE_ARPs for which
* we have no entry in the cache. 7.1.30
*/
if (!hlist_empty(&priv->lec_arp_empty_ones)) {
hlist_for_each_entry_safe(entry, node, next,
&priv->lec_arp_empty_ones, next) {
if (memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN) == 0) {
hlist_del(&entry->next);