Linux network driver - kernel network driver framework (2)

net_device structure

The Linux kernel uses the net_device structure to represent a specific network device, and net_device is driven by the entire network

soul. The core of the network driver is to initialize each member variable in the net_device structure, and then complete the initialization

After the net_device is registered to the Linux kernel. The net_device structure is defined in include/linux/netdevice.h,

net_device is a huge structure with the following contents (reduced):

struct net_device {
char name[IFNAMSIZ];
struct hlist_node name_hlist;
char *ifalias;
/*
* I/O specific fields
* FIXME: Merge these and struct ifmap into one
*/
unsigned long mem_end;
unsigned long mem_start;
unsigned long base_addr;
int irq;

atomic_t carrier_changes;

/*
* Some hardware also needs these fields (state,dev_list,
* napi_list,unreg_list,close_list) but they are not
* part of the usual set specified in Space.c.
*/

unsigned long state;

struct list_head dev_list;
struct list_head napi_list;
struct list_head unreg_list;
struct list_head close_list;
......
const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops;
#ifdef CONFIG_NET_SWITCHDEV
const struct swdev_ops *swdev_ops;
#endif

const struct header_ops *header_ops;

unsigned int flags;
......
unsigned char if_port;
unsigned char dma;

unsigned int mtu;
unsigned short type;
unsigned short hard_header_len;

unsigned short needed_headroom;
unsigned short needed_tailroom;

/* Interface address info. */
unsigned char perm_addr[MAX_ADDR_LEN];
unsigned char addr_assign_type;
unsigned char addr_len;
......
/*
* Cache lines mostly used on receive path (including
eth_type_trans())
*/
unsigned long last_rx;

/* Interface address info used in eth_type_trans() */
unsigned char *dev_addr;


#ifdef CONFIG_SYSFS
struct netdev_rx_queue *_rx;
unsigned int num_rx_queues;
unsigned int real_num_rx_queues;

#endif
......
/*
* Cache lines mostly used on transmit path
*/
struct netdev_queue *_tx ____cacheline_aligned_in_smp;
unsigned int num_tx_queues;
unsigned int real_num_tx_queues;
struct Qdisc *qdisc;
unsigned long tx_queue_len;
spinlock_t tx_global_lock;
int watchdog_timeo;
......
/* These may be needed for future network-power-down code. */

/*
* trans_start here is expensive for high speed devices on SMP,
* please use netdev_queue->trans_start instead.
*/
unsigned long trans_start;
......
struct phy_device *phydev;
struct lock_class_key *qdisc_tx_busylock;
};

The following introduces some key member variables, as follows:

name is the name of the network device.

mem_end is the end address of shared memory.

mem_start is the shared memory start address.

base_addr is the network device I/O address.

irq is the interrupt number of the network device.

dev_list is the global network device list.

napi_list is the list entry of napi network devices.

unreg_list is the entry of unregistered network device list.

close_list is the entry of closed network device list.

netdev_ops is the operation set function of the network device, including a series of network device operation callback functions,

Similar to file_operations in character devices, the netdev_ops structure will be explained later.

ethtool_ops is a set of functions related to network management tools, and user space network management tools will call this structure

The related functions in get the network card status or configure the network card.

header_ops is a set of relevant operation functions of the header, such as creation, parsing, buffering, etc.

flags is the network interface flag, and the flag type is defined in the include/uapi/linux/if.h file, which is an enumeration

The type is as follows:

