cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

link_watch.c (6358B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Linux network device link state notification
      4 *
      5 * Author:
      6 *     Stefan Rompf <sux@loplof.de>
      7 */
      8
      9#include <linux/module.h>
     10#include <linux/netdevice.h>
     11#include <linux/if.h>
     12#include <net/sock.h>
     13#include <net/pkt_sched.h>
     14#include <linux/rtnetlink.h>
     15#include <linux/jiffies.h>
     16#include <linux/spinlock.h>
     17#include <linux/workqueue.h>
     18#include <linux/bitops.h>
     19#include <linux/types.h>
     20
     21#include "dev.h"
     22
     23enum lw_bits {
     24	LW_URGENT = 0,
     25};
     26
     27static unsigned long linkwatch_flags;
     28static unsigned long linkwatch_nextevent;
     29
     30static void linkwatch_event(struct work_struct *dummy);
     31static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
     32
     33static LIST_HEAD(lweventlist);
     34static DEFINE_SPINLOCK(lweventlist_lock);
     35
     36static unsigned char default_operstate(const struct net_device *dev)
     37{
     38	if (netif_testing(dev))
     39		return IF_OPER_TESTING;
     40
     41	if (!netif_carrier_ok(dev))
     42		return (dev->ifindex != dev_get_iflink(dev) ?
     43			IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
     44
     45	if (netif_dormant(dev))
     46		return IF_OPER_DORMANT;
     47
     48	return IF_OPER_UP;
     49}
     50
     51
     52static void rfc2863_policy(struct net_device *dev)
     53{
     54	unsigned char operstate = default_operstate(dev);
     55
     56	if (operstate == dev->operstate)
     57		return;
     58
     59	write_lock(&dev_base_lock);
     60
     61	switch(dev->link_mode) {
     62	case IF_LINK_MODE_TESTING:
     63		if (operstate == IF_OPER_UP)
     64			operstate = IF_OPER_TESTING;
     65		break;
     66
     67	case IF_LINK_MODE_DORMANT:
     68		if (operstate == IF_OPER_UP)
     69			operstate = IF_OPER_DORMANT;
     70		break;
     71	case IF_LINK_MODE_DEFAULT:
     72	default:
     73		break;
     74	}
     75
     76	dev->operstate = operstate;
     77
     78	write_unlock(&dev_base_lock);
     79}
     80
     81
     82void linkwatch_init_dev(struct net_device *dev)
     83{
     84	/* Handle pre-registration link state changes */
     85	if (!netif_carrier_ok(dev) || netif_dormant(dev) ||
     86	    netif_testing(dev))
     87		rfc2863_policy(dev);
     88}
     89
     90
     91static bool linkwatch_urgent_event(struct net_device *dev)
     92{
     93	if (!netif_running(dev))
     94		return false;
     95
     96	if (dev->ifindex != dev_get_iflink(dev))
     97		return true;
     98
     99	if (netif_is_lag_port(dev) || netif_is_lag_master(dev))
    100		return true;
    101
    102	return netif_carrier_ok(dev) &&	qdisc_tx_changing(dev);
    103}
    104
    105
    106static void linkwatch_add_event(struct net_device *dev)
    107{
    108	unsigned long flags;
    109
    110	spin_lock_irqsave(&lweventlist_lock, flags);
    111	if (list_empty(&dev->link_watch_list)) {
    112		list_add_tail(&dev->link_watch_list, &lweventlist);
    113		dev_hold_track(dev, &dev->linkwatch_dev_tracker, GFP_ATOMIC);
    114	}
    115	spin_unlock_irqrestore(&lweventlist_lock, flags);
    116}
    117
    118
    119static void linkwatch_schedule_work(int urgent)
    120{
    121	unsigned long delay = linkwatch_nextevent - jiffies;
    122
    123	if (test_bit(LW_URGENT, &linkwatch_flags))
    124		return;
    125
    126	/* Minimise down-time: drop delay for up event. */
    127	if (urgent) {
    128		if (test_and_set_bit(LW_URGENT, &linkwatch_flags))
    129			return;
    130		delay = 0;
    131	}
    132
    133	/* If we wrap around we'll delay it by at most HZ. */
    134	if (delay > HZ)
    135		delay = 0;
    136
    137	/*
    138	 * If urgent, schedule immediate execution; otherwise, don't
    139	 * override the existing timer.
    140	 */
    141	if (test_bit(LW_URGENT, &linkwatch_flags))
    142		mod_delayed_work(system_wq, &linkwatch_work, 0);
    143	else
    144		schedule_delayed_work(&linkwatch_work, delay);
    145}
    146
    147
    148static void linkwatch_do_dev(struct net_device *dev)
    149{
    150	/*
    151	 * Make sure the above read is complete since it can be
    152	 * rewritten as soon as we clear the bit below.
    153	 */
    154	smp_mb__before_atomic();
    155
    156	/* We are about to handle this device,
    157	 * so new events can be accepted
    158	 */
    159	clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
    160
    161	rfc2863_policy(dev);
    162	if (dev->flags & IFF_UP) {
    163		if (netif_carrier_ok(dev))
    164			dev_activate(dev);
    165		else
    166			dev_deactivate(dev);
    167
    168		netdev_state_change(dev);
    169	}
    170	/* Note: our callers are responsible for calling netdev_tracker_free().
    171	 * This is the reason we use __dev_put() instead of dev_put().
    172	 */
    173	__dev_put(dev);
    174}
    175
    176static void __linkwatch_run_queue(int urgent_only)
    177{
    178#define MAX_DO_DEV_PER_LOOP	100
    179
    180	int do_dev = MAX_DO_DEV_PER_LOOP;
    181	struct net_device *dev;
    182	LIST_HEAD(wrk);
    183
    184	/* Give urgent case more budget */
    185	if (urgent_only)
    186		do_dev += MAX_DO_DEV_PER_LOOP;
    187
    188	/*
    189	 * Limit the number of linkwatch events to one
    190	 * per second so that a runaway driver does not
    191	 * cause a storm of messages on the netlink
    192	 * socket.  This limit does not apply to up events
    193	 * while the device qdisc is down.
    194	 */
    195	if (!urgent_only)
    196		linkwatch_nextevent = jiffies + HZ;
    197	/* Limit wrap-around effect on delay. */
    198	else if (time_after(linkwatch_nextevent, jiffies + HZ))
    199		linkwatch_nextevent = jiffies;
    200
    201	clear_bit(LW_URGENT, &linkwatch_flags);
    202
    203	spin_lock_irq(&lweventlist_lock);
    204	list_splice_init(&lweventlist, &wrk);
    205
    206	while (!list_empty(&wrk) && do_dev > 0) {
    207
    208		dev = list_first_entry(&wrk, struct net_device, link_watch_list);
    209		list_del_init(&dev->link_watch_list);
    210
    211		if (!netif_device_present(dev) ||
    212		    (urgent_only && !linkwatch_urgent_event(dev))) {
    213			list_add_tail(&dev->link_watch_list, &lweventlist);
    214			continue;
    215		}
    216		/* We must free netdev tracker under
    217		 * the spinlock protection.
    218		 */
    219		netdev_tracker_free(dev, &dev->linkwatch_dev_tracker);
    220		spin_unlock_irq(&lweventlist_lock);
    221		linkwatch_do_dev(dev);
    222		do_dev--;
    223		spin_lock_irq(&lweventlist_lock);
    224	}
    225
    226	/* Add the remaining work back to lweventlist */
    227	list_splice_init(&wrk, &lweventlist);
    228
    229	if (!list_empty(&lweventlist))
    230		linkwatch_schedule_work(0);
    231	spin_unlock_irq(&lweventlist_lock);
    232}
    233
    234void linkwatch_forget_dev(struct net_device *dev)
    235{
    236	unsigned long flags;
    237	int clean = 0;
    238
    239	spin_lock_irqsave(&lweventlist_lock, flags);
    240	if (!list_empty(&dev->link_watch_list)) {
    241		list_del_init(&dev->link_watch_list);
    242		clean = 1;
    243		/* We must release netdev tracker under
    244		 * the spinlock protection.
    245		 */
    246		netdev_tracker_free(dev, &dev->linkwatch_dev_tracker);
    247	}
    248	spin_unlock_irqrestore(&lweventlist_lock, flags);
    249	if (clean)
    250		linkwatch_do_dev(dev);
    251}
    252
    253
    254/* Must be called with the rtnl semaphore held */
    255void linkwatch_run_queue(void)
    256{
    257	__linkwatch_run_queue(0);
    258}
    259
    260
    261static void linkwatch_event(struct work_struct *dummy)
    262{
    263	rtnl_lock();
    264	__linkwatch_run_queue(time_after(linkwatch_nextevent, jiffies));
    265	rtnl_unlock();
    266}
    267
    268
    269void linkwatch_fire_event(struct net_device *dev)
    270{
    271	bool urgent = linkwatch_urgent_event(dev);
    272
    273	if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
    274		linkwatch_add_event(dev);
    275	} else if (!urgent)
    276		return;
    277
    278	linkwatch_schedule_work(urgent);
    279}
    280EXPORT_SYMBOL(linkwatch_fire_event);