18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* net/sched/sch_teql.c "True" (or "trivial") link equalizer. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/string.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 148c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 188c2ecf20Sopenharmony_ci#include <net/dst.h> 198c2ecf20Sopenharmony_ci#include <net/neighbour.h> 208c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci How to setup it. 248c2ecf20Sopenharmony_ci ---------------- 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci After loading this module you will find a new device teqlN 278c2ecf20Sopenharmony_ci and new qdisc with the same name. To join a slave to the equalizer 288c2ecf20Sopenharmony_ci you should just set this qdisc on a device f.e. 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci # tc qdisc add dev eth0 root teql0 318c2ecf20Sopenharmony_ci # tc qdisc add dev eth1 root teql0 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci That's all. Full PnP 8) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci Applicability. 368c2ecf20Sopenharmony_ci -------------- 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 1. Slave devices MUST be active devices, i.e., they must raise the tbusy 398c2ecf20Sopenharmony_ci signal and generate EOI events. If you want to equalize virtual devices 408c2ecf20Sopenharmony_ci like tunnels, use a normal eql device. 418c2ecf20Sopenharmony_ci 2. This device puts no limitations on physical slave characteristics 428c2ecf20Sopenharmony_ci f.e. it will equalize 9600baud line and 100Mb ethernet perfectly :-) 438c2ecf20Sopenharmony_ci Certainly, large difference in link speeds will make the resulting 448c2ecf20Sopenharmony_ci eqalized link unusable, because of huge packet reordering. 458c2ecf20Sopenharmony_ci I estimate an upper useful difference as ~10 times. 468c2ecf20Sopenharmony_ci 3. If the slave requires address resolution, only protocols using 478c2ecf20Sopenharmony_ci neighbour cache (IPv4/IPv6) will work over the equalized link. 488c2ecf20Sopenharmony_ci Other protocols are still allowed to use the slave device directly, 498c2ecf20Sopenharmony_ci which will not break load balancing, though native slave 508c2ecf20Sopenharmony_ci traffic will have the highest priority. */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct teql_master { 538c2ecf20Sopenharmony_ci struct Qdisc_ops qops; 548c2ecf20Sopenharmony_ci struct net_device *dev; 558c2ecf20Sopenharmony_ci struct Qdisc *slaves; 568c2ecf20Sopenharmony_ci struct list_head master_list; 578c2ecf20Sopenharmony_ci unsigned long tx_bytes; 588c2ecf20Sopenharmony_ci unsigned long tx_packets; 598c2ecf20Sopenharmony_ci unsigned long tx_errors; 608c2ecf20Sopenharmony_ci unsigned long tx_dropped; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct teql_sched_data { 648c2ecf20Sopenharmony_ci struct Qdisc *next; 658c2ecf20Sopenharmony_ci struct teql_master *m; 668c2ecf20Sopenharmony_ci struct sk_buff_head q; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define NEXT_SLAVE(q) (((struct teql_sched_data *)qdisc_priv(q))->next) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define FMASK (IFF_BROADCAST | IFF_POINTOPOINT) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* "teql*" qdisc routines */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int 768c2ecf20Sopenharmony_citeql_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 798c2ecf20Sopenharmony_ci struct teql_sched_data *q = qdisc_priv(sch); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (q->q.qlen < dev->tx_queue_len) { 828c2ecf20Sopenharmony_ci __skb_queue_tail(&q->q, skb); 838c2ecf20Sopenharmony_ci return NET_XMIT_SUCCESS; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return qdisc_drop(skb, sch, to_free); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic struct sk_buff * 908c2ecf20Sopenharmony_citeql_dequeue(struct Qdisc *sch) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct teql_sched_data *dat = qdisc_priv(sch); 938c2ecf20Sopenharmony_ci struct netdev_queue *dat_queue; 948c2ecf20Sopenharmony_ci struct sk_buff *skb; 958c2ecf20Sopenharmony_ci struct Qdisc *q; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci skb = __skb_dequeue(&dat->q); 988c2ecf20Sopenharmony_ci dat_queue = netdev_get_tx_queue(dat->m->dev, 0); 998c2ecf20Sopenharmony_ci q = rcu_dereference_bh(dat_queue->qdisc); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (skb == NULL) { 1028c2ecf20Sopenharmony_ci struct net_device *m = qdisc_dev(q); 1038c2ecf20Sopenharmony_ci if (m) { 1048c2ecf20Sopenharmony_ci dat->m->slaves = sch; 1058c2ecf20Sopenharmony_ci netif_wake_queue(m); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci } else { 1088c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci sch->q.qlen = dat->q.qlen + q->q.qlen; 1118c2ecf20Sopenharmony_ci return skb; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic struct sk_buff * 1158c2ecf20Sopenharmony_citeql_peek(struct Qdisc *sch) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci /* teql is meant to be used as root qdisc */ 1188c2ecf20Sopenharmony_ci return NULL; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void 1228c2ecf20Sopenharmony_citeql_reset(struct Qdisc *sch) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct teql_sched_data *dat = qdisc_priv(sch); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci skb_queue_purge(&dat->q); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void 1308c2ecf20Sopenharmony_citeql_destroy(struct Qdisc *sch) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct Qdisc *q, *prev; 1338c2ecf20Sopenharmony_ci struct teql_sched_data *dat = qdisc_priv(sch); 1348c2ecf20Sopenharmony_ci struct teql_master *master = dat->m; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!master) 1378c2ecf20Sopenharmony_ci return; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci prev = master->slaves; 1408c2ecf20Sopenharmony_ci if (prev) { 1418c2ecf20Sopenharmony_ci do { 1428c2ecf20Sopenharmony_ci q = NEXT_SLAVE(prev); 1438c2ecf20Sopenharmony_ci if (q == sch) { 1448c2ecf20Sopenharmony_ci NEXT_SLAVE(prev) = NEXT_SLAVE(q); 1458c2ecf20Sopenharmony_ci if (q == master->slaves) { 1468c2ecf20Sopenharmony_ci master->slaves = NEXT_SLAVE(q); 1478c2ecf20Sopenharmony_ci if (q == master->slaves) { 1488c2ecf20Sopenharmony_ci struct netdev_queue *txq; 1498c2ecf20Sopenharmony_ci spinlock_t *root_lock; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci txq = netdev_get_tx_queue(master->dev, 0); 1528c2ecf20Sopenharmony_ci master->slaves = NULL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci root_lock = qdisc_root_sleeping_lock(rtnl_dereference(txq->qdisc)); 1558c2ecf20Sopenharmony_ci spin_lock_bh(root_lock); 1568c2ecf20Sopenharmony_ci qdisc_reset(rtnl_dereference(txq->qdisc)); 1578c2ecf20Sopenharmony_ci spin_unlock_bh(root_lock); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci skb_queue_purge(&dat->q); 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci } while ((prev = q) != master->slaves); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, 1698c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 1728c2ecf20Sopenharmony_ci struct teql_master *m = (struct teql_master *)sch->ops; 1738c2ecf20Sopenharmony_ci struct teql_sched_data *q = qdisc_priv(sch); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (dev->hard_header_len > m->dev->hard_header_len) 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (m->dev == dev) 1798c2ecf20Sopenharmony_ci return -ELOOP; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci q->m = m; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci skb_queue_head_init(&q->q); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (m->slaves) { 1868c2ecf20Sopenharmony_ci if (m->dev->flags & IFF_UP) { 1878c2ecf20Sopenharmony_ci if ((m->dev->flags & IFF_POINTOPOINT && 1888c2ecf20Sopenharmony_ci !(dev->flags & IFF_POINTOPOINT)) || 1898c2ecf20Sopenharmony_ci (m->dev->flags & IFF_BROADCAST && 1908c2ecf20Sopenharmony_ci !(dev->flags & IFF_BROADCAST)) || 1918c2ecf20Sopenharmony_ci (m->dev->flags & IFF_MULTICAST && 1928c2ecf20Sopenharmony_ci !(dev->flags & IFF_MULTICAST)) || 1938c2ecf20Sopenharmony_ci dev->mtu < m->dev->mtu) 1948c2ecf20Sopenharmony_ci return -EINVAL; 1958c2ecf20Sopenharmony_ci } else { 1968c2ecf20Sopenharmony_ci if (!(dev->flags&IFF_POINTOPOINT)) 1978c2ecf20Sopenharmony_ci m->dev->flags &= ~IFF_POINTOPOINT; 1988c2ecf20Sopenharmony_ci if (!(dev->flags&IFF_BROADCAST)) 1998c2ecf20Sopenharmony_ci m->dev->flags &= ~IFF_BROADCAST; 2008c2ecf20Sopenharmony_ci if (!(dev->flags&IFF_MULTICAST)) 2018c2ecf20Sopenharmony_ci m->dev->flags &= ~IFF_MULTICAST; 2028c2ecf20Sopenharmony_ci if (dev->mtu < m->dev->mtu) 2038c2ecf20Sopenharmony_ci m->dev->mtu = dev->mtu; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci q->next = NEXT_SLAVE(m->slaves); 2068c2ecf20Sopenharmony_ci NEXT_SLAVE(m->slaves) = sch; 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci q->next = sch; 2098c2ecf20Sopenharmony_ci m->slaves = sch; 2108c2ecf20Sopenharmony_ci m->dev->mtu = dev->mtu; 2118c2ecf20Sopenharmony_ci m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int 2188c2ecf20Sopenharmony_ci__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, 2198c2ecf20Sopenharmony_ci struct net_device *dev, struct netdev_queue *txq, 2208c2ecf20Sopenharmony_ci struct dst_entry *dst) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct neighbour *n; 2238c2ecf20Sopenharmony_ci int err = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci n = dst_neigh_lookup_skb(dst, skb); 2268c2ecf20Sopenharmony_ci if (!n) 2278c2ecf20Sopenharmony_ci return -ENOENT; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (dst->dev != dev) { 2308c2ecf20Sopenharmony_ci struct neighbour *mn; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev); 2338c2ecf20Sopenharmony_ci neigh_release(n); 2348c2ecf20Sopenharmony_ci if (IS_ERR(mn)) 2358c2ecf20Sopenharmony_ci return PTR_ERR(mn); 2368c2ecf20Sopenharmony_ci n = mn; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (neigh_event_send(n, skb_res) == 0) { 2408c2ecf20Sopenharmony_ci int err; 2418c2ecf20Sopenharmony_ci char haddr[MAX_ADDR_LEN]; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci neigh_ha_snapshot(haddr, n, dev); 2448c2ecf20Sopenharmony_ci err = dev_hard_header(skb, dev, ntohs(skb_protocol(skb, false)), 2458c2ecf20Sopenharmony_ci haddr, NULL, skb->len); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (err < 0) 2488c2ecf20Sopenharmony_ci err = -EINVAL; 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci err = (skb_res == NULL) ? -EAGAIN : 1; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci neigh_release(n); 2538c2ecf20Sopenharmony_ci return err; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic inline int teql_resolve(struct sk_buff *skb, 2578c2ecf20Sopenharmony_ci struct sk_buff *skb_res, 2588c2ecf20Sopenharmony_ci struct net_device *dev, 2598c2ecf20Sopenharmony_ci struct netdev_queue *txq) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 2628c2ecf20Sopenharmony_ci int res; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (rcu_access_pointer(txq->qdisc) == &noop_qdisc) 2658c2ecf20Sopenharmony_ci return -ENODEV; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (!dev->header_ops || !dst) 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci rcu_read_lock(); 2718c2ecf20Sopenharmony_ci res = __teql_resolve(skb, skb_res, dev, txq, dst); 2728c2ecf20Sopenharmony_ci rcu_read_unlock(); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return res; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct teql_master *master = netdev_priv(dev); 2808c2ecf20Sopenharmony_ci struct Qdisc *start, *q; 2818c2ecf20Sopenharmony_ci int busy; 2828c2ecf20Sopenharmony_ci int nores; 2838c2ecf20Sopenharmony_ci int subq = skb_get_queue_mapping(skb); 2848c2ecf20Sopenharmony_ci struct sk_buff *skb_res = NULL; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci start = master->slaves; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cirestart: 2898c2ecf20Sopenharmony_ci nores = 0; 2908c2ecf20Sopenharmony_ci busy = 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci q = start; 2938c2ecf20Sopenharmony_ci if (!q) 2948c2ecf20Sopenharmony_ci goto drop; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci do { 2978c2ecf20Sopenharmony_ci struct net_device *slave = qdisc_dev(q); 2988c2ecf20Sopenharmony_ci struct netdev_queue *slave_txq = netdev_get_tx_queue(slave, 0); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (slave_txq->qdisc_sleeping != q) 3018c2ecf20Sopenharmony_ci continue; 3028c2ecf20Sopenharmony_ci if (netif_xmit_stopped(netdev_get_tx_queue(slave, subq)) || 3038c2ecf20Sopenharmony_ci !netif_running(slave)) { 3048c2ecf20Sopenharmony_ci busy = 1; 3058c2ecf20Sopenharmony_ci continue; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci switch (teql_resolve(skb, skb_res, slave, slave_txq)) { 3098c2ecf20Sopenharmony_ci case 0: 3108c2ecf20Sopenharmony_ci if (__netif_tx_trylock(slave_txq)) { 3118c2ecf20Sopenharmony_ci unsigned int length = qdisc_pkt_len(skb); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!netif_xmit_frozen_or_stopped(slave_txq) && 3148c2ecf20Sopenharmony_ci netdev_start_xmit(skb, slave, slave_txq, false) == 3158c2ecf20Sopenharmony_ci NETDEV_TX_OK) { 3168c2ecf20Sopenharmony_ci __netif_tx_unlock(slave_txq); 3178c2ecf20Sopenharmony_ci master->slaves = NEXT_SLAVE(q); 3188c2ecf20Sopenharmony_ci netif_wake_queue(dev); 3198c2ecf20Sopenharmony_ci master->tx_packets++; 3208c2ecf20Sopenharmony_ci master->tx_bytes += length; 3218c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci __netif_tx_unlock(slave_txq); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0))) 3268c2ecf20Sopenharmony_ci busy = 1; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case 1: 3298c2ecf20Sopenharmony_ci master->slaves = NEXT_SLAVE(q); 3308c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3318c2ecf20Sopenharmony_ci default: 3328c2ecf20Sopenharmony_ci nores = 1; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci __skb_pull(skb, skb_network_offset(skb)); 3368c2ecf20Sopenharmony_ci } while ((q = NEXT_SLAVE(q)) != start); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (nores && skb_res == NULL) { 3398c2ecf20Sopenharmony_ci skb_res = skb; 3408c2ecf20Sopenharmony_ci goto restart; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (busy) { 3448c2ecf20Sopenharmony_ci netif_stop_queue(dev); 3458c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci master->tx_errors++; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cidrop: 3508c2ecf20Sopenharmony_ci master->tx_dropped++; 3518c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3528c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int teql_master_open(struct net_device *dev) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct Qdisc *q; 3588c2ecf20Sopenharmony_ci struct teql_master *m = netdev_priv(dev); 3598c2ecf20Sopenharmony_ci int mtu = 0xFFFE; 3608c2ecf20Sopenharmony_ci unsigned int flags = IFF_NOARP | IFF_MULTICAST; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (m->slaves == NULL) 3638c2ecf20Sopenharmony_ci return -EUNATCH; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci flags = FMASK; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci q = m->slaves; 3688c2ecf20Sopenharmony_ci do { 3698c2ecf20Sopenharmony_ci struct net_device *slave = qdisc_dev(q); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (slave == NULL) 3728c2ecf20Sopenharmony_ci return -EUNATCH; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (slave->mtu < mtu) 3758c2ecf20Sopenharmony_ci mtu = slave->mtu; 3768c2ecf20Sopenharmony_ci if (slave->hard_header_len > LL_MAX_HEADER) 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* If all the slaves are BROADCAST, master is BROADCAST 3808c2ecf20Sopenharmony_ci If all the slaves are PtP, master is PtP 3818c2ecf20Sopenharmony_ci Otherwise, master is NBMA. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci if (!(slave->flags&IFF_POINTOPOINT)) 3848c2ecf20Sopenharmony_ci flags &= ~IFF_POINTOPOINT; 3858c2ecf20Sopenharmony_ci if (!(slave->flags&IFF_BROADCAST)) 3868c2ecf20Sopenharmony_ci flags &= ~IFF_BROADCAST; 3878c2ecf20Sopenharmony_ci if (!(slave->flags&IFF_MULTICAST)) 3888c2ecf20Sopenharmony_ci flags &= ~IFF_MULTICAST; 3898c2ecf20Sopenharmony_ci } while ((q = NEXT_SLAVE(q)) != m->slaves); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci m->dev->mtu = mtu; 3928c2ecf20Sopenharmony_ci m->dev->flags = (m->dev->flags&~FMASK) | flags; 3938c2ecf20Sopenharmony_ci netif_start_queue(m->dev); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int teql_master_close(struct net_device *dev) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic void teql_master_stats64(struct net_device *dev, 4048c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct teql_master *m = netdev_priv(dev); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci stats->tx_packets = m->tx_packets; 4098c2ecf20Sopenharmony_ci stats->tx_bytes = m->tx_bytes; 4108c2ecf20Sopenharmony_ci stats->tx_errors = m->tx_errors; 4118c2ecf20Sopenharmony_ci stats->tx_dropped = m->tx_dropped; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int teql_master_mtu(struct net_device *dev, int new_mtu) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct teql_master *m = netdev_priv(dev); 4178c2ecf20Sopenharmony_ci struct Qdisc *q; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci q = m->slaves; 4208c2ecf20Sopenharmony_ci if (q) { 4218c2ecf20Sopenharmony_ci do { 4228c2ecf20Sopenharmony_ci if (new_mtu > qdisc_dev(q)->mtu) 4238c2ecf20Sopenharmony_ci return -EINVAL; 4248c2ecf20Sopenharmony_ci } while ((q = NEXT_SLAVE(q)) != m->slaves); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic const struct net_device_ops teql_netdev_ops = { 4328c2ecf20Sopenharmony_ci .ndo_open = teql_master_open, 4338c2ecf20Sopenharmony_ci .ndo_stop = teql_master_close, 4348c2ecf20Sopenharmony_ci .ndo_start_xmit = teql_master_xmit, 4358c2ecf20Sopenharmony_ci .ndo_get_stats64 = teql_master_stats64, 4368c2ecf20Sopenharmony_ci .ndo_change_mtu = teql_master_mtu, 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic __init void teql_master_setup(struct net_device *dev) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct teql_master *master = netdev_priv(dev); 4428c2ecf20Sopenharmony_ci struct Qdisc_ops *ops = &master->qops; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci master->dev = dev; 4458c2ecf20Sopenharmony_ci ops->priv_size = sizeof(struct teql_sched_data); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ops->enqueue = teql_enqueue; 4488c2ecf20Sopenharmony_ci ops->dequeue = teql_dequeue; 4498c2ecf20Sopenharmony_ci ops->peek = teql_peek; 4508c2ecf20Sopenharmony_ci ops->init = teql_qdisc_init; 4518c2ecf20Sopenharmony_ci ops->reset = teql_reset; 4528c2ecf20Sopenharmony_ci ops->destroy = teql_destroy; 4538c2ecf20Sopenharmony_ci ops->owner = THIS_MODULE; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci dev->netdev_ops = &teql_netdev_ops; 4568c2ecf20Sopenharmony_ci dev->type = ARPHRD_VOID; 4578c2ecf20Sopenharmony_ci dev->mtu = 1500; 4588c2ecf20Sopenharmony_ci dev->min_mtu = 68; 4598c2ecf20Sopenharmony_ci dev->max_mtu = 65535; 4608c2ecf20Sopenharmony_ci dev->tx_queue_len = 100; 4618c2ecf20Sopenharmony_ci dev->flags = IFF_NOARP; 4628c2ecf20Sopenharmony_ci dev->hard_header_len = LL_MAX_HEADER; 4638c2ecf20Sopenharmony_ci netif_keep_dst(dev); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic LIST_HEAD(master_dev_list); 4678c2ecf20Sopenharmony_cistatic int max_equalizers = 1; 4688c2ecf20Sopenharmony_cimodule_param(max_equalizers, int, 0); 4698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_equalizers, "Max number of link equalizers"); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int __init teql_init(void) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci int i; 4748c2ecf20Sopenharmony_ci int err = -ENODEV; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci for (i = 0; i < max_equalizers; i++) { 4778c2ecf20Sopenharmony_ci struct net_device *dev; 4788c2ecf20Sopenharmony_ci struct teql_master *master; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dev = alloc_netdev(sizeof(struct teql_master), "teql%d", 4818c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, teql_master_setup); 4828c2ecf20Sopenharmony_ci if (!dev) { 4838c2ecf20Sopenharmony_ci err = -ENOMEM; 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if ((err = register_netdev(dev))) { 4888c2ecf20Sopenharmony_ci free_netdev(dev); 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci master = netdev_priv(dev); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci strlcpy(master->qops.id, dev->name, IFNAMSIZ); 4958c2ecf20Sopenharmony_ci err = register_qdisc(&master->qops); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (err) { 4988c2ecf20Sopenharmony_ci unregister_netdev(dev); 4998c2ecf20Sopenharmony_ci free_netdev(dev); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci list_add_tail(&master->master_list, &master_dev_list); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci return i ? 0 : err; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void __exit teql_exit(void) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct teql_master *master, *nxt; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci list_for_each_entry_safe(master, nxt, &master_dev_list, master_list) { 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci list_del(&master->master_list); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci unregister_qdisc(&master->qops); 5178c2ecf20Sopenharmony_ci unregister_netdev(master->dev); 5188c2ecf20Sopenharmony_ci free_netdev(master->dev); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cimodule_init(teql_init); 5238c2ecf20Sopenharmony_cimodule_exit(teql_exit); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 526