enum net_device_flags {
  IFF_UP = 1<<0, /* sysfs */
  IFF_BROADCAST = 1<<1, /* volatile */
  IFF_DEBUG = 1<<2, /* sysfs */
  IFF_LOOPBACK = 1<<3, /* volatile */
  IFF_POINTOPOINT = 1<<4, /* volatile */
  IFF_NOTRAILERS = 1<<5, /* sysfs */
  IFF_RUNNING = 1<<6, /* volatile */
  IFF_NOARP = 1<<7, /* sysfs */
  IFF_PROMISC = 1<<8, /* sysfs */
  IFF_ALLMULTI = 1<<9, /* sysfs */
  IFF_MASTER = 1<<10, /* volatile */
  IFF_SLAVE = 1<<11, /* volatile */
  IFF_MULTICAST = 1<<12, /* sysfs */
  IFF_PORTSEL = 1<<13, /* sysfs */
  IFF_AUTOMEDIA = 1<<14, /* sysfs */
  IFF_DYNAMIC = 1<<15, /* sysfs */
  IFF_LOWER_UP = 1<<16, /* volatile */
  IFF_DORMANT = 1<<17, /* volatile */
  IFF_ECHO = 1<<18, /* volatile */
};

Go back to the sample code and look at the net_device structure.

if_port specifies the port type of the interface, if the device supports multiple ports, use if_port to specify the port type used. The optional port type is defined in include/uapi/linux/netdevice.h, which is an enumerated type, as follows:

enum {
  IF_PORT_UNKNOWN = 0,
  IF_PORT_10BASE2,
  IF_PORT_10BASET,
  IF_PORT_AUI,
  IF_PORT_100BASET,
  IF_PORT_100BASETX,
  IF_PORT_100BASEFX
};

DMA is the DMA channel used by network devices, not all devices use DMA.

mtu is the maximum transmission unit of the network, which is 1500.

type is used to specify the type of ARP module, the ARP interface of Ethernet is ARPHRD_ETHER, Linux

The ARP protocol supported by the kernel is defined in include/uapi/linux/if_arp.h, please refer to it yourself.

perm_addr is a permanent hardware address, if a network card device has a permanent hardware address, then perm_addr will be filled

addr_len is the hardware address length.

last_rx is the timestamp of the last received packet, recorded in jiffies.

dev_addr is also a hardware address, which is the currently assigned MAC address and can be modified by software.

_rx is the receive queue.

num_rx_queues is the number of receiving queues, when calling register_netdev to register network devices, it will

Allocates the specified number of receive queues.

real_num_rx_queues is the number of queues currently active.

_tx is the send queue.

num_tx_queues is the number of sending queues, and the specified number of sending queues are allocated through the alloc_netdev_mq function

send queue.

real_num_tx_queues is the number of currently valid transmit queues.

trans_start is the timestamp of the last packet sent, recorded in jiffies.

phydev is the corresponding PHY device.

1. Apply for net_device

When writing a network driver, first apply for net_device, use the alloc_netdev function to apply for net_device, this

is a macro defined as follows:

define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \
   alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)

It can be seen that the essence of alloc_netdev is the alloc_netdev_mqs function, and the prototype of this function is as follows

struct net_device * alloc_netdev_mqs ( int sizeof_priv,
const char *name,void (*setup) (struct net_device *)),unsigned int txqs,
                              unsigned int rxqs);

Function parameters and return values ​​have the following meanings:

sizeof_priv : private data block size.

name : device name.

setup : Callback function, this function is called after initializing the device of the device.

txqs : Number of send queues allocated.

rxqs : Number of receive queues allocated.

Return value: If the application is successful, it returns the net_device pointer applied for, and if it fails, it returns NULL.

In fact, there are many kinds of network devices, so don't think that there is only one kind of Ethernet. Network Interfaces Supported by the Linux Kernel

There are many ports, such as Fiber Distributed Data Interface (FDDI), Ethernet Device (Ethernet), Infrared Data Interface (InDA), High

Performance Parallel Interface (HPPI), CAN network, etc. The kernel provides different network devices based on alloc_netdev

One layer of encapsulation, such as the Ethernet we explained in this chapter, the net_device application function for Ethernet encapsulation is alloc_etherdev

and, which is also a macro, reads:

#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count)
                    alloc_etherdev_mqs(sizeof_priv, count, count)

