drivers/net/tun.c

Source file repositories/reference/linux-study-clean/drivers/net/tun.c

File Facts

System
Linux kernel
Corpus path
drivers/net/tun.c
Extension
.c
Size
91396 bytes
Lines
3842
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct net_device_ops tun_netdev_ops = {
	.ndo_init		= tun_net_init,
	.ndo_uninit		= tun_net_uninit,
	.ndo_open		= tun_net_open,
	.ndo_stop		= tun_net_close,
	.ndo_start_xmit		= tun_net_xmit,
	.ndo_fix_features	= tun_net_fix_features,
	.ndo_select_queue	= tun_select_queue,
	.ndo_set_rx_headroom	= tun_set_headroom,
	.ndo_get_stats64	= tun_net_get_stats64,
	.ndo_change_carrier	= tun_net_change_carrier,
};

static void __tun_xdp_flush_tfile(struct tun_file *tfile)
{
	/* Notify and wake up reader process */
	if (tfile->flags & TUN_FASYNC)
		kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
	tfile->socket.sk->sk_data_ready(tfile->socket.sk);
}

static int tun_xdp_xmit(struct net_device *dev, int n,
			struct xdp_frame **frames, u32 flags)
{
	struct tun_struct *tun = netdev_priv(dev);
	struct tun_file *tfile;
	u32 numqueues;
	int nxmit = 0;
	int i;

	if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
		return -EINVAL;

	rcu_read_lock();

resample:
	numqueues = READ_ONCE(tun->numqueues);
	if (!numqueues) {
		rcu_read_unlock();
		return -ENXIO; /* Caller will free/return all frames */
	}

	tfile = rcu_dereference(tun->tfiles[smp_processor_id() %
					    numqueues]);
	if (unlikely(!tfile))
		goto resample;

	spin_lock(&tfile->tx_ring.producer_lock);
	for (i = 0; i < n; i++) {
		struct xdp_frame *xdp = frames[i];
		/* Encode the XDP flag into lowest bit for consumer to differ
		 * XDP buffer from sk_buff.
		 */
		void *frame = tun_xdp_to_ptr(xdp);

		if (__ptr_ring_produce(&tfile->tx_ring, frame)) {
			dev_core_stats_tx_dropped_inc(dev);
			break;
		}
		nxmit++;
	}
	spin_unlock(&tfile->tx_ring.producer_lock);

	if (flags & XDP_XMIT_FLUSH)
		__tun_xdp_flush_tfile(tfile);

	rcu_read_unlock();
	return nxmit;
}

static int tun_xdp_tx(struct net_device *dev, struct xdp_buff *xdp)
{
	struct xdp_frame *frame = xdp_convert_buff_to_frame(xdp);
	int nxmit;

	if (unlikely(!frame))
		return -EOVERFLOW;

	nxmit = tun_xdp_xmit(dev, 1, &frame, XDP_XMIT_FLUSH);
	if (!nxmit)
		xdp_return_frame_rx_napi(frame);
	return nxmit;
}

static const struct net_device_ops tap_netdev_ops = {
	.ndo_init		= tun_net_init,
	.ndo_uninit		= tun_net_uninit,
	.ndo_open		= tun_net_open,
	.ndo_stop		= tun_net_close,
	.ndo_start_xmit		= tun_net_xmit,

Annotation

Implementation Notes