162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/string.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/timer.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/capability.h> 1162306a36Sopenharmony_ci#include <linux/seq_file.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* We are an ethernet device */ 1462306a36Sopenharmony_ci#include <linux/if_ether.h> 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/etherdevice.h> 1762306a36Sopenharmony_ci#include <net/sock.h> 1862306a36Sopenharmony_ci#include <linux/skbuff.h> 1962306a36Sopenharmony_ci#include <linux/ip.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci#include <asm/byteorder.h> 2262306a36Sopenharmony_ci#include <net/checksum.h> /* for ip_fast_csum() */ 2362306a36Sopenharmony_ci#include <net/arp.h> 2462306a36Sopenharmony_ci#include <net/dst.h> 2562306a36Sopenharmony_ci#include <linux/proc_fs.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* And atm device */ 2862306a36Sopenharmony_ci#include <linux/atmdev.h> 2962306a36Sopenharmony_ci#include <linux/atmlec.h> 3062306a36Sopenharmony_ci#include <linux/atmmpc.h> 3162306a36Sopenharmony_ci/* Modular too */ 3262306a36Sopenharmony_ci#include <linux/module.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "lec.h" 3562306a36Sopenharmony_ci#include "mpc.h" 3662306a36Sopenharmony_ci#include "resources.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * mpc.c: Implementation of MPOA client kernel part 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#if 0 4362306a36Sopenharmony_ci#define dprintk(format, args...) \ 4462306a36Sopenharmony_ci printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args) 4562306a36Sopenharmony_ci#define dprintk_cont(format, args...) printk(KERN_CONT format, ##args) 4662306a36Sopenharmony_ci#else 4762306a36Sopenharmony_ci#define dprintk(format, args...) \ 4862306a36Sopenharmony_ci do { if (0) \ 4962306a36Sopenharmony_ci printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args);\ 5062306a36Sopenharmony_ci } while (0) 5162306a36Sopenharmony_ci#define dprintk_cont(format, args...) \ 5262306a36Sopenharmony_ci do { if (0) printk(KERN_CONT format, ##args); } while (0) 5362306a36Sopenharmony_ci#endif 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#if 0 5662306a36Sopenharmony_ci#define ddprintk(format, args...) \ 5762306a36Sopenharmony_ci printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args) 5862306a36Sopenharmony_ci#define ddprintk_cont(format, args...) printk(KERN_CONT format, ##args) 5962306a36Sopenharmony_ci#else 6062306a36Sopenharmony_ci#define ddprintk(format, args...) \ 6162306a36Sopenharmony_ci do { if (0) \ 6262306a36Sopenharmony_ci printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args);\ 6362306a36Sopenharmony_ci } while (0) 6462306a36Sopenharmony_ci#define ddprintk_cont(format, args...) \ 6562306a36Sopenharmony_ci do { if (0) printk(KERN_CONT format, ##args); } while (0) 6662306a36Sopenharmony_ci#endif 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* mpc_daemon -> kernel */ 6962306a36Sopenharmony_cistatic void MPOA_trigger_rcvd(struct k_message *msg, struct mpoa_client *mpc); 7062306a36Sopenharmony_cistatic void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc); 7162306a36Sopenharmony_cistatic void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); 7262306a36Sopenharmony_cistatic void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); 7362306a36Sopenharmony_cistatic void mps_death(struct k_message *msg, struct mpoa_client *mpc); 7462306a36Sopenharmony_cistatic void clean_up(struct k_message *msg, struct mpoa_client *mpc, 7562306a36Sopenharmony_ci int action); 7662306a36Sopenharmony_cistatic void MPOA_cache_impos_rcvd(struct k_message *msg, 7762306a36Sopenharmony_ci struct mpoa_client *mpc); 7862306a36Sopenharmony_cistatic void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, 7962306a36Sopenharmony_ci struct mpoa_client *mpc); 8062306a36Sopenharmony_cistatic void set_mps_mac_addr_rcvd(struct k_message *mesg, 8162306a36Sopenharmony_ci struct mpoa_client *mpc); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const uint8_t *copy_macs(struct mpoa_client *mpc, 8462306a36Sopenharmony_ci const uint8_t *router_mac, 8562306a36Sopenharmony_ci const uint8_t *tlvs, uint8_t mps_macs, 8662306a36Sopenharmony_ci uint8_t device_type); 8762306a36Sopenharmony_cistatic void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void send_set_mps_ctrl_addr(const char *addr, struct mpoa_client *mpc); 9062306a36Sopenharmony_cistatic void mpoad_close(struct atm_vcc *vcc); 9162306a36Sopenharmony_cistatic int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb); 9462306a36Sopenharmony_cistatic netdev_tx_t mpc_send_packet(struct sk_buff *skb, 9562306a36Sopenharmony_ci struct net_device *dev); 9662306a36Sopenharmony_cistatic int mpoa_event_listener(struct notifier_block *mpoa_notifier, 9762306a36Sopenharmony_ci unsigned long event, void *dev); 9862306a36Sopenharmony_cistatic void mpc_timer_refresh(void); 9962306a36Sopenharmony_cistatic void mpc_cache_check(struct timer_list *unused); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic struct llc_snap_hdr llc_snap_mpoa_ctrl = { 10262306a36Sopenharmony_ci 0xaa, 0xaa, 0x03, 10362306a36Sopenharmony_ci {0x00, 0x00, 0x5e}, 10462306a36Sopenharmony_ci {0x00, 0x03} /* For MPOA control PDUs */ 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_cistatic struct llc_snap_hdr llc_snap_mpoa_data = { 10762306a36Sopenharmony_ci 0xaa, 0xaa, 0x03, 10862306a36Sopenharmony_ci {0x00, 0x00, 0x00}, 10962306a36Sopenharmony_ci {0x08, 0x00} /* This is for IP PDUs only */ 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_cistatic struct llc_snap_hdr llc_snap_mpoa_data_tagged = { 11262306a36Sopenharmony_ci 0xaa, 0xaa, 0x03, 11362306a36Sopenharmony_ci {0x00, 0x00, 0x00}, 11462306a36Sopenharmony_ci {0x88, 0x4c} /* This is for tagged data PDUs */ 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct notifier_block mpoa_notifier = { 11862306a36Sopenharmony_ci mpoa_event_listener, 11962306a36Sopenharmony_ci NULL, 12062306a36Sopenharmony_ci 0 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistruct mpoa_client *mpcs = NULL; /* FIXME */ 12462306a36Sopenharmony_cistatic struct atm_mpoa_qos *qos_head = NULL; 12562306a36Sopenharmony_cistatic DEFINE_TIMER(mpc_timer, mpc_cache_check); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct mpoa_client *find_mpc_by_itfnum(int itf) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct mpoa_client *mpc; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci mpc = mpcs; /* our global linked list */ 13362306a36Sopenharmony_ci while (mpc != NULL) { 13462306a36Sopenharmony_ci if (mpc->dev_num == itf) 13562306a36Sopenharmony_ci return mpc; 13662306a36Sopenharmony_ci mpc = mpc->next; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return NULL; /* not found */ 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic struct mpoa_client *find_mpc_by_vcc(struct atm_vcc *vcc) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct mpoa_client *mpc; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci mpc = mpcs; /* our global linked list */ 14762306a36Sopenharmony_ci while (mpc != NULL) { 14862306a36Sopenharmony_ci if (mpc->mpoad_vcc == vcc) 14962306a36Sopenharmony_ci return mpc; 15062306a36Sopenharmony_ci mpc = mpc->next; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return NULL; /* not found */ 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic struct mpoa_client *find_mpc_by_lec(struct net_device *dev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct mpoa_client *mpc; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci mpc = mpcs; /* our global linked list */ 16162306a36Sopenharmony_ci while (mpc != NULL) { 16262306a36Sopenharmony_ci if (mpc->dev == dev) 16362306a36Sopenharmony_ci return mpc; 16462306a36Sopenharmony_ci mpc = mpc->next; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return NULL; /* not found */ 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * Functions for managing QoS list 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * Overwrites the old entry or makes a new one. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_cistruct atm_mpoa_qos *atm_mpoa_add_qos(__be32 dst_ip, struct atm_qos *qos) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct atm_mpoa_qos *entry; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci entry = atm_mpoa_search_qos(dst_ip); 18262306a36Sopenharmony_ci if (entry != NULL) { 18362306a36Sopenharmony_ci entry->qos = *qos; 18462306a36Sopenharmony_ci return entry; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci entry = kmalloc(sizeof(struct atm_mpoa_qos), GFP_KERNEL); 18862306a36Sopenharmony_ci if (entry == NULL) { 18962306a36Sopenharmony_ci pr_info("mpoa: out of memory\n"); 19062306a36Sopenharmony_ci return entry; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci entry->ipaddr = dst_ip; 19462306a36Sopenharmony_ci entry->qos = *qos; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci entry->next = qos_head; 19762306a36Sopenharmony_ci qos_head = entry; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return entry; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistruct atm_mpoa_qos *atm_mpoa_search_qos(__be32 dst_ip) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct atm_mpoa_qos *qos; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci qos = qos_head; 20762306a36Sopenharmony_ci while (qos) { 20862306a36Sopenharmony_ci if (qos->ipaddr == dst_ip) 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci qos = qos->next; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return qos; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * Returns 0 for failure 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ciint atm_mpoa_delete_qos(struct atm_mpoa_qos *entry) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct atm_mpoa_qos *curr; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (entry == NULL) 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci if (entry == qos_head) { 22662306a36Sopenharmony_ci qos_head = qos_head->next; 22762306a36Sopenharmony_ci kfree(entry); 22862306a36Sopenharmony_ci return 1; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci curr = qos_head; 23262306a36Sopenharmony_ci while (curr != NULL) { 23362306a36Sopenharmony_ci if (curr->next == entry) { 23462306a36Sopenharmony_ci curr->next = entry->next; 23562306a36Sopenharmony_ci kfree(entry); 23662306a36Sopenharmony_ci return 1; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci curr = curr->next; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* this is buggered - we need locking for qos_head */ 24562306a36Sopenharmony_civoid atm_mpoa_disp_qos(struct seq_file *m) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct atm_mpoa_qos *qos; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci qos = qos_head; 25062306a36Sopenharmony_ci seq_printf(m, "QoS entries for shortcuts:\n"); 25162306a36Sopenharmony_ci seq_printf(m, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n"); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci while (qos != NULL) { 25462306a36Sopenharmony_ci seq_printf(m, "%pI4\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n", 25562306a36Sopenharmony_ci &qos->ipaddr, 25662306a36Sopenharmony_ci qos->qos.txtp.max_pcr, 25762306a36Sopenharmony_ci qos->qos.txtp.pcr, 25862306a36Sopenharmony_ci qos->qos.txtp.min_pcr, 25962306a36Sopenharmony_ci qos->qos.txtp.max_cdv, 26062306a36Sopenharmony_ci qos->qos.txtp.max_sdu, 26162306a36Sopenharmony_ci qos->qos.rxtp.max_pcr, 26262306a36Sopenharmony_ci qos->qos.rxtp.pcr, 26362306a36Sopenharmony_ci qos->qos.rxtp.min_pcr, 26462306a36Sopenharmony_ci qos->qos.rxtp.max_cdv, 26562306a36Sopenharmony_ci qos->qos.rxtp.max_sdu); 26662306a36Sopenharmony_ci qos = qos->next; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic struct net_device *find_lec_by_itfnum(int itf) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct net_device *dev; 27362306a36Sopenharmony_ci char name[IFNAMSIZ]; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci sprintf(name, "lec%d", itf); 27662306a36Sopenharmony_ci dev = dev_get_by_name(&init_net, name); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return dev; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic struct mpoa_client *alloc_mpc(void) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct mpoa_client *mpc; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci mpc = kzalloc(sizeof(struct mpoa_client), GFP_KERNEL); 28662306a36Sopenharmony_ci if (mpc == NULL) 28762306a36Sopenharmony_ci return NULL; 28862306a36Sopenharmony_ci rwlock_init(&mpc->ingress_lock); 28962306a36Sopenharmony_ci rwlock_init(&mpc->egress_lock); 29062306a36Sopenharmony_ci mpc->next = mpcs; 29162306a36Sopenharmony_ci atm_mpoa_init_cache(mpc); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci mpc->parameters.mpc_p1 = MPC_P1; 29462306a36Sopenharmony_ci mpc->parameters.mpc_p2 = MPC_P2; 29562306a36Sopenharmony_ci memset(mpc->parameters.mpc_p3, 0, sizeof(mpc->parameters.mpc_p3)); 29662306a36Sopenharmony_ci mpc->parameters.mpc_p4 = MPC_P4; 29762306a36Sopenharmony_ci mpc->parameters.mpc_p5 = MPC_P5; 29862306a36Sopenharmony_ci mpc->parameters.mpc_p6 = MPC_P6; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci mpcs = mpc; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return mpc; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * start_mpc() puts the MPC on line. All the packets destined 30862306a36Sopenharmony_ci * to the lec underneath us are now being monitored and 30962306a36Sopenharmony_ci * shortcuts will be established. 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_cistatic void start_mpc(struct mpoa_client *mpc, struct net_device *dev) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dprintk("(%s)\n", mpc->dev->name); 31662306a36Sopenharmony_ci if (!dev->netdev_ops) 31762306a36Sopenharmony_ci pr_info("(%s) not starting\n", dev->name); 31862306a36Sopenharmony_ci else { 31962306a36Sopenharmony_ci mpc->old_ops = dev->netdev_ops; 32062306a36Sopenharmony_ci mpc->new_ops = *mpc->old_ops; 32162306a36Sopenharmony_ci mpc->new_ops.ndo_start_xmit = mpc_send_packet; 32262306a36Sopenharmony_ci dev->netdev_ops = &mpc->new_ops; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void stop_mpc(struct mpoa_client *mpc) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct net_device *dev = mpc->dev; 32962306a36Sopenharmony_ci dprintk("(%s)", mpc->dev->name); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Lets not nullify lec device's dev->hard_start_xmit */ 33262306a36Sopenharmony_ci if (dev->netdev_ops != &mpc->new_ops) { 33362306a36Sopenharmony_ci dprintk_cont(" mpc already stopped, not fatal\n"); 33462306a36Sopenharmony_ci return; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci dprintk_cont("\n"); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci dev->netdev_ops = mpc->old_ops; 33962306a36Sopenharmony_ci mpc->old_ops = NULL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* close_shortcuts(mpc); ??? FIXME */ 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic const char *mpoa_device_type_string(char type) __attribute__ ((unused)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const char *mpoa_device_type_string(char type) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci switch (type) { 34962306a36Sopenharmony_ci case NON_MPOA: 35062306a36Sopenharmony_ci return "non-MPOA device"; 35162306a36Sopenharmony_ci case MPS: 35262306a36Sopenharmony_ci return "MPS"; 35362306a36Sopenharmony_ci case MPC: 35462306a36Sopenharmony_ci return "MPC"; 35562306a36Sopenharmony_ci case MPS_AND_MPC: 35662306a36Sopenharmony_ci return "both MPS and MPC"; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return "unspecified (non-MPOA) device"; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * lec device calls this via its netdev_priv(dev)->lane2_ops 36462306a36Sopenharmony_ci * ->associate_indicator() when it sees a TLV in LE_ARP packet. 36562306a36Sopenharmony_ci * We fill in the pointer above when we see a LANE2 lec initializing 36662306a36Sopenharmony_ci * See LANE2 spec 3.1.5 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * Quite a big and ugly function but when you look at it 36962306a36Sopenharmony_ci * all it does is to try to locate and parse MPOA Device 37062306a36Sopenharmony_ci * Type TLV. 37162306a36Sopenharmony_ci * We give our lec a pointer to this function and when the 37262306a36Sopenharmony_ci * lec sees a TLV it uses the pointer to call this function. 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_cistatic void lane2_assoc_ind(struct net_device *dev, const u8 *mac_addr, 37662306a36Sopenharmony_ci const u8 *tlvs, u32 sizeoftlvs) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci uint32_t type; 37962306a36Sopenharmony_ci uint8_t length, mpoa_device_type, number_of_mps_macs; 38062306a36Sopenharmony_ci const uint8_t *end_of_tlvs; 38162306a36Sopenharmony_ci struct mpoa_client *mpc; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci mpoa_device_type = number_of_mps_macs = 0; /* silence gcc */ 38462306a36Sopenharmony_ci dprintk("(%s) received TLV(s), ", dev->name); 38562306a36Sopenharmony_ci dprintk("total length of all TLVs %d\n", sizeoftlvs); 38662306a36Sopenharmony_ci mpc = find_mpc_by_lec(dev); /* Sampo-Fix: moved here from below */ 38762306a36Sopenharmony_ci if (mpc == NULL) { 38862306a36Sopenharmony_ci pr_info("(%s) no mpc\n", dev->name); 38962306a36Sopenharmony_ci return; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci end_of_tlvs = tlvs + sizeoftlvs; 39262306a36Sopenharmony_ci while (end_of_tlvs - tlvs >= 5) { 39362306a36Sopenharmony_ci type = ((tlvs[0] << 24) | (tlvs[1] << 16) | 39462306a36Sopenharmony_ci (tlvs[2] << 8) | tlvs[3]); 39562306a36Sopenharmony_ci length = tlvs[4]; 39662306a36Sopenharmony_ci tlvs += 5; 39762306a36Sopenharmony_ci dprintk(" type 0x%x length %02x\n", type, length); 39862306a36Sopenharmony_ci if (tlvs + length > end_of_tlvs) { 39962306a36Sopenharmony_ci pr_info("TLV value extends past its buffer, aborting parse\n"); 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (type == 0) { 40462306a36Sopenharmony_ci pr_info("mpoa: (%s) TLV type was 0, returning\n", 40562306a36Sopenharmony_ci dev->name); 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (type != TLV_MPOA_DEVICE_TYPE) { 41062306a36Sopenharmony_ci tlvs += length; 41162306a36Sopenharmony_ci continue; /* skip other TLVs */ 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci mpoa_device_type = *tlvs++; 41462306a36Sopenharmony_ci number_of_mps_macs = *tlvs++; 41562306a36Sopenharmony_ci dprintk("(%s) MPOA device type '%s', ", 41662306a36Sopenharmony_ci dev->name, mpoa_device_type_string(mpoa_device_type)); 41762306a36Sopenharmony_ci if (mpoa_device_type == MPS_AND_MPC && 41862306a36Sopenharmony_ci length < (42 + number_of_mps_macs*ETH_ALEN)) { /* :) */ 41962306a36Sopenharmony_ci pr_info("(%s) short MPOA Device Type TLV\n", 42062306a36Sopenharmony_ci dev->name); 42162306a36Sopenharmony_ci continue; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci if ((mpoa_device_type == MPS || mpoa_device_type == MPC) && 42462306a36Sopenharmony_ci length < 22 + number_of_mps_macs*ETH_ALEN) { 42562306a36Sopenharmony_ci pr_info("(%s) short MPOA Device Type TLV\n", dev->name); 42662306a36Sopenharmony_ci continue; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (mpoa_device_type != MPS && 42962306a36Sopenharmony_ci mpoa_device_type != MPS_AND_MPC) { 43062306a36Sopenharmony_ci dprintk("ignoring non-MPS device "); 43162306a36Sopenharmony_ci if (mpoa_device_type == MPC) 43262306a36Sopenharmony_ci tlvs += 20; 43362306a36Sopenharmony_ci continue; /* we are only interested in MPSs */ 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci if (number_of_mps_macs == 0 && 43662306a36Sopenharmony_ci mpoa_device_type == MPS_AND_MPC) { 43762306a36Sopenharmony_ci pr_info("(%s) MPS_AND_MPC has zero MACs\n", dev->name); 43862306a36Sopenharmony_ci continue; /* someone should read the spec */ 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci dprintk_cont("this MPS has %d MAC addresses\n", 44162306a36Sopenharmony_ci number_of_mps_macs); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * ok, now we can go and tell our daemon 44562306a36Sopenharmony_ci * the control address of MPS 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci send_set_mps_ctrl_addr(tlvs, mpc); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci tlvs = copy_macs(mpc, mac_addr, tlvs, 45062306a36Sopenharmony_ci number_of_mps_macs, mpoa_device_type); 45162306a36Sopenharmony_ci if (tlvs == NULL) 45262306a36Sopenharmony_ci return; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci if (end_of_tlvs - tlvs != 0) 45562306a36Sopenharmony_ci pr_info("(%s) ignoring %zd bytes of trailing TLV garbage\n", 45662306a36Sopenharmony_ci dev->name, end_of_tlvs - tlvs); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* 46062306a36Sopenharmony_ci * Store at least advertizing router's MAC address 46162306a36Sopenharmony_ci * plus the possible MAC address(es) to mpc->mps_macs. 46262306a36Sopenharmony_ci * For a freshly allocated MPOA client mpc->mps_macs == 0. 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_cistatic const uint8_t *copy_macs(struct mpoa_client *mpc, 46562306a36Sopenharmony_ci const uint8_t *router_mac, 46662306a36Sopenharmony_ci const uint8_t *tlvs, uint8_t mps_macs, 46762306a36Sopenharmony_ci uint8_t device_type) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int num_macs; 47062306a36Sopenharmony_ci num_macs = (mps_macs > 1) ? mps_macs : 1; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (mpc->number_of_mps_macs != num_macs) { /* need to reallocate? */ 47362306a36Sopenharmony_ci if (mpc->number_of_mps_macs != 0) 47462306a36Sopenharmony_ci kfree(mpc->mps_macs); 47562306a36Sopenharmony_ci mpc->number_of_mps_macs = 0; 47662306a36Sopenharmony_ci mpc->mps_macs = kmalloc_array(ETH_ALEN, num_macs, GFP_KERNEL); 47762306a36Sopenharmony_ci if (mpc->mps_macs == NULL) { 47862306a36Sopenharmony_ci pr_info("(%s) out of mem\n", mpc->dev->name); 47962306a36Sopenharmony_ci return NULL; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci ether_addr_copy(mpc->mps_macs, router_mac); 48362306a36Sopenharmony_ci tlvs += 20; if (device_type == MPS_AND_MPC) tlvs += 20; 48462306a36Sopenharmony_ci if (mps_macs > 0) 48562306a36Sopenharmony_ci memcpy(mpc->mps_macs, tlvs, mps_macs*ETH_ALEN); 48662306a36Sopenharmony_ci tlvs += mps_macs*ETH_ALEN; 48762306a36Sopenharmony_ci mpc->number_of_mps_macs = num_macs; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return tlvs; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int send_via_shortcut(struct sk_buff *skb, struct mpoa_client *mpc) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci in_cache_entry *entry; 49562306a36Sopenharmony_ci struct iphdr *iph; 49662306a36Sopenharmony_ci char *buff; 49762306a36Sopenharmony_ci __be32 ipaddr = 0; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci static struct { 50062306a36Sopenharmony_ci struct llc_snap_hdr hdr; 50162306a36Sopenharmony_ci __be32 tag; 50262306a36Sopenharmony_ci } tagged_llc_snap_hdr = { 50362306a36Sopenharmony_ci {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}, {0x88, 0x4c}}, 50462306a36Sopenharmony_ci 0 50562306a36Sopenharmony_ci }; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci buff = skb->data + mpc->dev->hard_header_len; 50862306a36Sopenharmony_ci iph = (struct iphdr *)buff; 50962306a36Sopenharmony_ci ipaddr = iph->daddr; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ddprintk("(%s) ipaddr 0x%x\n", 51262306a36Sopenharmony_ci mpc->dev->name, ipaddr); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci entry = mpc->in_ops->get(ipaddr, mpc); 51562306a36Sopenharmony_ci if (entry == NULL) { 51662306a36Sopenharmony_ci entry = mpc->in_ops->add_entry(ipaddr, mpc); 51762306a36Sopenharmony_ci if (entry != NULL) 51862306a36Sopenharmony_ci mpc->in_ops->put(entry); 51962306a36Sopenharmony_ci return 1; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci /* threshold not exceeded or VCC not ready */ 52262306a36Sopenharmony_ci if (mpc->in_ops->cache_hit(entry, mpc) != OPEN) { 52362306a36Sopenharmony_ci ddprintk("(%s) cache_hit: returns != OPEN\n", 52462306a36Sopenharmony_ci mpc->dev->name); 52562306a36Sopenharmony_ci mpc->in_ops->put(entry); 52662306a36Sopenharmony_ci return 1; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ddprintk("(%s) using shortcut\n", 53062306a36Sopenharmony_ci mpc->dev->name); 53162306a36Sopenharmony_ci /* MPOA spec A.1.4, MPOA client must decrement IP ttl at least by one */ 53262306a36Sopenharmony_ci if (iph->ttl <= 1) { 53362306a36Sopenharmony_ci ddprintk("(%s) IP ttl = %u, using LANE\n", 53462306a36Sopenharmony_ci mpc->dev->name, iph->ttl); 53562306a36Sopenharmony_ci mpc->in_ops->put(entry); 53662306a36Sopenharmony_ci return 1; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci iph->ttl--; 53962306a36Sopenharmony_ci iph->check = 0; 54062306a36Sopenharmony_ci iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (entry->ctrl_info.tag != 0) { 54362306a36Sopenharmony_ci ddprintk("(%s) adding tag 0x%x\n", 54462306a36Sopenharmony_ci mpc->dev->name, entry->ctrl_info.tag); 54562306a36Sopenharmony_ci tagged_llc_snap_hdr.tag = entry->ctrl_info.tag; 54662306a36Sopenharmony_ci skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ 54762306a36Sopenharmony_ci skb_push(skb, sizeof(tagged_llc_snap_hdr)); 54862306a36Sopenharmony_ci /* add LLC/SNAP header */ 54962306a36Sopenharmony_ci skb_copy_to_linear_data(skb, &tagged_llc_snap_hdr, 55062306a36Sopenharmony_ci sizeof(tagged_llc_snap_hdr)); 55162306a36Sopenharmony_ci } else { 55262306a36Sopenharmony_ci skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ 55362306a36Sopenharmony_ci skb_push(skb, sizeof(struct llc_snap_hdr)); 55462306a36Sopenharmony_ci /* add LLC/SNAP header + tag */ 55562306a36Sopenharmony_ci skb_copy_to_linear_data(skb, &llc_snap_mpoa_data, 55662306a36Sopenharmony_ci sizeof(struct llc_snap_hdr)); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci atm_account_tx(entry->shortcut, skb); 56062306a36Sopenharmony_ci entry->shortcut->send(entry->shortcut, skb); 56162306a36Sopenharmony_ci entry->packets_fwded++; 56262306a36Sopenharmony_ci mpc->in_ops->put(entry); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/* 56862306a36Sopenharmony_ci * Probably needs some error checks and locking, not sure... 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_cistatic netdev_tx_t mpc_send_packet(struct sk_buff *skb, 57162306a36Sopenharmony_ci struct net_device *dev) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct mpoa_client *mpc; 57462306a36Sopenharmony_ci struct ethhdr *eth; 57562306a36Sopenharmony_ci int i = 0; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci mpc = find_mpc_by_lec(dev); /* this should NEVER fail */ 57862306a36Sopenharmony_ci if (mpc == NULL) { 57962306a36Sopenharmony_ci pr_info("(%s) no MPC found\n", dev->name); 58062306a36Sopenharmony_ci goto non_ip; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci eth = (struct ethhdr *)skb->data; 58462306a36Sopenharmony_ci if (eth->h_proto != htons(ETH_P_IP)) 58562306a36Sopenharmony_ci goto non_ip; /* Multi-Protocol Over ATM :-) */ 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Weed out funny packets (e.g., AF_PACKET or raw). */ 58862306a36Sopenharmony_ci if (skb->len < ETH_HLEN + sizeof(struct iphdr)) 58962306a36Sopenharmony_ci goto non_ip; 59062306a36Sopenharmony_ci skb_set_network_header(skb, ETH_HLEN); 59162306a36Sopenharmony_ci if (skb->len < ETH_HLEN + ip_hdr(skb)->ihl * 4 || ip_hdr(skb)->ihl < 5) 59262306a36Sopenharmony_ci goto non_ip; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci while (i < mpc->number_of_mps_macs) { 59562306a36Sopenharmony_ci if (ether_addr_equal(eth->h_dest, mpc->mps_macs + i * ETH_ALEN)) 59662306a36Sopenharmony_ci if (send_via_shortcut(skb, mpc) == 0) /* try shortcut */ 59762306a36Sopenharmony_ci return NETDEV_TX_OK; 59862306a36Sopenharmony_ci i++; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cinon_ip: 60262306a36Sopenharmony_ci return __netdev_start_xmit(mpc->old_ops, skb, dev, false); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int atm_mpoa_vcc_attach(struct atm_vcc *vcc, void __user *arg) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int bytes_left; 60862306a36Sopenharmony_ci struct mpoa_client *mpc; 60962306a36Sopenharmony_ci struct atmmpc_ioc ioc_data; 61062306a36Sopenharmony_ci in_cache_entry *in_entry; 61162306a36Sopenharmony_ci __be32 ipaddr; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmmpc_ioc)); 61462306a36Sopenharmony_ci if (bytes_left != 0) { 61562306a36Sopenharmony_ci pr_info("mpoa:Short read (missed %d bytes) from userland\n", 61662306a36Sopenharmony_ci bytes_left); 61762306a36Sopenharmony_ci return -EFAULT; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci ipaddr = ioc_data.ipaddr; 62062306a36Sopenharmony_ci if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF) 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci mpc = find_mpc_by_itfnum(ioc_data.dev_num); 62462306a36Sopenharmony_ci if (mpc == NULL) 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (ioc_data.type == MPC_SOCKET_INGRESS) { 62862306a36Sopenharmony_ci in_entry = mpc->in_ops->get(ipaddr, mpc); 62962306a36Sopenharmony_ci if (in_entry == NULL || 63062306a36Sopenharmony_ci in_entry->entry_state < INGRESS_RESOLVED) { 63162306a36Sopenharmony_ci pr_info("(%s) did not find RESOLVED entry from ingress cache\n", 63262306a36Sopenharmony_ci mpc->dev->name); 63362306a36Sopenharmony_ci if (in_entry != NULL) 63462306a36Sopenharmony_ci mpc->in_ops->put(in_entry); 63562306a36Sopenharmony_ci return -EINVAL; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci pr_info("(%s) attaching ingress SVC, entry = %pI4\n", 63862306a36Sopenharmony_ci mpc->dev->name, &in_entry->ctrl_info.in_dst_ip); 63962306a36Sopenharmony_ci in_entry->shortcut = vcc; 64062306a36Sopenharmony_ci mpc->in_ops->put(in_entry); 64162306a36Sopenharmony_ci } else { 64262306a36Sopenharmony_ci pr_info("(%s) attaching egress SVC\n", mpc->dev->name); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci vcc->proto_data = mpc->dev; 64662306a36Sopenharmony_ci vcc->push = mpc_push; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return 0; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/* 65262306a36Sopenharmony_ci * 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_cistatic void mpc_vcc_close(struct atm_vcc *vcc, struct net_device *dev) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct mpoa_client *mpc; 65762306a36Sopenharmony_ci in_cache_entry *in_entry; 65862306a36Sopenharmony_ci eg_cache_entry *eg_entry; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci mpc = find_mpc_by_lec(dev); 66162306a36Sopenharmony_ci if (mpc == NULL) { 66262306a36Sopenharmony_ci pr_info("(%s) close for unknown MPC\n", dev->name); 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci dprintk("(%s)\n", dev->name); 66762306a36Sopenharmony_ci in_entry = mpc->in_ops->get_by_vcc(vcc, mpc); 66862306a36Sopenharmony_ci if (in_entry) { 66962306a36Sopenharmony_ci dprintk("(%s) ingress SVC closed ip = %pI4\n", 67062306a36Sopenharmony_ci mpc->dev->name, &in_entry->ctrl_info.in_dst_ip); 67162306a36Sopenharmony_ci in_entry->shortcut = NULL; 67262306a36Sopenharmony_ci mpc->in_ops->put(in_entry); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci eg_entry = mpc->eg_ops->get_by_vcc(vcc, mpc); 67562306a36Sopenharmony_ci if (eg_entry) { 67662306a36Sopenharmony_ci dprintk("(%s) egress SVC closed\n", mpc->dev->name); 67762306a36Sopenharmony_ci eg_entry->shortcut = NULL; 67862306a36Sopenharmony_ci mpc->eg_ops->put(eg_entry); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (in_entry == NULL && eg_entry == NULL) 68262306a36Sopenharmony_ci dprintk("(%s) unused vcc closed\n", dev->name); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)vcc->proto_data; 68862306a36Sopenharmony_ci struct sk_buff *new_skb; 68962306a36Sopenharmony_ci eg_cache_entry *eg; 69062306a36Sopenharmony_ci struct mpoa_client *mpc; 69162306a36Sopenharmony_ci __be32 tag; 69262306a36Sopenharmony_ci char *tmp; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci ddprintk("(%s)\n", dev->name); 69562306a36Sopenharmony_ci if (skb == NULL) { 69662306a36Sopenharmony_ci dprintk("(%s) null skb, closing VCC\n", dev->name); 69762306a36Sopenharmony_ci mpc_vcc_close(vcc, dev); 69862306a36Sopenharmony_ci return; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci skb->dev = dev; 70262306a36Sopenharmony_ci if (memcmp(skb->data, &llc_snap_mpoa_ctrl, 70362306a36Sopenharmony_ci sizeof(struct llc_snap_hdr)) == 0) { 70462306a36Sopenharmony_ci struct sock *sk = sk_atm(vcc); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci dprintk("(%s) control packet arrived\n", dev->name); 70762306a36Sopenharmony_ci /* Pass control packets to daemon */ 70862306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 70962306a36Sopenharmony_ci sk->sk_data_ready(sk); 71062306a36Sopenharmony_ci return; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* data coming over the shortcut */ 71462306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci mpc = find_mpc_by_lec(dev); 71762306a36Sopenharmony_ci if (mpc == NULL) { 71862306a36Sopenharmony_ci pr_info("(%s) unknown MPC\n", dev->name); 71962306a36Sopenharmony_ci return; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (memcmp(skb->data, &llc_snap_mpoa_data_tagged, 72362306a36Sopenharmony_ci sizeof(struct llc_snap_hdr)) == 0) { /* MPOA tagged data */ 72462306a36Sopenharmony_ci ddprintk("(%s) tagged data packet arrived\n", dev->name); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci } else if (memcmp(skb->data, &llc_snap_mpoa_data, 72762306a36Sopenharmony_ci sizeof(struct llc_snap_hdr)) == 0) { /* MPOA data */ 72862306a36Sopenharmony_ci pr_info("(%s) Unsupported non-tagged data packet arrived. Purging\n", 72962306a36Sopenharmony_ci dev->name); 73062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 73162306a36Sopenharmony_ci return; 73262306a36Sopenharmony_ci } else { 73362306a36Sopenharmony_ci pr_info("(%s) garbage arrived, purging\n", dev->name); 73462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 73562306a36Sopenharmony_ci return; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci tmp = skb->data + sizeof(struct llc_snap_hdr); 73962306a36Sopenharmony_ci tag = *(__be32 *)tmp; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci eg = mpc->eg_ops->get_by_tag(tag, mpc); 74262306a36Sopenharmony_ci if (eg == NULL) { 74362306a36Sopenharmony_ci pr_info("mpoa: (%s) Didn't find egress cache entry, tag = %u\n", 74462306a36Sopenharmony_ci dev->name, tag); 74562306a36Sopenharmony_ci purge_egress_shortcut(vcc, NULL); 74662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 74762306a36Sopenharmony_ci return; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* 75162306a36Sopenharmony_ci * See if ingress MPC is using shortcut we opened as a return channel. 75262306a36Sopenharmony_ci * This means we have a bi-directional vcc opened by us. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci if (eg->shortcut == NULL) { 75562306a36Sopenharmony_ci eg->shortcut = vcc; 75662306a36Sopenharmony_ci pr_info("(%s) egress SVC in use\n", dev->name); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci skb_pull(skb, sizeof(struct llc_snap_hdr) + sizeof(tag)); 76062306a36Sopenharmony_ci /* get rid of LLC/SNAP header */ 76162306a36Sopenharmony_ci new_skb = skb_realloc_headroom(skb, eg->ctrl_info.DH_length); 76262306a36Sopenharmony_ci /* LLC/SNAP is shorter than MAC header :( */ 76362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 76462306a36Sopenharmony_ci if (new_skb == NULL) { 76562306a36Sopenharmony_ci mpc->eg_ops->put(eg); 76662306a36Sopenharmony_ci return; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci skb_push(new_skb, eg->ctrl_info.DH_length); /* add MAC header */ 76962306a36Sopenharmony_ci skb_copy_to_linear_data(new_skb, eg->ctrl_info.DLL_header, 77062306a36Sopenharmony_ci eg->ctrl_info.DH_length); 77162306a36Sopenharmony_ci new_skb->protocol = eth_type_trans(new_skb, dev); 77262306a36Sopenharmony_ci skb_reset_network_header(new_skb); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci eg->latest_ip_addr = ip_hdr(new_skb)->saddr; 77562306a36Sopenharmony_ci eg->packets_rcvd++; 77662306a36Sopenharmony_ci mpc->eg_ops->put(eg); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci memset(ATM_SKB(new_skb), 0, sizeof(struct atm_skb_data)); 77962306a36Sopenharmony_ci netif_rx(new_skb); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic const struct atmdev_ops mpc_ops = { /* only send is required */ 78362306a36Sopenharmony_ci .close = mpoad_close, 78462306a36Sopenharmony_ci .send = msg_from_mpoad 78562306a36Sopenharmony_ci}; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic struct atm_dev mpc_dev = { 78862306a36Sopenharmony_ci .ops = &mpc_ops, 78962306a36Sopenharmony_ci .type = "mpc", 79062306a36Sopenharmony_ci .number = 42, 79162306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(mpc_dev.lock) 79262306a36Sopenharmony_ci /* members not explicitly initialised will be 0 */ 79362306a36Sopenharmony_ci}; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int atm_mpoa_mpoad_attach(struct atm_vcc *vcc, int arg) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct mpoa_client *mpc; 79862306a36Sopenharmony_ci struct lec_priv *priv; 79962306a36Sopenharmony_ci int err; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (mpcs == NULL) { 80262306a36Sopenharmony_ci mpc_timer_refresh(); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* This lets us now how our LECs are doing */ 80562306a36Sopenharmony_ci err = register_netdevice_notifier(&mpoa_notifier); 80662306a36Sopenharmony_ci if (err < 0) { 80762306a36Sopenharmony_ci del_timer(&mpc_timer); 80862306a36Sopenharmony_ci return err; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci mpc = find_mpc_by_itfnum(arg); 81362306a36Sopenharmony_ci if (mpc == NULL) { 81462306a36Sopenharmony_ci dprintk("allocating new mpc for itf %d\n", arg); 81562306a36Sopenharmony_ci mpc = alloc_mpc(); 81662306a36Sopenharmony_ci if (mpc == NULL) 81762306a36Sopenharmony_ci return -ENOMEM; 81862306a36Sopenharmony_ci mpc->dev_num = arg; 81962306a36Sopenharmony_ci mpc->dev = find_lec_by_itfnum(arg); 82062306a36Sopenharmony_ci /* NULL if there was no lec */ 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci if (mpc->mpoad_vcc) { 82362306a36Sopenharmony_ci pr_info("mpoad is already present for itf %d\n", arg); 82462306a36Sopenharmony_ci return -EADDRINUSE; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (mpc->dev) { /* check if the lec is LANE2 capable */ 82862306a36Sopenharmony_ci priv = netdev_priv(mpc->dev); 82962306a36Sopenharmony_ci if (priv->lane_version < 2) { 83062306a36Sopenharmony_ci dev_put(mpc->dev); 83162306a36Sopenharmony_ci mpc->dev = NULL; 83262306a36Sopenharmony_ci } else 83362306a36Sopenharmony_ci priv->lane2_ops->associate_indicator = lane2_assoc_ind; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci mpc->mpoad_vcc = vcc; 83762306a36Sopenharmony_ci vcc->dev = &mpc_dev; 83862306a36Sopenharmony_ci vcc_insert_socket(sk_atm(vcc)); 83962306a36Sopenharmony_ci set_bit(ATM_VF_META, &vcc->flags); 84062306a36Sopenharmony_ci set_bit(ATM_VF_READY, &vcc->flags); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (mpc->dev) { 84362306a36Sopenharmony_ci char empty[ATM_ESA_LEN]; 84462306a36Sopenharmony_ci memset(empty, 0, ATM_ESA_LEN); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci start_mpc(mpc, mpc->dev); 84762306a36Sopenharmony_ci /* set address if mpcd e.g. gets killed and restarted. 84862306a36Sopenharmony_ci * If we do not do it now we have to wait for the next LE_ARP 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_ci if (memcmp(mpc->mps_ctrl_addr, empty, ATM_ESA_LEN) != 0) 85162306a36Sopenharmony_ci send_set_mps_ctrl_addr(mpc->mps_ctrl_addr, mpc); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci __module_get(THIS_MODULE); 85562306a36Sopenharmony_ci return arg; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic void send_set_mps_ctrl_addr(const char *addr, struct mpoa_client *mpc) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct k_message mesg; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci memcpy(mpc->mps_ctrl_addr, addr, ATM_ESA_LEN); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci mesg.type = SET_MPS_CTRL_ADDR; 86562306a36Sopenharmony_ci memcpy(mesg.MPS_ctrl, addr, ATM_ESA_LEN); 86662306a36Sopenharmony_ci msg_to_mpoad(&mesg, mpc); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic void mpoad_close(struct atm_vcc *vcc) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct mpoa_client *mpc; 87262306a36Sopenharmony_ci struct sk_buff *skb; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci mpc = find_mpc_by_vcc(vcc); 87562306a36Sopenharmony_ci if (mpc == NULL) { 87662306a36Sopenharmony_ci pr_info("did not find MPC\n"); 87762306a36Sopenharmony_ci return; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci if (!mpc->mpoad_vcc) { 88062306a36Sopenharmony_ci pr_info("close for non-present mpoad\n"); 88162306a36Sopenharmony_ci return; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci mpc->mpoad_vcc = NULL; 88562306a36Sopenharmony_ci if (mpc->dev) { 88662306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(mpc->dev); 88762306a36Sopenharmony_ci priv->lane2_ops->associate_indicator = NULL; 88862306a36Sopenharmony_ci stop_mpc(mpc); 88962306a36Sopenharmony_ci dev_put(mpc->dev); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci mpc->in_ops->destroy_cache(mpc); 89362306a36Sopenharmony_ci mpc->eg_ops->destroy_cache(mpc); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci while ((skb = skb_dequeue(&sk_atm(vcc)->sk_receive_queue))) { 89662306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 89762306a36Sopenharmony_ci kfree_skb(skb); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci pr_info("(%s) going down\n", 90162306a36Sopenharmony_ci (mpc->dev) ? mpc->dev->name : "<unknown>"); 90262306a36Sopenharmony_ci module_put(THIS_MODULE); 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/* 90662306a36Sopenharmony_ci * 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_cistatic int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci struct mpoa_client *mpc = find_mpc_by_vcc(vcc); 91262306a36Sopenharmony_ci struct k_message *mesg = (struct k_message *)skb->data; 91362306a36Sopenharmony_ci WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (mpc == NULL) { 91662306a36Sopenharmony_ci pr_info("no mpc found\n"); 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci dprintk("(%s)", mpc->dev ? mpc->dev->name : "<unknown>"); 92062306a36Sopenharmony_ci switch (mesg->type) { 92162306a36Sopenharmony_ci case MPOA_RES_REPLY_RCVD: 92262306a36Sopenharmony_ci dprintk_cont("mpoa_res_reply_rcvd\n"); 92362306a36Sopenharmony_ci MPOA_res_reply_rcvd(mesg, mpc); 92462306a36Sopenharmony_ci break; 92562306a36Sopenharmony_ci case MPOA_TRIGGER_RCVD: 92662306a36Sopenharmony_ci dprintk_cont("mpoa_trigger_rcvd\n"); 92762306a36Sopenharmony_ci MPOA_trigger_rcvd(mesg, mpc); 92862306a36Sopenharmony_ci break; 92962306a36Sopenharmony_ci case INGRESS_PURGE_RCVD: 93062306a36Sopenharmony_ci dprintk_cont("nhrp_purge_rcvd\n"); 93162306a36Sopenharmony_ci ingress_purge_rcvd(mesg, mpc); 93262306a36Sopenharmony_ci break; 93362306a36Sopenharmony_ci case EGRESS_PURGE_RCVD: 93462306a36Sopenharmony_ci dprintk_cont("egress_purge_reply_rcvd\n"); 93562306a36Sopenharmony_ci egress_purge_rcvd(mesg, mpc); 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci case MPS_DEATH: 93862306a36Sopenharmony_ci dprintk_cont("mps_death\n"); 93962306a36Sopenharmony_ci mps_death(mesg, mpc); 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci case CACHE_IMPOS_RCVD: 94262306a36Sopenharmony_ci dprintk_cont("cache_impos_rcvd\n"); 94362306a36Sopenharmony_ci MPOA_cache_impos_rcvd(mesg, mpc); 94462306a36Sopenharmony_ci break; 94562306a36Sopenharmony_ci case SET_MPC_CTRL_ADDR: 94662306a36Sopenharmony_ci dprintk_cont("set_mpc_ctrl_addr\n"); 94762306a36Sopenharmony_ci set_mpc_ctrl_addr_rcvd(mesg, mpc); 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci case SET_MPS_MAC_ADDR: 95062306a36Sopenharmony_ci dprintk_cont("set_mps_mac_addr\n"); 95162306a36Sopenharmony_ci set_mps_mac_addr_rcvd(mesg, mpc); 95262306a36Sopenharmony_ci break; 95362306a36Sopenharmony_ci case CLEAN_UP_AND_EXIT: 95462306a36Sopenharmony_ci dprintk_cont("clean_up_and_exit\n"); 95562306a36Sopenharmony_ci clean_up(mesg, mpc, DIE); 95662306a36Sopenharmony_ci break; 95762306a36Sopenharmony_ci case RELOAD: 95862306a36Sopenharmony_ci dprintk_cont("reload\n"); 95962306a36Sopenharmony_ci clean_up(mesg, mpc, RELOAD); 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci case SET_MPC_PARAMS: 96262306a36Sopenharmony_ci dprintk_cont("set_mpc_params\n"); 96362306a36Sopenharmony_ci mpc->parameters = mesg->content.params; 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci default: 96662306a36Sopenharmony_ci dprintk_cont("unknown message %d\n", mesg->type); 96762306a36Sopenharmony_ci break; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci kfree_skb(skb); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci/* Remember that this function may not do things that sleep */ 97562306a36Sopenharmony_ciint msg_to_mpoad(struct k_message *mesg, struct mpoa_client *mpc) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct sk_buff *skb; 97862306a36Sopenharmony_ci struct sock *sk; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (mpc == NULL || !mpc->mpoad_vcc) { 98162306a36Sopenharmony_ci pr_info("mesg %d to a non-existent mpoad\n", mesg->type); 98262306a36Sopenharmony_ci return -ENXIO; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); 98662306a36Sopenharmony_ci if (skb == NULL) 98762306a36Sopenharmony_ci return -ENOMEM; 98862306a36Sopenharmony_ci skb_put(skb, sizeof(struct k_message)); 98962306a36Sopenharmony_ci skb_copy_to_linear_data(skb, mesg, sizeof(*mesg)); 99062306a36Sopenharmony_ci atm_force_charge(mpc->mpoad_vcc, skb->truesize); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci sk = sk_atm(mpc->mpoad_vcc); 99362306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 99462306a36Sopenharmony_ci sk->sk_data_ready(sk); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic int mpoa_event_listener(struct notifier_block *mpoa_notifier, 100062306a36Sopenharmony_ci unsigned long event, void *ptr) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 100362306a36Sopenharmony_ci struct mpoa_client *mpc; 100462306a36Sopenharmony_ci struct lec_priv *priv; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci if (!net_eq(dev_net(dev), &init_net)) 100762306a36Sopenharmony_ci return NOTIFY_DONE; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (strncmp(dev->name, "lec", 3)) 101062306a36Sopenharmony_ci return NOTIFY_DONE; /* we are only interested in lec:s */ 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci switch (event) { 101362306a36Sopenharmony_ci case NETDEV_REGISTER: /* a new lec device was allocated */ 101462306a36Sopenharmony_ci priv = netdev_priv(dev); 101562306a36Sopenharmony_ci if (priv->lane_version < 2) 101662306a36Sopenharmony_ci break; 101762306a36Sopenharmony_ci priv->lane2_ops->associate_indicator = lane2_assoc_ind; 101862306a36Sopenharmony_ci mpc = find_mpc_by_itfnum(priv->itfnum); 101962306a36Sopenharmony_ci if (mpc == NULL) { 102062306a36Sopenharmony_ci dprintk("allocating new mpc for %s\n", dev->name); 102162306a36Sopenharmony_ci mpc = alloc_mpc(); 102262306a36Sopenharmony_ci if (mpc == NULL) { 102362306a36Sopenharmony_ci pr_info("no new mpc"); 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci mpc->dev_num = priv->itfnum; 102862306a36Sopenharmony_ci mpc->dev = dev; 102962306a36Sopenharmony_ci dev_hold(dev); 103062306a36Sopenharmony_ci dprintk("(%s) was initialized\n", dev->name); 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci case NETDEV_UNREGISTER: 103362306a36Sopenharmony_ci /* the lec device was deallocated */ 103462306a36Sopenharmony_ci mpc = find_mpc_by_lec(dev); 103562306a36Sopenharmony_ci if (mpc == NULL) 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci dprintk("device (%s) was deallocated\n", dev->name); 103862306a36Sopenharmony_ci stop_mpc(mpc); 103962306a36Sopenharmony_ci dev_put(mpc->dev); 104062306a36Sopenharmony_ci mpc->dev = NULL; 104162306a36Sopenharmony_ci break; 104262306a36Sopenharmony_ci case NETDEV_UP: 104362306a36Sopenharmony_ci /* the dev was ifconfig'ed up */ 104462306a36Sopenharmony_ci mpc = find_mpc_by_lec(dev); 104562306a36Sopenharmony_ci if (mpc == NULL) 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci if (mpc->mpoad_vcc != NULL) 104862306a36Sopenharmony_ci start_mpc(mpc, dev); 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci case NETDEV_DOWN: 105162306a36Sopenharmony_ci /* the dev was ifconfig'ed down */ 105262306a36Sopenharmony_ci /* this means that the flow of packets from the 105362306a36Sopenharmony_ci * upper layer stops 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_ci mpc = find_mpc_by_lec(dev); 105662306a36Sopenharmony_ci if (mpc == NULL) 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci if (mpc->mpoad_vcc != NULL) 105962306a36Sopenharmony_ci stop_mpc(mpc); 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci case NETDEV_REBOOT: 106262306a36Sopenharmony_ci case NETDEV_CHANGE: 106362306a36Sopenharmony_ci case NETDEV_CHANGEMTU: 106462306a36Sopenharmony_ci case NETDEV_CHANGEADDR: 106562306a36Sopenharmony_ci case NETDEV_GOING_DOWN: 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci default: 106862306a36Sopenharmony_ci break; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return NOTIFY_DONE; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci/* 107562306a36Sopenharmony_ci * Functions which are called after a message is received from mpcd. 107662306a36Sopenharmony_ci * Msg is reused on purpose. 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic void MPOA_trigger_rcvd(struct k_message *msg, struct mpoa_client *mpc) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci __be32 dst_ip = msg->content.in_info.in_dst_ip; 108362306a36Sopenharmony_ci in_cache_entry *entry; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci entry = mpc->in_ops->get(dst_ip, mpc); 108662306a36Sopenharmony_ci if (entry == NULL) { 108762306a36Sopenharmony_ci entry = mpc->in_ops->add_entry(dst_ip, mpc); 108862306a36Sopenharmony_ci entry->entry_state = INGRESS_RESOLVING; 108962306a36Sopenharmony_ci msg->type = SND_MPOA_RES_RQST; 109062306a36Sopenharmony_ci msg->content.in_info = entry->ctrl_info; 109162306a36Sopenharmony_ci msg_to_mpoad(msg, mpc); 109262306a36Sopenharmony_ci entry->reply_wait = ktime_get_seconds(); 109362306a36Sopenharmony_ci mpc->in_ops->put(entry); 109462306a36Sopenharmony_ci return; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (entry->entry_state == INGRESS_INVALID) { 109862306a36Sopenharmony_ci entry->entry_state = INGRESS_RESOLVING; 109962306a36Sopenharmony_ci msg->type = SND_MPOA_RES_RQST; 110062306a36Sopenharmony_ci msg->content.in_info = entry->ctrl_info; 110162306a36Sopenharmony_ci msg_to_mpoad(msg, mpc); 110262306a36Sopenharmony_ci entry->reply_wait = ktime_get_seconds(); 110362306a36Sopenharmony_ci mpc->in_ops->put(entry); 110462306a36Sopenharmony_ci return; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci pr_info("(%s) entry already in resolving state\n", 110862306a36Sopenharmony_ci (mpc->dev) ? mpc->dev->name : "<unknown>"); 110962306a36Sopenharmony_ci mpc->in_ops->put(entry); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/* 111362306a36Sopenharmony_ci * Things get complicated because we have to check if there's an egress 111462306a36Sopenharmony_ci * shortcut with suitable traffic parameters we could use. 111562306a36Sopenharmony_ci */ 111662306a36Sopenharmony_cistatic void check_qos_and_open_shortcut(struct k_message *msg, 111762306a36Sopenharmony_ci struct mpoa_client *client, 111862306a36Sopenharmony_ci in_cache_entry *entry) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci __be32 dst_ip = msg->content.in_info.in_dst_ip; 112162306a36Sopenharmony_ci struct atm_mpoa_qos *qos = atm_mpoa_search_qos(dst_ip); 112262306a36Sopenharmony_ci eg_cache_entry *eg_entry = client->eg_ops->get_by_src_ip(dst_ip, client); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (eg_entry && eg_entry->shortcut) { 112562306a36Sopenharmony_ci if (eg_entry->shortcut->qos.txtp.traffic_class & 112662306a36Sopenharmony_ci msg->qos.txtp.traffic_class & 112762306a36Sopenharmony_ci (qos ? qos->qos.txtp.traffic_class : ATM_UBR | ATM_CBR)) { 112862306a36Sopenharmony_ci if (eg_entry->shortcut->qos.txtp.traffic_class == ATM_UBR) 112962306a36Sopenharmony_ci entry->shortcut = eg_entry->shortcut; 113062306a36Sopenharmony_ci else if (eg_entry->shortcut->qos.txtp.max_pcr > 0) 113162306a36Sopenharmony_ci entry->shortcut = eg_entry->shortcut; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci if (entry->shortcut) { 113462306a36Sopenharmony_ci dprintk("(%s) using egress SVC to reach %pI4\n", 113562306a36Sopenharmony_ci client->dev->name, &dst_ip); 113662306a36Sopenharmony_ci client->eg_ops->put(eg_entry); 113762306a36Sopenharmony_ci return; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci if (eg_entry != NULL) 114162306a36Sopenharmony_ci client->eg_ops->put(eg_entry); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* No luck in the egress cache we must open an ingress SVC */ 114462306a36Sopenharmony_ci msg->type = OPEN_INGRESS_SVC; 114562306a36Sopenharmony_ci if (qos && 114662306a36Sopenharmony_ci (qos->qos.txtp.traffic_class == msg->qos.txtp.traffic_class)) { 114762306a36Sopenharmony_ci msg->qos = qos->qos; 114862306a36Sopenharmony_ci pr_info("(%s) trying to get a CBR shortcut\n", 114962306a36Sopenharmony_ci client->dev->name); 115062306a36Sopenharmony_ci } else 115162306a36Sopenharmony_ci memset(&msg->qos, 0, sizeof(struct atm_qos)); 115262306a36Sopenharmony_ci msg_to_mpoad(msg, client); 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci __be32 dst_ip = msg->content.in_info.in_dst_ip; 115862306a36Sopenharmony_ci in_cache_entry *entry = mpc->in_ops->get(dst_ip, mpc); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci dprintk("(%s) ip %pI4\n", 116162306a36Sopenharmony_ci mpc->dev->name, &dst_ip); 116262306a36Sopenharmony_ci ddprintk("(%s) entry = %p", 116362306a36Sopenharmony_ci mpc->dev->name, entry); 116462306a36Sopenharmony_ci if (entry == NULL) { 116562306a36Sopenharmony_ci pr_info("(%s) ARGH, received res. reply for an entry that doesn't exist.\n", 116662306a36Sopenharmony_ci mpc->dev->name); 116762306a36Sopenharmony_ci return; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci ddprintk_cont(" entry_state = %d ", entry->entry_state); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (entry->entry_state == INGRESS_RESOLVED) { 117262306a36Sopenharmony_ci pr_info("(%s) RESOLVED entry!\n", mpc->dev->name); 117362306a36Sopenharmony_ci mpc->in_ops->put(entry); 117462306a36Sopenharmony_ci return; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci entry->ctrl_info = msg->content.in_info; 117862306a36Sopenharmony_ci entry->time = ktime_get_seconds(); 117962306a36Sopenharmony_ci /* Used in refreshing func from now on */ 118062306a36Sopenharmony_ci entry->reply_wait = ktime_get_seconds(); 118162306a36Sopenharmony_ci entry->refresh_time = 0; 118262306a36Sopenharmony_ci ddprintk_cont("entry->shortcut = %p\n", entry->shortcut); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (entry->entry_state == INGRESS_RESOLVING && 118562306a36Sopenharmony_ci entry->shortcut != NULL) { 118662306a36Sopenharmony_ci entry->entry_state = INGRESS_RESOLVED; 118762306a36Sopenharmony_ci mpc->in_ops->put(entry); 118862306a36Sopenharmony_ci return; /* Shortcut already open... */ 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (entry->shortcut != NULL) { 119262306a36Sopenharmony_ci pr_info("(%s) entry->shortcut != NULL, impossible!\n", 119362306a36Sopenharmony_ci mpc->dev->name); 119462306a36Sopenharmony_ci mpc->in_ops->put(entry); 119562306a36Sopenharmony_ci return; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci check_qos_and_open_shortcut(msg, mpc, entry); 119962306a36Sopenharmony_ci entry->entry_state = INGRESS_RESOLVED; 120062306a36Sopenharmony_ci mpc->in_ops->put(entry); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci return; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci __be32 dst_ip = msg->content.in_info.in_dst_ip; 120962306a36Sopenharmony_ci __be32 mask = msg->ip_mask; 121062306a36Sopenharmony_ci in_cache_entry *entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (entry == NULL) { 121362306a36Sopenharmony_ci pr_info("(%s) purge for a non-existing entry, ip = %pI4\n", 121462306a36Sopenharmony_ci mpc->dev->name, &dst_ip); 121562306a36Sopenharmony_ci return; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci do { 121962306a36Sopenharmony_ci dprintk("(%s) removing an ingress entry, ip = %pI4\n", 122062306a36Sopenharmony_ci mpc->dev->name, &dst_ip); 122162306a36Sopenharmony_ci write_lock_bh(&mpc->ingress_lock); 122262306a36Sopenharmony_ci mpc->in_ops->remove_entry(entry, mpc); 122362306a36Sopenharmony_ci write_unlock_bh(&mpc->ingress_lock); 122462306a36Sopenharmony_ci mpc->in_ops->put(entry); 122562306a36Sopenharmony_ci entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask); 122662306a36Sopenharmony_ci } while (entry != NULL); 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci __be32 cache_id = msg->content.eg_info.cache_id; 123262306a36Sopenharmony_ci eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(cache_id, mpc); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci if (entry == NULL) { 123562306a36Sopenharmony_ci dprintk("(%s) purge for a non-existing entry\n", 123662306a36Sopenharmony_ci mpc->dev->name); 123762306a36Sopenharmony_ci return; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci write_lock_irq(&mpc->egress_lock); 124162306a36Sopenharmony_ci mpc->eg_ops->remove_entry(entry, mpc); 124262306a36Sopenharmony_ci write_unlock_irq(&mpc->egress_lock); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci mpc->eg_ops->put(entry); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci struct sock *sk; 125062306a36Sopenharmony_ci struct k_message *purge_msg; 125162306a36Sopenharmony_ci struct sk_buff *skb; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci dprintk("entering\n"); 125462306a36Sopenharmony_ci if (vcc == NULL) { 125562306a36Sopenharmony_ci pr_info("vcc == NULL\n"); 125662306a36Sopenharmony_ci return; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); 126062306a36Sopenharmony_ci if (skb == NULL) { 126162306a36Sopenharmony_ci pr_info("out of memory\n"); 126262306a36Sopenharmony_ci return; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci skb_put(skb, sizeof(struct k_message)); 126662306a36Sopenharmony_ci memset(skb->data, 0, sizeof(struct k_message)); 126762306a36Sopenharmony_ci purge_msg = (struct k_message *)skb->data; 126862306a36Sopenharmony_ci purge_msg->type = DATA_PLANE_PURGE; 126962306a36Sopenharmony_ci if (entry != NULL) 127062306a36Sopenharmony_ci purge_msg->content.eg_info = entry->ctrl_info; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci atm_force_charge(vcc, skb->truesize); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci sk = sk_atm(vcc); 127562306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 127662306a36Sopenharmony_ci sk->sk_data_ready(sk); 127762306a36Sopenharmony_ci dprintk("exiting\n"); 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci/* 128162306a36Sopenharmony_ci * Our MPS died. Tell our daemon to send NHRP data plane purge to each 128262306a36Sopenharmony_ci * of the egress shortcuts we have. 128362306a36Sopenharmony_ci */ 128462306a36Sopenharmony_cistatic void mps_death(struct k_message *msg, struct mpoa_client *mpc) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci eg_cache_entry *entry; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci dprintk("(%s)\n", mpc->dev->name); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (memcmp(msg->MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN)) { 129162306a36Sopenharmony_ci pr_info("(%s) wrong MPS\n", mpc->dev->name); 129262306a36Sopenharmony_ci return; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* FIXME: This knows too much of the cache structure */ 129662306a36Sopenharmony_ci read_lock_irq(&mpc->egress_lock); 129762306a36Sopenharmony_ci entry = mpc->eg_cache; 129862306a36Sopenharmony_ci while (entry != NULL) { 129962306a36Sopenharmony_ci purge_egress_shortcut(entry->shortcut, entry); 130062306a36Sopenharmony_ci entry = entry->next; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci read_unlock_irq(&mpc->egress_lock); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci mpc->in_ops->destroy_cache(mpc); 130562306a36Sopenharmony_ci mpc->eg_ops->destroy_cache(mpc); 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cistatic void MPOA_cache_impos_rcvd(struct k_message *msg, 130962306a36Sopenharmony_ci struct mpoa_client *mpc) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci uint16_t holding_time; 131262306a36Sopenharmony_ci eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(msg->content.eg_info.cache_id, mpc); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci holding_time = msg->content.eg_info.holding_time; 131562306a36Sopenharmony_ci dprintk("(%s) entry = %p, holding_time = %u\n", 131662306a36Sopenharmony_ci mpc->dev->name, entry, holding_time); 131762306a36Sopenharmony_ci if (entry == NULL && holding_time) { 131862306a36Sopenharmony_ci entry = mpc->eg_ops->add_entry(msg, mpc); 131962306a36Sopenharmony_ci mpc->eg_ops->put(entry); 132062306a36Sopenharmony_ci return; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci if (holding_time) { 132362306a36Sopenharmony_ci mpc->eg_ops->update(entry, holding_time); 132462306a36Sopenharmony_ci return; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci write_lock_irq(&mpc->egress_lock); 132862306a36Sopenharmony_ci mpc->eg_ops->remove_entry(entry, mpc); 132962306a36Sopenharmony_ci write_unlock_irq(&mpc->egress_lock); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci mpc->eg_ops->put(entry); 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, 133562306a36Sopenharmony_ci struct mpoa_client *mpc) 133662306a36Sopenharmony_ci{ 133762306a36Sopenharmony_ci struct lec_priv *priv; 133862306a36Sopenharmony_ci int i, retval ; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci uint8_t tlv[4 + 1 + 1 + 1 + ATM_ESA_LEN]; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci tlv[0] = 00; tlv[1] = 0xa0; tlv[2] = 0x3e; tlv[3] = 0x2a; /* type */ 134362306a36Sopenharmony_ci tlv[4] = 1 + 1 + ATM_ESA_LEN; /* length */ 134462306a36Sopenharmony_ci tlv[5] = 0x02; /* MPOA client */ 134562306a36Sopenharmony_ci tlv[6] = 0x00; /* number of MPS MAC addresses */ 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci memcpy(&tlv[7], mesg->MPS_ctrl, ATM_ESA_LEN); /* MPC ctrl ATM addr */ 134862306a36Sopenharmony_ci memcpy(mpc->our_ctrl_addr, mesg->MPS_ctrl, ATM_ESA_LEN); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci dprintk("(%s) setting MPC ctrl ATM address to", 135162306a36Sopenharmony_ci mpc->dev ? mpc->dev->name : "<unknown>"); 135262306a36Sopenharmony_ci for (i = 7; i < sizeof(tlv); i++) 135362306a36Sopenharmony_ci dprintk_cont(" %02x", tlv[i]); 135462306a36Sopenharmony_ci dprintk_cont("\n"); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (mpc->dev) { 135762306a36Sopenharmony_ci priv = netdev_priv(mpc->dev); 135862306a36Sopenharmony_ci retval = priv->lane2_ops->associate_req(mpc->dev, 135962306a36Sopenharmony_ci mpc->dev->dev_addr, 136062306a36Sopenharmony_ci tlv, sizeof(tlv)); 136162306a36Sopenharmony_ci if (retval == 0) 136262306a36Sopenharmony_ci pr_info("(%s) MPOA device type TLV association failed\n", 136362306a36Sopenharmony_ci mpc->dev->name); 136462306a36Sopenharmony_ci retval = priv->lane2_ops->resolve(mpc->dev, NULL, 1, NULL, NULL); 136562306a36Sopenharmony_ci if (retval < 0) 136662306a36Sopenharmony_ci pr_info("(%s) targetless LE_ARP request failed\n", 136762306a36Sopenharmony_ci mpc->dev->name); 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic void set_mps_mac_addr_rcvd(struct k_message *msg, 137262306a36Sopenharmony_ci struct mpoa_client *client) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (client->number_of_mps_macs) 137662306a36Sopenharmony_ci kfree(client->mps_macs); 137762306a36Sopenharmony_ci client->number_of_mps_macs = 0; 137862306a36Sopenharmony_ci client->mps_macs = kmemdup(msg->MPS_ctrl, ETH_ALEN, GFP_KERNEL); 137962306a36Sopenharmony_ci if (client->mps_macs == NULL) { 138062306a36Sopenharmony_ci pr_info("out of memory\n"); 138162306a36Sopenharmony_ci return; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci client->number_of_mps_macs = 1; 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci/* 138762306a36Sopenharmony_ci * purge egress cache and tell daemon to 'action' (DIE, RELOAD) 138862306a36Sopenharmony_ci */ 138962306a36Sopenharmony_cistatic void clean_up(struct k_message *msg, struct mpoa_client *mpc, int action) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci eg_cache_entry *entry; 139362306a36Sopenharmony_ci msg->type = SND_EGRESS_PURGE; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* FIXME: This knows too much of the cache structure */ 139762306a36Sopenharmony_ci read_lock_irq(&mpc->egress_lock); 139862306a36Sopenharmony_ci entry = mpc->eg_cache; 139962306a36Sopenharmony_ci while (entry != NULL) { 140062306a36Sopenharmony_ci msg->content.eg_info = entry->ctrl_info; 140162306a36Sopenharmony_ci dprintk("cache_id %u\n", entry->ctrl_info.cache_id); 140262306a36Sopenharmony_ci msg_to_mpoad(msg, mpc); 140362306a36Sopenharmony_ci entry = entry->next; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci read_unlock_irq(&mpc->egress_lock); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci msg->type = action; 140862306a36Sopenharmony_ci msg_to_mpoad(msg, mpc); 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_cistatic unsigned long checking_time; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic void mpc_timer_refresh(void) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci mpc_timer.expires = jiffies + (MPC_P2 * HZ); 141662306a36Sopenharmony_ci checking_time = mpc_timer.expires; 141762306a36Sopenharmony_ci add_timer(&mpc_timer); 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic void mpc_cache_check(struct timer_list *unused) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci struct mpoa_client *mpc = mpcs; 142362306a36Sopenharmony_ci static unsigned long previous_resolving_check_time; 142462306a36Sopenharmony_ci static unsigned long previous_refresh_time; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci while (mpc != NULL) { 142762306a36Sopenharmony_ci mpc->in_ops->clear_count(mpc); 142862306a36Sopenharmony_ci mpc->eg_ops->clear_expired(mpc); 142962306a36Sopenharmony_ci if (checking_time - previous_resolving_check_time > 143062306a36Sopenharmony_ci mpc->parameters.mpc_p4 * HZ) { 143162306a36Sopenharmony_ci mpc->in_ops->check_resolving(mpc); 143262306a36Sopenharmony_ci previous_resolving_check_time = checking_time; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci if (checking_time - previous_refresh_time > 143562306a36Sopenharmony_ci mpc->parameters.mpc_p5 * HZ) { 143662306a36Sopenharmony_ci mpc->in_ops->refresh(mpc); 143762306a36Sopenharmony_ci previous_refresh_time = checking_time; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci mpc = mpc->next; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci mpc_timer_refresh(); 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic int atm_mpoa_ioctl(struct socket *sock, unsigned int cmd, 144562306a36Sopenharmony_ci unsigned long arg) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci int err = 0; 144862306a36Sopenharmony_ci struct atm_vcc *vcc = ATM_SD(sock); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (cmd != ATMMPC_CTRL && cmd != ATMMPC_DATA) 145162306a36Sopenharmony_ci return -ENOIOCTLCMD; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 145462306a36Sopenharmony_ci return -EPERM; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci switch (cmd) { 145762306a36Sopenharmony_ci case ATMMPC_CTRL: 145862306a36Sopenharmony_ci err = atm_mpoa_mpoad_attach(vcc, (int)arg); 145962306a36Sopenharmony_ci if (err >= 0) 146062306a36Sopenharmony_ci sock->state = SS_CONNECTED; 146162306a36Sopenharmony_ci break; 146262306a36Sopenharmony_ci case ATMMPC_DATA: 146362306a36Sopenharmony_ci err = atm_mpoa_vcc_attach(vcc, (void __user *)arg); 146462306a36Sopenharmony_ci break; 146562306a36Sopenharmony_ci default: 146662306a36Sopenharmony_ci break; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci return err; 146962306a36Sopenharmony_ci} 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_cistatic struct atm_ioctl atm_ioctl_ops = { 147262306a36Sopenharmony_ci .owner = THIS_MODULE, 147362306a36Sopenharmony_ci .ioctl = atm_mpoa_ioctl, 147462306a36Sopenharmony_ci}; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic __init int atm_mpoa_init(void) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci register_atm_ioctl(&atm_ioctl_ops); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (mpc_proc_init() != 0) 148162306a36Sopenharmony_ci pr_info("failed to initialize /proc/mpoa\n"); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci pr_info("mpc.c: initialized\n"); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci return 0; 148662306a36Sopenharmony_ci} 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic void __exit atm_mpoa_cleanup(void) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci struct mpoa_client *mpc, *tmp; 149162306a36Sopenharmony_ci struct atm_mpoa_qos *qos, *nextqos; 149262306a36Sopenharmony_ci struct lec_priv *priv; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci mpc_proc_clean(); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci del_timer_sync(&mpc_timer); 149762306a36Sopenharmony_ci unregister_netdevice_notifier(&mpoa_notifier); 149862306a36Sopenharmony_ci deregister_atm_ioctl(&atm_ioctl_ops); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci mpc = mpcs; 150162306a36Sopenharmony_ci mpcs = NULL; 150262306a36Sopenharmony_ci while (mpc != NULL) { 150362306a36Sopenharmony_ci tmp = mpc->next; 150462306a36Sopenharmony_ci if (mpc->dev != NULL) { 150562306a36Sopenharmony_ci stop_mpc(mpc); 150662306a36Sopenharmony_ci priv = netdev_priv(mpc->dev); 150762306a36Sopenharmony_ci if (priv->lane2_ops != NULL) 150862306a36Sopenharmony_ci priv->lane2_ops->associate_indicator = NULL; 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci ddprintk("about to clear caches\n"); 151162306a36Sopenharmony_ci mpc->in_ops->destroy_cache(mpc); 151262306a36Sopenharmony_ci mpc->eg_ops->destroy_cache(mpc); 151362306a36Sopenharmony_ci ddprintk("caches cleared\n"); 151462306a36Sopenharmony_ci kfree(mpc->mps_macs); 151562306a36Sopenharmony_ci memset(mpc, 0, sizeof(struct mpoa_client)); 151662306a36Sopenharmony_ci ddprintk("about to kfree %p\n", mpc); 151762306a36Sopenharmony_ci kfree(mpc); 151862306a36Sopenharmony_ci ddprintk("next mpc is at %p\n", tmp); 151962306a36Sopenharmony_ci mpc = tmp; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci qos = qos_head; 152362306a36Sopenharmony_ci qos_head = NULL; 152462306a36Sopenharmony_ci while (qos != NULL) { 152562306a36Sopenharmony_ci nextqos = qos->next; 152662306a36Sopenharmony_ci dprintk("freeing qos entry %p\n", qos); 152762306a36Sopenharmony_ci kfree(qos); 152862306a36Sopenharmony_ci qos = nextqos; 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci} 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_cimodule_init(atm_mpoa_init); 153362306a36Sopenharmony_cimodule_exit(atm_mpoa_cleanup); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1536