It can be seen that alloc_etherdev ultimately relies on the alloc_etherdev_mqs function, which is a simple encapsulation of alloc_netdev_mqs. The function content is as follows:

struct net_device *alloc_etherdev_mqs(int sizeof_priv,
  unsigned int txqs,
   unsigned int rxqs)
{
      return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN,
                                  ether_setup, txqs, rxqs);
}

Line 5 calls alloc_netdev_mqs to apply for net_device. Note that the name of the network card is set to "eth%d", which means

It is a formatted string. The names of network cards such as "eth0" and "eth1" that you see after entering the linux system of the development board are

is from here. Similarly, the setup function of Ethernet is set here as ether_setup, and the setup function of different network devices

The functions are different, for example, the setup function in the CAN network is can_setup

The ether_setup function will initially initialize net_device, and the function content is as follows:

void ether_setup(struct net_device *dev)
{
   dev->header_ops = &eth_header_ops;
   dev->type = ARPHRD_ETHER;
   dev->hard_header_len = ETH_HLEN;
   dev->mtu = ETH_DATA_LEN;
   dev->addr_len = ETH_ALEN;
   dev->tx_queue_len = 1000; /* Ethernet wants good queues */
   dev->flags = IFF_BROADCAST|IFF_MULTICAST;
   dev->priv_flags |= IFF_TX_SKB_SHARING;

   eth_broadcast_addr(dev->broadcast);
}

This is the end of the application for net_device. For network devices, use alloc_etherdev alloc_etherdev_mqs to apply for net_device. The official network driver written by NXP uses alloc_etherdev_mqs to apply for net_device.

2. Delete net_device

When we log out of the network driver, we need to release the net_device that has been applied for before. The release function is free_netdev. The function prototype is as follows:

void free_netdev(struct net_device *dev)

Function parameters and return values ​​have the following meanings:

dev: The net_device pointer to be released.

Return value: None.

3. Register net_device

After net_device is applied and initialized, it is necessary to register net_device with the kernel. The function register_netdev is used. The function prototype is as follows:

int register_netdev(struct net_device *dev)

Function parameters and return values ​​have the following meanings:

dev: The net_device pointer to be registered.

Return value: None

net_device_ops structure

net_device has a very important member variable: netdev_ops, which is the net_device_ops structure pointer type, which is the operation set of the network device. The net_device_ops structure is defined in the include/linux/netdevice.h file. The net_device_ops structure contains some functions starting with "ndo_". These functions need to be implemented by network driver writers, not all of them. According to the actual driver Part of the situation can be realized. The content of the structure is as follows (the structure is relatively large, and there are reductions here):

     示例代码 69.3.2.1 net_device_ops 结构体 
struct net_device_ops {
   int (*ndo_init)(struct net_device *dev);
   void (*ndo_uninit)(struct net_device *dev);
   int (*ndo_open)(struct net_device *dev);
   int (*ndo_stop)(struct net_device *dev);
   netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev);
   u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
   void *accel_priv,select_queue_fallback_t fallback);
   void (*ndo_change_rx_flags)(struct net_device *dev,int flags);
   void (*ndo_set_rx_mode)(struct net_device *dev);
   int (*ndo_set_mac_address)(struct net_device *dev,void *addr);
   int (*ndo_validate_addr)(struct net_device *dev);
   int (*ndo_do_ioctl)(struct net_device *dev,struct ifreq *ifr, int cmd);
   int (*ndo_set_config)(struct net_device *dev,struct ifmap *map);
   int (*ndo_change_mtu)(struct net_device *dev,int new_mtu);
   int (*ndo_neigh_setup)(struct net_device *dev,struct neigh_parms *);
   void (*ndo_tx_timeout) (struct net_device *dev);
