User Tools

Site Tools


operstate

This is an old revision of the document!


Overview

Dissection of how operstate works (RFC 2863).

3.1.14.  IfOperStatus in an Interface Stack

   When an interface is a part of an interface-stack, but is not the
   lowest interface in the stack, then:

   (1)   ifOperStatus has the value 'up' if it is able to pass packets
         due to one or more interfaces below it in the stack being 'up',
         irrespective of whether other interfaces below it are 'down', '
         dormant', 'notPresent', 'lowerLayerDown', 'unknown' or '
         testing'.

   (2)   ifOperStatus may have the value 'up' or 'dormant' if one or
         more interfaces below it in the stack are 'dormant', and all
         others below it are either 'down', 'dormant', 'notPresent', '
         lowerLayerDown', 'unknown' or 'testing'.

   (3)   ifOperStatus has the value 'lowerLayerDown' while all
         interfaces below it in the stack are either 'down', '
         notPresent', 'lowerLayerDown', or 'testing'.

Links:

Flag/state bits

include/linux/netdevice.h

Among many other fields:

*   @state:      Generic network queuing layer state, see netdev_state_t
*   @operstate:  RFC2863 operstate
*   @flags:      Interface flags (a la BSD)

struct net_device {
        unsigned long state;
        unsigned char operstate;
        unsigned int  flags;
};

Here are the possible values of net_device->state:

/* These flag bits are private to the generic network queueing
 * layer; they may not be explicitly referenced by any other
 * code.
 */

enum netdev_state_t {
        __LINK_STATE_START,
        __LINK_STATE_PRESENT,
        __LINK_STATE_NOCARRIER,
        __LINK_STATE_LINKWATCH_PENDING,
        __LINK_STATE_DORMANT,
};

include/uapi/linux/if.h

Possible values for netdev->operstate:

/* RFC 2863 operational status */
enum {
        IF_OPER_UNKNOWN,
        IF_OPER_NOTPRESENT,       // currently unused
        IF_OPER_DOWN,
        IF_OPER_LOWERLAYERDOWN,
        IF_OPER_TESTING,          // currently unused
        IF_OPER_DORMANT,
        IF_OPER_UP,
};

net/core/dev.c

/**
 *      dev_get_flags - get flags reported to userspace
 *      @dev: device
 *
 *      Get the combination of flag bits exported through APIs to userspace.
 */
unsigned int dev_get_flags(const struct net_device *dev)
{
        unsigned int flags;

        flags = (dev->flags & ~(IFF_PROMISC |
                                IFF_ALLMULTI |
                                IFF_RUNNING |
                                IFF_LOWER_UP |
                                IFF_DORMANT)) |
                (dev->gflags & (IFF_PROMISC |
                                IFF_ALLMULTI));

        if (netif_running(dev)) {
                if (netif_oper_up(dev))
                        flags |= IFF_RUNNING;
                if (netif_carrier_ok(dev))
                        flags |= IFF_LOWER_UP;
                if (netif_dormant(dev))
                        flags |= IFF_DORMANT;
        }

        return flags;
}

Testing under sysfs

Unplugging and plugging:

$ cat carrier operstate
1
up
$ cat carrier operstate
0
down
$ cat carrier operstate
1
up
$

netif* routines [netdevice.h]

netif_running()

Based on private netdev_state_t, administrative state:

/**
 *      netif_running - test if up
 *      @dev: network device
 *
 *      Test if the device has been brought up.
 */
static inline bool netif_running(const struct net_device *dev)
{
        return test_bit(__LINK_STATE_START, &dev->state);
}

This should be set by dev_open(), so it should be up regardless. See usage here.

netif_oper_up()

This is the important one related to plugging and unplugging (ignore the possibility of IF_OPER_UNKNOWN for now):

/**
 *      netif_oper_up - test if device is operational
 *      @dev: network device
 *
 * Check if carrier is operational
 */
static inline bool netif_oper_up(const struct net_device *dev)
{
        return (dev->operstate == IF_OPER_UP ||
                dev->operstate == IF_OPER_UNKNOWN /* backward compat */);
}

netif_carrier_ok()

This routine becomes important shortly:

/**
 *      netif_carrier_ok - test if carrier present
 *      @dev: network device
 *
 * Check if carrier is present on device
 */
static inline bool netif_carrier_ok(const struct net_device *dev)
{
        return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
}

Where is IF_OPER_DOWN set?

Philosophy

Clearly(?) based on presence or absence of carrier (see tests above).

net/core/net-sysfs.c

Consults netif_carrier_ok():

static ssize_t carrier_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
{
        struct net_device *netdev = to_net_dev(dev);

        if (netif_running(netdev))  // if __LINK_STATE_START, which should be true
                return sprintf(buf, fmt_dec, !!netif_carrier_ok(netdev));

        return -EINVAL;
}
static DEVICE_ATTR_RW(carrier);

defined as:

static inline bool netif_carrier_ok(const struct net_device *dev)
{
	return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
}

Where is IF_OPER_DOWN set?

From net/core/link_watch.c, it all comes back to netif_carrier_ok():

static unsigned char default_operstate(const struct net_device *dev)
{
	if (!netif_carrier_ok(dev))
		return (dev->ifindex != dev_get_iflink(dev) ?
			IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);

	if (netif_dormant(dev))
		return IF_OPER_DORMANT;

	return IF_OPER_UP;
}

which brings us back to who sets __LINK_STATE_NOCARRIER.

Setting/detecting carrier

From net/sched/sch_generic.c:

/**
 *	netif_carrier_on - set carrier
 *	@dev: network device
 *
 * Device has detected that carrier.
 */
void netif_carrier_on(struct net_device *dev)
{
	if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
		if (dev->reg_state == NETREG_UNINITIALIZED)
			return;
		atomic_inc(&dev->carrier_up_count);
		linkwatch_fire_event(dev);
		if (netif_running(dev))
			__netdev_watchdog_up(dev);
	}
}
EXPORT_SYMBOL(netif_carrier_on);

/**
 *	netif_carrier_off - clear carrier
 *	@dev: network device
 *
 * Device has detected loss of carrier.
 */
void netif_carrier_off(struct net_device *dev)
{
	if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
		if (dev->reg_state == NETREG_UNINITIALIZED)
			return;
		atomic_inc(&dev->carrier_down_count);
		linkwatch_fire_event(dev);
	}
}
EXPORT_SYMBOL(netif_carrier_off);

So who calls netif_carrier_off()? Apparently, lots of people.

operstate.1535368001.txt.gz · Last modified: 2018/08/27 11:06 by rpjday