#ifdef CONFIG_NET_POLL_CONTROLLER
   void (*ndo_poll_controller)(struct net_device *dev);
   int (*ndo_netpoll_setup)(struct net_device *dev,struct netpoll_info *info);
   void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
  int (*ndo_set_features)(struct net_device *dev,netdev_features_t features);
}

Line 3: ndo_init function, this function will be executed when the network device is registered for the first time, the device can do some content that needs to be initialized later in this function, but this function is not used in general drivers, virtual network devices may be use.

Line 4: ndo_uninit function, this function will be executed when the network device is uninstalled.

Line 5: ndo_open function, this function will be executed when the network device is opened, the network driver needs to implement this function, it is very important! Taking NXP's I.MX series SOC network driver as an example, it will do the following work in this function:

· Enable network peripheral clocks.

· Apply for the ring buffer used by the network.

• Initialize the MAC peripherals.

· Bind the PHY corresponding to the interface.

·If NAPI is used, the NAPI module must be enabled through the napi_enable function.

· Turn on the PHY. • Call netif_tx_start_all_queues to enable transmission queues, and may also call the netif_start_queue function.

·……

Line 6: ndo_stop function, this function will be executed when the network device is closed, and the network driver also needs to implement this function. Taking NXP's I.MX series SOC network driver as an example, it will do the following work in this function:

• Stop the PHY. • Stop the NAPI function. ·Stop sending function. · Turn off the MAC. • Disconnect the PHY connection. · Turn off the network clock. · Release the data buffer. ·...

Line 7: ndo_start_xmit function, this function will be executed when data needs to be sent, this function has a parameter of sk_buff structure pointer, sk_buff structure is very important in Linux network driver, sk_buff saves the upper layer and passes it to the network driver layer data. That is to say, the data to be sent is stored in sk_buff, and sk_buff will be explained in detail later. This function returns NETDEV_TX_OK if the transmission is successful, and returns NETDEV_TX_BUSY if the transmission fails, and we need to stop the queue if the transmission fails.

Line 8: ndo_select_queue function, select which queue to use when the device supports multiple transmission queues.

Line 11: ndo_set_rx_mode function, this function is used to change the address filtering list, and set the SOC network peripheral registers according to the flags member variable of net_device. For example, flags may be IFF_PROMISC, IFF_ALLMULTI or IFF_MULTICAST, respectively representing promiscuous mode, unicast mode or multicast mode.

Line 12: ndo_set_mac_address function, this function is used to modify the MAC address of the network card, set the dev_addr member variable of net_device, and write the MAC address to the hardware register of the network peripheral

Line 13: ndo_validate_addr function, to verify whether the MAC address is valid, that is, to verify whether the MAC address in the dev_addr of net_device is valid, and directly call the is_valid_ether_addr function.

Line 14: ndo_do_ioctl function, this function will be executed when the user program calls ioctl, such as PHY chip-related command operations, generally directly call the phy_mii_ioctl function.

Line 16: ndo_change_mtu function, change the MTU size.

Line 18: ndo_tx_timeout function, the function will be executed when the sending timeout occurs, usually because of a problem with the network that causes the sending timeout. Generally, the MAC and PHY may be restarted, data transmission may be restarted, etc.

Line 20: ndo_poll_controller function, use the query method to process the sending and receiving of network card data.

Line 24: ndo_set_features function, modify the features attribute of net_device, and set the corresponding hardware attributes

sk_buff structure

The network is layered. For the application layer, there is no need to care about how the specific bottom layer works. It only needs to package the data to be sent or received according to the protocol. After packing, the data will be sent out through the dev_queue_xmit function, and the netif_rx function can be used to receive the data. Let's take a look at these two functions in turn.

  1. dev_queue_xmit function

This function is used to send network data. The function is defined in include/linux/netdevice.h. The function prototype is as follows: static inline int dev_queue_xmit(struct sk_buff *skb)

Function parameters and return values ​​have the following meanings:

skb: the data to be sent, this is a sk_buff structure pointer, sk_buff is a very important structure in the Linux network driver, the network data is saved in sk_buff, each protocol layer adds its own protocol header in sk_buff, and finally the The underlying driver sends out the data in sk_buff. The receiving process of network data is just the opposite. The underlying network driver packs the received raw data into sk_buff, and then sends it to the upper layer protocol. The upper layer will remove the corresponding header, and then send the final data to the user.

Return value: 0 is sent successfully, negative value is sent failed.

The dev_queue_xmit function is too long, so I won’t analyze it in detail here. The dev_queue_xmit function is finally sent through the ndo_start_xmit function in the net_device_ops operation set. ndo_start_xmit is implemented by the network driver writer. The whole process is shown in the figure:

  1. netif_rx function

The upper layer uses the netif_rx function to receive data, but the most primitive network data is generally received by polling, interrupt or NAPI. The netif_rx function is defined in net/core/dev.c, and the function prototype is as follows:

int netif_rx(struct sk_buff *skb)

Function parameters and return values ​​have the following meanings:

skb: save the sk_buff of the received data,.

Return value: NET_RX_SUCCESS success, NET_RX_DROP packet drop.

Let's focus on the structure of sk_buff. sk_buff is an important data structure of Linux network, which is used to manage receiving or sending data packets. The structure of sk_buff is defined in include/linux/skbuff.h. The content of the structure is as follows (because the structure Relatively large, in order to reduce the space only some important content is listed):

 struct sk_buff {
    union {
        struct {
            /* These two members must be first. */
            struct sk_buff *next;
            struct sk_buff *prev;
            union {
                ktime_t tstamp;
                struct skb_mstamp skb_mstamp;
            };
       };
      struct rb_node rbnode; /* used in netem & tcp stack */
   };
  struct sock *sk;
  struct net_device *dev;
  /*
   * This is the control buffer. It is free to use for every
   * layer. Please put your private variables there. If you
   * want to keep them across layers you have to do a skb_clone()
   * first. This is owned by whoever has the skb queued ATM.
  */
 char cb[48] __aligned(8);
 unsigned long _skb_refdst;
 void (*destructor)(struct sk_buff *skb);
 unsigned int len, data_len;
 __u16 mac_len, hdr_len;
 __be16 protocol;
 __u16 transport_header;
 __u16 network_header;
 __u16 mac_header;
 __u32 headers_end[0];
 sk_buff_data_t tail;
 sk_buff_data_t end;
 unsigned char *head, *data;
 unsigned int truesize;
 atomic_t users;
};

Lines 5~6: next and prev point to the next and previous sk_buff respectively, forming a doubly linked list.

Line 9: tstamp indicates the timestamp when the packet was received or ready to be sent.

Line 14: sk indicates the Socket to which the current sk_buff belongs.

Line 15: dev indicates which device the current sk_buff is received or sent from.

Line 22: cb is the control buffer, no matter which layer can freely use this buffer to place private data.

Line 24: The destructor function, when the buffer is released, certain actions can be completed in this function.

Line 25: len is the actual data length, including the data length in the main buffer and the data length in the fragment. data_len is the data length, and only calculates the length of the data in the slice.

Line 26: mac_len is the length of the connection layer header, that is, the length of the MAC header.

Line 27: protocol protocol.

Line 28: transport_header is the transport layer header.

Line 29: network_header is the header of the network layer.

Line 30: mac_header is the link layer header.

Line 32: tail points to the end of the actual data.

Line 33: end points to the end of the buffer.

Line 34: head points to the head of the buffer, and data points to the head of the actual data. data and tail point to the head and tail of the actual data, and head and end point to the head and tail of the buffer. The structure is shown in the figure:

A series of operation and management functions are provided for the sk_buff kernel. Let's briefly look at some common API functions:

3. Assign sk_buff

To use sk_buff, it must be allocated first. Let’s take a look at the alloc_skb function first. This function is defined in include/linux/skbuff.h. The function prototype is as follows:

static inline struct sk_buff *alloc_skb(unsigned int size,gfp_t priority)

Function parameters and return values ​​have the following meanings:

size: The size to be allocated, that is, the size of the skb data segment.

priority: GFP MASK macro, such as GFP_KERNEL, GFP_ATOMIC, etc.

Return value: If the allocation is successful, it will return the first address of the applied sk_buff, if it fails, it will return NULL.

Netdev_alloc_skb is often used in network device drivers to apply for an skb_buff for receiving for a certain device. This function

Also defined in include/linux/skbuff.h, the function prototype is as follows:

static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev,unsigned int length)

Function parameters and return values ​​have the following meanings:

dev: Which device to assign sk_buff to.

length: The size to allocate.

Return value: If the allocation is successful, it will return the first address of the applied sk_buff, if it fails, it will return NULL.

4. Release sk_buff

When the use is completed, the sk_buff must be released. The release function can use kfree_skb. The function is defined in include/linux/skbuff.c, and the function prototype is as follows:

void kfree_skb(struct sk_buff *skb);

Function parameters and return values ​​have the following meanings:

skb: The sk_buff to release.

Return value: None.

For network devices it is best to use release functions like this:

void dev_kfree_skb (struct sk_buff *skb)

The function only needs one parameter skb, which is the sk_buff to be released.

Return value: None.

For network devices it is best to use release functions like this:

void dev_kfree_skb (struct sk_buff *skb)

The function only needs one parameter skb, which is the sk_buff to be released.

5、skb_put、skb_push、sbk_pull 和 skb_reserve

These four functions are used to change sk_buff. Let’s take a look at the skb_put function first. This function is used to expand the data area of ​​skb_buff at the end, that is, to move the tail of skb_buff back by n bytes, thus causing the len of skb_buff to increase by n words. Section, the prototype is as follows:

unsigned char *skb_put(struct sk_buff *skb, unsigned int len)

Function parameters and return values ​​have the following meanings:

skb: The sk_buff to operate on.

len: How many bytes to add.

Return value: the first address of the extended data area.

The data area before and after the skb_put operation is shown in the figure:

The skb_push function is used to expand the data area of ​​skb_buff in the head, and the function prototype is as follows:

unsigned char *skb_push(struct sk_buff *skb, unsigned int len)

Function parameters and return values ​​have the following meanings:

skb: sk_buff to operate

len: how many bytes to add

Return value: the first address of the new data area after the expansion is completed

The data area before and after the skb_push operation is shown in the figure

The sbk_pull function is used to delete data from the beginning of the data area of ​​sk_buff. The function prototype is as follows:

unsigned char *skb_push(struct sk_buff *skb, unsigned int len)

Function parameters and return values ​​have the following meanings:

skb: The sk_buff to operate on.

len: How many bytes to add.

Return value: the first address of the new data area after the expansion is completed.

The data area before and after the skb_push operation is shown in the figure:

sbk_reserve 函数用于调整缓冲区的头部大小,方法很简单将 skb_buff 的 data 和 tail 同时后移 n 个字节即可,函数原型如下所示:

static inline void skb_reserve(struct sk_buff *skb, int len)

函数参数和返回值含义如下:

skb:要操作的 sk_buff。

len:要增加的缓冲区头部大小。

返回值:无。

网络 NAPI 处理机制

如果玩过单片机的话应该都知道,像 IIC、SPI、网络等这些通信接口,接收数据有两种方 法:轮询或中断。Linux 里面的网络数据接收也轮询和中断两种,中断的好处就是响应快,数据 量小的时候处理及时,速度快,但是一当数据量大,而且都是短帧的时候会导致中断频繁发 生,消耗大量的 CPU 处理时间在中断自身处理上。轮询恰好相反,响应没有中断及时,但是在 处理大量数据的时候不需要消耗过多的 CPU 处理时间。Linux 在这两个处理方式的基础上提出 了另外一种网络数据接收的处理方法:NAPI(New API),NAPI 是一种高效的网络处理技术。 NAPI 的核心思想就是不全部采用中断来读取网络数据,而是采用中断来唤醒数据接收服务程 序,在接收服务程序中采用 POLL 的方法来轮询处理数据。这种方法的好处就是可以提高短数 据包的接收效率,减少中断处理的时间。目前 NAPI 已经在 Linux 的网络驱动中得到了大量的 应用,NXP 官方编写的网络驱动都是采用的 NAPI 机制。 关于 NAPI 详细的处理过程本章节不讨论,本章节就简单讲解一下如何在驱动中使用 NAPI, Linux 内核使用结构体 napi_struct 表示 NAPI,在使用 NAPI 之前要先初始化一个 napi_struct 实 例。

1、初始化 NAPI

首先要初始化一个 napi_struct 实例,使用 netif_napi_add 函数,此函数定义在 net/core/dev.c

中,函数原型:

void netif_napi_add(struct net_device *dev,struct napi_struct *napi,int (*poll)(struct napi_struct *, int), int weight)

函数参数和返回值含义如下:

dev:每个 NAPI 必须关联一个网络设备,此参数指定 NAPI 要关联的网络设备

napi:要初始化的 NAPI 实例。

poll:NAPI 所使用的轮询函数,非常重要,一般在此轮询函数中完成网络数据接收的工作。

weight:NAPI 默认权重(weight),一般为 NAPI_POLL_WEIGHT。

返回值:无。

2、删除 NAPI

如果要删除 NAPI,使用 netif_napi_del 函数即可,函数原型如下:

void netif_napi_del(struct napi_struct *napi)

函数参数和返回值含义如下:

napi:要删除的 NAPI。

返回值:无。

3、使能 NAPI

初始化完 NAPI 以后,必须使能才能使用,使用函数 napi_enable,函数原型如下:

inline void napi_enable(struct napi_struct *n)

函数参数和返回值含义如下:

n:要使能的 NAPI。

返回值:无。

4、关闭 NAPI

关闭 NAPI 使用 napi_disable 函数即可,函数原型如下:

void napi_disable(struct napi_struct *n)

函数参数和返回值含义如下:

n:要关闭的 NAPI。

返回值:无。

5、检查 NAPI 是否可以进行调度

使用 napi_schedule_prep 函数检查 NAPI 是否可以进行调度,函数原型如下:

inline bool napi_schedule_prep(struct napi_struct *n)

函数参数和返回值含义如下:

n:要检查的 NAPI。

返回值:如果可以调度就返回真,如果不可调度就返回假。

6、NAPI 调度

如果可以调度的话就进行调度,使用__napi_schedule 函数完成 NAPI 调度,函数原型如下:

void __napi_schedule(struct napi_struct *n)

函数参数和返回值含义如下:

n:要调度的 NAPI。

返回值:无。

我们也可以使用 napi_schedule 函数来一次完成 napi_schedule_prep 和__napi_schedule 这两个函数的工作,napi_schedule 函数内容如下所示:

static inline void napi_schedule(struct napi_struct *n)
{
   if (napi_schedule_prep(n))
              __napi_schedule(n);
}

从上面的示例代码可以看 出 ,napi_schedule 函 数 就 是 对 napi_schedule_prep 和__napi_schedule 的简单封装,一次完成判断和调度。

7、NAPI 处理完成

NAPI 处理完成以后需要调用 napi_complete 函数来标记 NAPI 处理完成,函数原型如下:

inline void napi_complete(struct napi_struct *n)

函数参数和返回值含义如下:

n:处理完成的 NAPI。

返回值:无。

Guess you like

Origin blog.csdn.net/wanglei_11/article/details/129786381