18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 - 2019 Cambridge Greys Limited
48c2ecf20Sopenharmony_ci * Copyright (C) 2011 - 2014 Cisco Systems Inc
58c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
68c2ecf20Sopenharmony_ci * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
78c2ecf20Sopenharmony_ci * James Leu (jleu@mindspring.net).
88c2ecf20Sopenharmony_ci * Copyright (C) 2001 by various other people who didn't put their name here.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/version.h>
128c2ecf20Sopenharmony_ci#include <linux/memblock.h>
138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
158c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/list.h>
188c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
218c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
248c2ecf20Sopenharmony_ci#include <linux/firmware.h>
258c2ecf20Sopenharmony_ci#include <linux/fs.h>
268c2ecf20Sopenharmony_ci#include <uapi/linux/filter.h>
278c2ecf20Sopenharmony_ci#include <init.h>
288c2ecf20Sopenharmony_ci#include <irq_kern.h>
298c2ecf20Sopenharmony_ci#include <irq_user.h>
308c2ecf20Sopenharmony_ci#include <net_kern.h>
318c2ecf20Sopenharmony_ci#include <os.h>
328c2ecf20Sopenharmony_ci#include "mconsole_kern.h"
338c2ecf20Sopenharmony_ci#include "vector_user.h"
348c2ecf20Sopenharmony_ci#include "vector_kern.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Adapted from network devices with the following major changes:
388c2ecf20Sopenharmony_ci * All transports are static - simplifies the code significantly
398c2ecf20Sopenharmony_ci * Multiple FDs/IRQs per device
408c2ecf20Sopenharmony_ci * Vector IO optionally used for read/write, falling back to legacy
418c2ecf20Sopenharmony_ci * based on configuration and/or availability
428c2ecf20Sopenharmony_ci * Configuration is no longer positional - L2TPv3 and GRE require up to
438c2ecf20Sopenharmony_ci * 10 parameters, passing this as positional is not fit for purpose.
448c2ecf20Sopenharmony_ci * Only socket transports are supported
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define DRIVER_NAME "uml-vector"
498c2ecf20Sopenharmony_cistruct vector_cmd_line_arg {
508c2ecf20Sopenharmony_ci	struct list_head list;
518c2ecf20Sopenharmony_ci	int unit;
528c2ecf20Sopenharmony_ci	char *arguments;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct vector_device {
568c2ecf20Sopenharmony_ci	struct list_head list;
578c2ecf20Sopenharmony_ci	struct net_device *dev;
588c2ecf20Sopenharmony_ci	struct platform_device pdev;
598c2ecf20Sopenharmony_ci	int unit;
608c2ecf20Sopenharmony_ci	int opened;
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic LIST_HEAD(vec_cmd_line);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(vector_devices_lock);
668c2ecf20Sopenharmony_cistatic LIST_HEAD(vector_devices);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int driver_registered;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic void vector_eth_configure(int n, struct arglist *def);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* Argument accessors to set variables (and/or set default values)
738c2ecf20Sopenharmony_ci * mtu, buffer sizing, default headroom, etc
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define DEFAULT_HEADROOM 2
778c2ecf20Sopenharmony_ci#define SAFETY_MARGIN 32
788c2ecf20Sopenharmony_ci#define DEFAULT_VECTOR_SIZE 64
798c2ecf20Sopenharmony_ci#define TX_SMALL_PACKET 128
808c2ecf20Sopenharmony_ci#define MAX_IOV_SIZE (MAX_SKB_FRAGS + 1)
818c2ecf20Sopenharmony_ci#define MAX_ITERATIONS 64
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic const struct {
848c2ecf20Sopenharmony_ci	const char string[ETH_GSTRING_LEN];
858c2ecf20Sopenharmony_ci} ethtool_stats_keys[] = {
868c2ecf20Sopenharmony_ci	{ "rx_queue_max" },
878c2ecf20Sopenharmony_ci	{ "rx_queue_running_average" },
888c2ecf20Sopenharmony_ci	{ "tx_queue_max" },
898c2ecf20Sopenharmony_ci	{ "tx_queue_running_average" },
908c2ecf20Sopenharmony_ci	{ "rx_encaps_errors" },
918c2ecf20Sopenharmony_ci	{ "tx_timeout_count" },
928c2ecf20Sopenharmony_ci	{ "tx_restart_queue" },
938c2ecf20Sopenharmony_ci	{ "tx_kicks" },
948c2ecf20Sopenharmony_ci	{ "tx_flow_control_xon" },
958c2ecf20Sopenharmony_ci	{ "tx_flow_control_xoff" },
968c2ecf20Sopenharmony_ci	{ "rx_csum_offload_good" },
978c2ecf20Sopenharmony_ci	{ "rx_csum_offload_errors"},
988c2ecf20Sopenharmony_ci	{ "sg_ok"},
998c2ecf20Sopenharmony_ci	{ "sg_linearized"},
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define VECTOR_NUM_STATS	ARRAY_SIZE(ethtool_stats_keys)
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void vector_reset_stats(struct vector_private *vp)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	vp->estats.rx_queue_max = 0;
1078c2ecf20Sopenharmony_ci	vp->estats.rx_queue_running_average = 0;
1088c2ecf20Sopenharmony_ci	vp->estats.tx_queue_max = 0;
1098c2ecf20Sopenharmony_ci	vp->estats.tx_queue_running_average = 0;
1108c2ecf20Sopenharmony_ci	vp->estats.rx_encaps_errors = 0;
1118c2ecf20Sopenharmony_ci	vp->estats.tx_timeout_count = 0;
1128c2ecf20Sopenharmony_ci	vp->estats.tx_restart_queue = 0;
1138c2ecf20Sopenharmony_ci	vp->estats.tx_kicks = 0;
1148c2ecf20Sopenharmony_ci	vp->estats.tx_flow_control_xon = 0;
1158c2ecf20Sopenharmony_ci	vp->estats.tx_flow_control_xoff = 0;
1168c2ecf20Sopenharmony_ci	vp->estats.sg_ok = 0;
1178c2ecf20Sopenharmony_ci	vp->estats.sg_linearized = 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int get_mtu(struct arglist *def)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	char *mtu = uml_vector_fetch_arg(def, "mtu");
1238c2ecf20Sopenharmony_ci	long result;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (mtu != NULL) {
1268c2ecf20Sopenharmony_ci		if (kstrtoul(mtu, 10, &result) == 0)
1278c2ecf20Sopenharmony_ci			if ((result < (1 << 16) - 1) && (result >= 576))
1288c2ecf20Sopenharmony_ci				return result;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	return ETH_MAX_PACKET;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic char *get_bpf_file(struct arglist *def)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return uml_vector_fetch_arg(def, "bpffile");
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic bool get_bpf_flash(struct arglist *def)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	char *allow = uml_vector_fetch_arg(def, "bpfflash");
1418c2ecf20Sopenharmony_ci	long result;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (allow != NULL) {
1448c2ecf20Sopenharmony_ci		if (kstrtoul(allow, 10, &result) == 0)
1458c2ecf20Sopenharmony_ci			return (allow > 0);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci	return false;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int get_depth(struct arglist *def)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	char *mtu = uml_vector_fetch_arg(def, "depth");
1538c2ecf20Sopenharmony_ci	long result;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (mtu != NULL) {
1568c2ecf20Sopenharmony_ci		if (kstrtoul(mtu, 10, &result) == 0)
1578c2ecf20Sopenharmony_ci			return result;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	return DEFAULT_VECTOR_SIZE;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int get_headroom(struct arglist *def)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	char *mtu = uml_vector_fetch_arg(def, "headroom");
1658c2ecf20Sopenharmony_ci	long result;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (mtu != NULL) {
1688c2ecf20Sopenharmony_ci		if (kstrtoul(mtu, 10, &result) == 0)
1698c2ecf20Sopenharmony_ci			return result;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	return DEFAULT_HEADROOM;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int get_req_size(struct arglist *def)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	char *gro = uml_vector_fetch_arg(def, "gro");
1778c2ecf20Sopenharmony_ci	long result;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (gro != NULL) {
1808c2ecf20Sopenharmony_ci		if (kstrtoul(gro, 10, &result) == 0) {
1818c2ecf20Sopenharmony_ci			if (result > 0)
1828c2ecf20Sopenharmony_ci				return 65536;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	return get_mtu(def) + ETH_HEADER_OTHER +
1868c2ecf20Sopenharmony_ci		get_headroom(def) + SAFETY_MARGIN;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic int get_transport_options(struct arglist *def)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	char *transport = uml_vector_fetch_arg(def, "transport");
1938c2ecf20Sopenharmony_ci	char *vector = uml_vector_fetch_arg(def, "vec");
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	int vec_rx = VECTOR_RX;
1968c2ecf20Sopenharmony_ci	int vec_tx = VECTOR_TX;
1978c2ecf20Sopenharmony_ci	long parsed;
1988c2ecf20Sopenharmony_ci	int result = 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (transport == NULL)
2018c2ecf20Sopenharmony_ci		return -EINVAL;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (vector != NULL) {
2048c2ecf20Sopenharmony_ci		if (kstrtoul(vector, 10, &parsed) == 0) {
2058c2ecf20Sopenharmony_ci			if (parsed == 0) {
2068c2ecf20Sopenharmony_ci				vec_rx = 0;
2078c2ecf20Sopenharmony_ci				vec_tx = 0;
2088c2ecf20Sopenharmony_ci			}
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (get_bpf_flash(def))
2138c2ecf20Sopenharmony_ci		result = VECTOR_BPF_FLASH;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
2168c2ecf20Sopenharmony_ci		return result;
2178c2ecf20Sopenharmony_ci	if (strncmp(transport, TRANS_HYBRID, TRANS_HYBRID_LEN) == 0)
2188c2ecf20Sopenharmony_ci		return (result | vec_rx | VECTOR_BPF);
2198c2ecf20Sopenharmony_ci	if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
2208c2ecf20Sopenharmony_ci		return (result | vec_rx | vec_tx | VECTOR_QDISC_BYPASS);
2218c2ecf20Sopenharmony_ci	return (result | vec_rx | vec_tx);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/* A mini-buffer for packet drop read
2268c2ecf20Sopenharmony_ci * All of our supported transports are datagram oriented and we always
2278c2ecf20Sopenharmony_ci * read using recvmsg or recvmmsg. If we pass a buffer which is smaller
2288c2ecf20Sopenharmony_ci * than the packet size it still counts as full packet read and will
2298c2ecf20Sopenharmony_ci * clean the incoming stream to keep sigio/epoll happy
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci#define DROP_BUFFER_SIZE 32
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic char *drop_buffer;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/* Array backed queues optimized for bulk enqueue/dequeue and
2378c2ecf20Sopenharmony_ci * 1:N (small values of N) or 1:1 enqueuer/dequeuer ratios.
2388c2ecf20Sopenharmony_ci * For more details and full design rationale see
2398c2ecf20Sopenharmony_ci * http://foswiki.cambridgegreys.com/Main/EatYourTailAndEnjoyIt
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/*
2448c2ecf20Sopenharmony_ci * Advance the mmsg queue head by n = advance. Resets the queue to
2458c2ecf20Sopenharmony_ci * maximum enqueue/dequeue-at-once capacity if possible. Called by
2468c2ecf20Sopenharmony_ci * dequeuers. Caller must hold the head_lock!
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int vector_advancehead(struct vector_queue *qi, int advance)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	int queue_depth;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	qi->head =
2548c2ecf20Sopenharmony_ci		(qi->head + advance)
2558c2ecf20Sopenharmony_ci			% qi->max_depth;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	spin_lock(&qi->tail_lock);
2598c2ecf20Sopenharmony_ci	qi->queue_depth -= advance;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* we are at 0, use this to
2628c2ecf20Sopenharmony_ci	 * reset head and tail so we can use max size vectors
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (qi->queue_depth == 0) {
2668c2ecf20Sopenharmony_ci		qi->head = 0;
2678c2ecf20Sopenharmony_ci		qi->tail = 0;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	queue_depth = qi->queue_depth;
2708c2ecf20Sopenharmony_ci	spin_unlock(&qi->tail_lock);
2718c2ecf20Sopenharmony_ci	return queue_depth;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/*	Advance the queue tail by n = advance.
2758c2ecf20Sopenharmony_ci *	This is called by enqueuers which should hold the
2768c2ecf20Sopenharmony_ci *	head lock already
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int vector_advancetail(struct vector_queue *qi, int advance)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	int queue_depth;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	qi->tail =
2848c2ecf20Sopenharmony_ci		(qi->tail + advance)
2858c2ecf20Sopenharmony_ci			% qi->max_depth;
2868c2ecf20Sopenharmony_ci	spin_lock(&qi->head_lock);
2878c2ecf20Sopenharmony_ci	qi->queue_depth += advance;
2888c2ecf20Sopenharmony_ci	queue_depth = qi->queue_depth;
2898c2ecf20Sopenharmony_ci	spin_unlock(&qi->head_lock);
2908c2ecf20Sopenharmony_ci	return queue_depth;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int prep_msg(struct vector_private *vp,
2948c2ecf20Sopenharmony_ci	struct sk_buff *skb,
2958c2ecf20Sopenharmony_ci	struct iovec *iov)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int iov_index = 0;
2988c2ecf20Sopenharmony_ci	int nr_frags, frag;
2998c2ecf20Sopenharmony_ci	skb_frag_t *skb_frag;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
3028c2ecf20Sopenharmony_ci	if (nr_frags > MAX_IOV_SIZE) {
3038c2ecf20Sopenharmony_ci		if (skb_linearize(skb) != 0)
3048c2ecf20Sopenharmony_ci			goto drop;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci	if (vp->header_size > 0) {
3078c2ecf20Sopenharmony_ci		iov[iov_index].iov_len = vp->header_size;
3088c2ecf20Sopenharmony_ci		vp->form_header(iov[iov_index].iov_base, skb, vp);
3098c2ecf20Sopenharmony_ci		iov_index++;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	iov[iov_index].iov_base = skb->data;
3128c2ecf20Sopenharmony_ci	if (nr_frags > 0) {
3138c2ecf20Sopenharmony_ci		iov[iov_index].iov_len = skb->len - skb->data_len;
3148c2ecf20Sopenharmony_ci		vp->estats.sg_ok++;
3158c2ecf20Sopenharmony_ci	} else
3168c2ecf20Sopenharmony_ci		iov[iov_index].iov_len = skb->len;
3178c2ecf20Sopenharmony_ci	iov_index++;
3188c2ecf20Sopenharmony_ci	for (frag = 0; frag < nr_frags; frag++) {
3198c2ecf20Sopenharmony_ci		skb_frag = &skb_shinfo(skb)->frags[frag];
3208c2ecf20Sopenharmony_ci		iov[iov_index].iov_base = skb_frag_address_safe(skb_frag);
3218c2ecf20Sopenharmony_ci		iov[iov_index].iov_len = skb_frag_size(skb_frag);
3228c2ecf20Sopenharmony_ci		iov_index++;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci	return iov_index;
3258c2ecf20Sopenharmony_cidrop:
3268c2ecf20Sopenharmony_ci	return -1;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci/*
3298c2ecf20Sopenharmony_ci * Generic vector enqueue with support for forming headers using transport
3308c2ecf20Sopenharmony_ci * specific callback. Allows GRE, L2TPv3, RAW and other transports
3318c2ecf20Sopenharmony_ci * to use a common enqueue procedure in vector mode
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int vector_enqueue(struct vector_queue *qi, struct sk_buff *skb)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(qi->dev);
3378c2ecf20Sopenharmony_ci	int queue_depth;
3388c2ecf20Sopenharmony_ci	int packet_len;
3398c2ecf20Sopenharmony_ci	struct mmsghdr *mmsg_vector = qi->mmsg_vector;
3408c2ecf20Sopenharmony_ci	int iov_count;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	spin_lock(&qi->tail_lock);
3438c2ecf20Sopenharmony_ci	spin_lock(&qi->head_lock);
3448c2ecf20Sopenharmony_ci	queue_depth = qi->queue_depth;
3458c2ecf20Sopenharmony_ci	spin_unlock(&qi->head_lock);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (skb)
3488c2ecf20Sopenharmony_ci		packet_len = skb->len;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (queue_depth < qi->max_depth) {
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		*(qi->skbuff_vector + qi->tail) = skb;
3538c2ecf20Sopenharmony_ci		mmsg_vector += qi->tail;
3548c2ecf20Sopenharmony_ci		iov_count = prep_msg(
3558c2ecf20Sopenharmony_ci			vp,
3568c2ecf20Sopenharmony_ci			skb,
3578c2ecf20Sopenharmony_ci			mmsg_vector->msg_hdr.msg_iov
3588c2ecf20Sopenharmony_ci		);
3598c2ecf20Sopenharmony_ci		if (iov_count < 1)
3608c2ecf20Sopenharmony_ci			goto drop;
3618c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_iovlen = iov_count;
3628c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_name = vp->fds->remote_addr;
3638c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_namelen = vp->fds->remote_addr_size;
3648c2ecf20Sopenharmony_ci		queue_depth = vector_advancetail(qi, 1);
3658c2ecf20Sopenharmony_ci	} else
3668c2ecf20Sopenharmony_ci		goto drop;
3678c2ecf20Sopenharmony_ci	spin_unlock(&qi->tail_lock);
3688c2ecf20Sopenharmony_ci	return queue_depth;
3698c2ecf20Sopenharmony_cidrop:
3708c2ecf20Sopenharmony_ci	qi->dev->stats.tx_dropped++;
3718c2ecf20Sopenharmony_ci	if (skb != NULL) {
3728c2ecf20Sopenharmony_ci		packet_len = skb->len;
3738c2ecf20Sopenharmony_ci		dev_consume_skb_any(skb);
3748c2ecf20Sopenharmony_ci		netdev_completed_queue(qi->dev, 1, packet_len);
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci	spin_unlock(&qi->tail_lock);
3778c2ecf20Sopenharmony_ci	return queue_depth;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int consume_vector_skbs(struct vector_queue *qi, int count)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3838c2ecf20Sopenharmony_ci	int skb_index;
3848c2ecf20Sopenharmony_ci	int bytes_compl = 0;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	for (skb_index = qi->head; skb_index < qi->head + count; skb_index++) {
3878c2ecf20Sopenharmony_ci		skb = *(qi->skbuff_vector + skb_index);
3888c2ecf20Sopenharmony_ci		/* mark as empty to ensure correct destruction if
3898c2ecf20Sopenharmony_ci		 * needed
3908c2ecf20Sopenharmony_ci		 */
3918c2ecf20Sopenharmony_ci		bytes_compl += skb->len;
3928c2ecf20Sopenharmony_ci		*(qi->skbuff_vector + skb_index) = NULL;
3938c2ecf20Sopenharmony_ci		dev_consume_skb_any(skb);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci	qi->dev->stats.tx_bytes += bytes_compl;
3968c2ecf20Sopenharmony_ci	qi->dev->stats.tx_packets += count;
3978c2ecf20Sopenharmony_ci	netdev_completed_queue(qi->dev, count, bytes_compl);
3988c2ecf20Sopenharmony_ci	return vector_advancehead(qi, count);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/*
4028c2ecf20Sopenharmony_ci * Generic vector deque via sendmmsg with support for forming headers
4038c2ecf20Sopenharmony_ci * using transport specific callback. Allows GRE, L2TPv3, RAW and
4048c2ecf20Sopenharmony_ci * other transports to use a common dequeue procedure in vector mode
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int vector_send(struct vector_queue *qi)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(qi->dev);
4118c2ecf20Sopenharmony_ci	struct mmsghdr *send_from;
4128c2ecf20Sopenharmony_ci	int result = 0, send_len, queue_depth = qi->max_depth;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (spin_trylock(&qi->head_lock)) {
4158c2ecf20Sopenharmony_ci		if (spin_trylock(&qi->tail_lock)) {
4168c2ecf20Sopenharmony_ci			/* update queue_depth to current value */
4178c2ecf20Sopenharmony_ci			queue_depth = qi->queue_depth;
4188c2ecf20Sopenharmony_ci			spin_unlock(&qi->tail_lock);
4198c2ecf20Sopenharmony_ci			while (queue_depth > 0) {
4208c2ecf20Sopenharmony_ci				/* Calculate the start of the vector */
4218c2ecf20Sopenharmony_ci				send_len = queue_depth;
4228c2ecf20Sopenharmony_ci				send_from = qi->mmsg_vector;
4238c2ecf20Sopenharmony_ci				send_from += qi->head;
4248c2ecf20Sopenharmony_ci				/* Adjust vector size if wraparound */
4258c2ecf20Sopenharmony_ci				if (send_len + qi->head > qi->max_depth)
4268c2ecf20Sopenharmony_ci					send_len = qi->max_depth - qi->head;
4278c2ecf20Sopenharmony_ci				/* Try to TX as many packets as possible */
4288c2ecf20Sopenharmony_ci				if (send_len > 0) {
4298c2ecf20Sopenharmony_ci					result = uml_vector_sendmmsg(
4308c2ecf20Sopenharmony_ci						 vp->fds->tx_fd,
4318c2ecf20Sopenharmony_ci						 send_from,
4328c2ecf20Sopenharmony_ci						 send_len,
4338c2ecf20Sopenharmony_ci						 0
4348c2ecf20Sopenharmony_ci					);
4358c2ecf20Sopenharmony_ci					vp->in_write_poll =
4368c2ecf20Sopenharmony_ci						(result != send_len);
4378c2ecf20Sopenharmony_ci				}
4388c2ecf20Sopenharmony_ci				/* For some of the sendmmsg error scenarios
4398c2ecf20Sopenharmony_ci				 * we may end being unsure in the TX success
4408c2ecf20Sopenharmony_ci				 * for all packets. It is safer to declare
4418c2ecf20Sopenharmony_ci				 * them all TX-ed and blame the network.
4428c2ecf20Sopenharmony_ci				 */
4438c2ecf20Sopenharmony_ci				if (result < 0) {
4448c2ecf20Sopenharmony_ci					if (net_ratelimit())
4458c2ecf20Sopenharmony_ci						netdev_err(vp->dev, "sendmmsg err=%i\n",
4468c2ecf20Sopenharmony_ci							result);
4478c2ecf20Sopenharmony_ci					vp->in_error = true;
4488c2ecf20Sopenharmony_ci					result = send_len;
4498c2ecf20Sopenharmony_ci				}
4508c2ecf20Sopenharmony_ci				if (result > 0) {
4518c2ecf20Sopenharmony_ci					queue_depth =
4528c2ecf20Sopenharmony_ci						consume_vector_skbs(qi, result);
4538c2ecf20Sopenharmony_ci					/* This is equivalent to an TX IRQ.
4548c2ecf20Sopenharmony_ci					 * Restart the upper layers to feed us
4558c2ecf20Sopenharmony_ci					 * more packets.
4568c2ecf20Sopenharmony_ci					 */
4578c2ecf20Sopenharmony_ci					if (result > vp->estats.tx_queue_max)
4588c2ecf20Sopenharmony_ci						vp->estats.tx_queue_max = result;
4598c2ecf20Sopenharmony_ci					vp->estats.tx_queue_running_average =
4608c2ecf20Sopenharmony_ci						(vp->estats.tx_queue_running_average + result) >> 1;
4618c2ecf20Sopenharmony_ci				}
4628c2ecf20Sopenharmony_ci				netif_trans_update(qi->dev);
4638c2ecf20Sopenharmony_ci				netif_wake_queue(qi->dev);
4648c2ecf20Sopenharmony_ci				/* if TX is busy, break out of the send loop,
4658c2ecf20Sopenharmony_ci				 *  poll write IRQ will reschedule xmit for us
4668c2ecf20Sopenharmony_ci				 */
4678c2ecf20Sopenharmony_ci				if (result != send_len) {
4688c2ecf20Sopenharmony_ci					vp->estats.tx_restart_queue++;
4698c2ecf20Sopenharmony_ci					break;
4708c2ecf20Sopenharmony_ci				}
4718c2ecf20Sopenharmony_ci			}
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci		spin_unlock(&qi->head_lock);
4748c2ecf20Sopenharmony_ci	} else {
4758c2ecf20Sopenharmony_ci		tasklet_schedule(&vp->tx_poll);
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	return queue_depth;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci/* Queue destructor. Deliberately stateless so we can use
4818c2ecf20Sopenharmony_ci * it in queue cleanup if initialization fails.
4828c2ecf20Sopenharmony_ci */
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic void destroy_queue(struct vector_queue *qi)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	int i;
4878c2ecf20Sopenharmony_ci	struct iovec *iov;
4888c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(qi->dev);
4898c2ecf20Sopenharmony_ci	struct mmsghdr *mmsg_vector;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (qi == NULL)
4928c2ecf20Sopenharmony_ci		return;
4938c2ecf20Sopenharmony_ci	/* deallocate any skbuffs - we rely on any unused to be
4948c2ecf20Sopenharmony_ci	 * set to NULL.
4958c2ecf20Sopenharmony_ci	 */
4968c2ecf20Sopenharmony_ci	if (qi->skbuff_vector != NULL) {
4978c2ecf20Sopenharmony_ci		for (i = 0; i < qi->max_depth; i++) {
4988c2ecf20Sopenharmony_ci			if (*(qi->skbuff_vector + i) != NULL)
4998c2ecf20Sopenharmony_ci				dev_kfree_skb_any(*(qi->skbuff_vector + i));
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci		kfree(qi->skbuff_vector);
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci	/* deallocate matching IOV structures including header buffs */
5048c2ecf20Sopenharmony_ci	if (qi->mmsg_vector != NULL) {
5058c2ecf20Sopenharmony_ci		mmsg_vector = qi->mmsg_vector;
5068c2ecf20Sopenharmony_ci		for (i = 0; i < qi->max_depth; i++) {
5078c2ecf20Sopenharmony_ci			iov = mmsg_vector->msg_hdr.msg_iov;
5088c2ecf20Sopenharmony_ci			if (iov != NULL) {
5098c2ecf20Sopenharmony_ci				if ((vp->header_size > 0) &&
5108c2ecf20Sopenharmony_ci					(iov->iov_base != NULL))
5118c2ecf20Sopenharmony_ci					kfree(iov->iov_base);
5128c2ecf20Sopenharmony_ci				kfree(iov);
5138c2ecf20Sopenharmony_ci			}
5148c2ecf20Sopenharmony_ci			mmsg_vector++;
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci		kfree(qi->mmsg_vector);
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci	kfree(qi);
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci/*
5228c2ecf20Sopenharmony_ci * Queue constructor. Create a queue with a given side.
5238c2ecf20Sopenharmony_ci */
5248c2ecf20Sopenharmony_cistatic struct vector_queue *create_queue(
5258c2ecf20Sopenharmony_ci	struct vector_private *vp,
5268c2ecf20Sopenharmony_ci	int max_size,
5278c2ecf20Sopenharmony_ci	int header_size,
5288c2ecf20Sopenharmony_ci	int num_extra_frags)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	struct vector_queue *result;
5318c2ecf20Sopenharmony_ci	int i;
5328c2ecf20Sopenharmony_ci	struct iovec *iov;
5338c2ecf20Sopenharmony_ci	struct mmsghdr *mmsg_vector;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	result = kmalloc(sizeof(struct vector_queue), GFP_KERNEL);
5368c2ecf20Sopenharmony_ci	if (result == NULL)
5378c2ecf20Sopenharmony_ci		return NULL;
5388c2ecf20Sopenharmony_ci	result->max_depth = max_size;
5398c2ecf20Sopenharmony_ci	result->dev = vp->dev;
5408c2ecf20Sopenharmony_ci	result->mmsg_vector = kmalloc(
5418c2ecf20Sopenharmony_ci		(sizeof(struct mmsghdr) * max_size), GFP_KERNEL);
5428c2ecf20Sopenharmony_ci	if (result->mmsg_vector == NULL)
5438c2ecf20Sopenharmony_ci		goto out_mmsg_fail;
5448c2ecf20Sopenharmony_ci	result->skbuff_vector = kmalloc(
5458c2ecf20Sopenharmony_ci		(sizeof(void *) * max_size), GFP_KERNEL);
5468c2ecf20Sopenharmony_ci	if (result->skbuff_vector == NULL)
5478c2ecf20Sopenharmony_ci		goto out_skb_fail;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/* further failures can be handled safely by destroy_queue*/
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	mmsg_vector = result->mmsg_vector;
5528c2ecf20Sopenharmony_ci	for (i = 0; i < max_size; i++) {
5538c2ecf20Sopenharmony_ci		/* Clear all pointers - we use non-NULL as marking on
5548c2ecf20Sopenharmony_ci		 * what to free on destruction
5558c2ecf20Sopenharmony_ci		 */
5568c2ecf20Sopenharmony_ci		*(result->skbuff_vector + i) = NULL;
5578c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_iov = NULL;
5588c2ecf20Sopenharmony_ci		mmsg_vector++;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci	mmsg_vector = result->mmsg_vector;
5618c2ecf20Sopenharmony_ci	result->max_iov_frags = num_extra_frags;
5628c2ecf20Sopenharmony_ci	for (i = 0; i < max_size; i++) {
5638c2ecf20Sopenharmony_ci		if (vp->header_size > 0)
5648c2ecf20Sopenharmony_ci			iov = kmalloc_array(3 + num_extra_frags,
5658c2ecf20Sopenharmony_ci					    sizeof(struct iovec),
5668c2ecf20Sopenharmony_ci					    GFP_KERNEL
5678c2ecf20Sopenharmony_ci			);
5688c2ecf20Sopenharmony_ci		else
5698c2ecf20Sopenharmony_ci			iov = kmalloc_array(2 + num_extra_frags,
5708c2ecf20Sopenharmony_ci					    sizeof(struct iovec),
5718c2ecf20Sopenharmony_ci					    GFP_KERNEL
5728c2ecf20Sopenharmony_ci			);
5738c2ecf20Sopenharmony_ci		if (iov == NULL)
5748c2ecf20Sopenharmony_ci			goto out_fail;
5758c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_iov = iov;
5768c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_iovlen = 1;
5778c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_control = NULL;
5788c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_controllen = 0;
5798c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_flags = MSG_DONTWAIT;
5808c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_name = NULL;
5818c2ecf20Sopenharmony_ci		mmsg_vector->msg_hdr.msg_namelen = 0;
5828c2ecf20Sopenharmony_ci		if (vp->header_size > 0) {
5838c2ecf20Sopenharmony_ci			iov->iov_base = kmalloc(header_size, GFP_KERNEL);
5848c2ecf20Sopenharmony_ci			if (iov->iov_base == NULL)
5858c2ecf20Sopenharmony_ci				goto out_fail;
5868c2ecf20Sopenharmony_ci			iov->iov_len = header_size;
5878c2ecf20Sopenharmony_ci			mmsg_vector->msg_hdr.msg_iovlen = 2;
5888c2ecf20Sopenharmony_ci			iov++;
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci		iov->iov_base = NULL;
5918c2ecf20Sopenharmony_ci		iov->iov_len = 0;
5928c2ecf20Sopenharmony_ci		mmsg_vector++;
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci	spin_lock_init(&result->head_lock);
5958c2ecf20Sopenharmony_ci	spin_lock_init(&result->tail_lock);
5968c2ecf20Sopenharmony_ci	result->queue_depth = 0;
5978c2ecf20Sopenharmony_ci	result->head = 0;
5988c2ecf20Sopenharmony_ci	result->tail = 0;
5998c2ecf20Sopenharmony_ci	return result;
6008c2ecf20Sopenharmony_ciout_skb_fail:
6018c2ecf20Sopenharmony_ci	kfree(result->mmsg_vector);
6028c2ecf20Sopenharmony_ciout_mmsg_fail:
6038c2ecf20Sopenharmony_ci	kfree(result);
6048c2ecf20Sopenharmony_ci	return NULL;
6058c2ecf20Sopenharmony_ciout_fail:
6068c2ecf20Sopenharmony_ci	destroy_queue(result);
6078c2ecf20Sopenharmony_ci	return NULL;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci/*
6118c2ecf20Sopenharmony_ci * We do not use the RX queue as a proper wraparound queue for now
6128c2ecf20Sopenharmony_ci * This is not necessary because the consumption via netif_rx()
6138c2ecf20Sopenharmony_ci * happens in-line. While we can try using the return code of
6148c2ecf20Sopenharmony_ci * netif_rx() for flow control there are no drivers doing this today.
6158c2ecf20Sopenharmony_ci * For this RX specific use we ignore the tail/head locks and
6168c2ecf20Sopenharmony_ci * just read into a prepared queue filled with skbuffs.
6178c2ecf20Sopenharmony_ci */
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic struct sk_buff *prep_skb(
6208c2ecf20Sopenharmony_ci	struct vector_private *vp,
6218c2ecf20Sopenharmony_ci	struct user_msghdr *msg)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	int linear = vp->max_packet + vp->headroom + SAFETY_MARGIN;
6248c2ecf20Sopenharmony_ci	struct sk_buff *result;
6258c2ecf20Sopenharmony_ci	int iov_index = 0, len;
6268c2ecf20Sopenharmony_ci	struct iovec *iov = msg->msg_iov;
6278c2ecf20Sopenharmony_ci	int err, nr_frags, frag;
6288c2ecf20Sopenharmony_ci	skb_frag_t *skb_frag;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (vp->req_size <= linear)
6318c2ecf20Sopenharmony_ci		len = linear;
6328c2ecf20Sopenharmony_ci	else
6338c2ecf20Sopenharmony_ci		len = vp->req_size;
6348c2ecf20Sopenharmony_ci	result = alloc_skb_with_frags(
6358c2ecf20Sopenharmony_ci		linear,
6368c2ecf20Sopenharmony_ci		len - vp->max_packet,
6378c2ecf20Sopenharmony_ci		3,
6388c2ecf20Sopenharmony_ci		&err,
6398c2ecf20Sopenharmony_ci		GFP_ATOMIC
6408c2ecf20Sopenharmony_ci	);
6418c2ecf20Sopenharmony_ci	if (vp->header_size > 0)
6428c2ecf20Sopenharmony_ci		iov_index++;
6438c2ecf20Sopenharmony_ci	if (result == NULL) {
6448c2ecf20Sopenharmony_ci		iov[iov_index].iov_base = NULL;
6458c2ecf20Sopenharmony_ci		iov[iov_index].iov_len = 0;
6468c2ecf20Sopenharmony_ci		goto done;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci	skb_reserve(result, vp->headroom);
6498c2ecf20Sopenharmony_ci	result->dev = vp->dev;
6508c2ecf20Sopenharmony_ci	skb_put(result, vp->max_packet);
6518c2ecf20Sopenharmony_ci	result->data_len = len - vp->max_packet;
6528c2ecf20Sopenharmony_ci	result->len += len - vp->max_packet;
6538c2ecf20Sopenharmony_ci	skb_reset_mac_header(result);
6548c2ecf20Sopenharmony_ci	result->ip_summed = CHECKSUM_NONE;
6558c2ecf20Sopenharmony_ci	iov[iov_index].iov_base = result->data;
6568c2ecf20Sopenharmony_ci	iov[iov_index].iov_len = vp->max_packet;
6578c2ecf20Sopenharmony_ci	iov_index++;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	nr_frags = skb_shinfo(result)->nr_frags;
6608c2ecf20Sopenharmony_ci	for (frag = 0; frag < nr_frags; frag++) {
6618c2ecf20Sopenharmony_ci		skb_frag = &skb_shinfo(result)->frags[frag];
6628c2ecf20Sopenharmony_ci		iov[iov_index].iov_base = skb_frag_address_safe(skb_frag);
6638c2ecf20Sopenharmony_ci		if (iov[iov_index].iov_base != NULL)
6648c2ecf20Sopenharmony_ci			iov[iov_index].iov_len = skb_frag_size(skb_frag);
6658c2ecf20Sopenharmony_ci		else
6668c2ecf20Sopenharmony_ci			iov[iov_index].iov_len = 0;
6678c2ecf20Sopenharmony_ci		iov_index++;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_cidone:
6708c2ecf20Sopenharmony_ci	msg->msg_iovlen = iov_index;
6718c2ecf20Sopenharmony_ci	return result;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci/* Prepare queue for recvmmsg one-shot rx - fill with fresh sk_buffs*/
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic void prep_queue_for_rx(struct vector_queue *qi)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(qi->dev);
6808c2ecf20Sopenharmony_ci	struct mmsghdr *mmsg_vector = qi->mmsg_vector;
6818c2ecf20Sopenharmony_ci	void **skbuff_vector = qi->skbuff_vector;
6828c2ecf20Sopenharmony_ci	int i;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (qi->queue_depth == 0)
6858c2ecf20Sopenharmony_ci		return;
6868c2ecf20Sopenharmony_ci	for (i = 0; i < qi->queue_depth; i++) {
6878c2ecf20Sopenharmony_ci		/* it is OK if allocation fails - recvmmsg with NULL data in
6888c2ecf20Sopenharmony_ci		 * iov argument still performs an RX, just drops the packet
6898c2ecf20Sopenharmony_ci		 * This allows us stop faffing around with a "drop buffer"
6908c2ecf20Sopenharmony_ci		 */
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		*skbuff_vector = prep_skb(vp, &mmsg_vector->msg_hdr);
6938c2ecf20Sopenharmony_ci		skbuff_vector++;
6948c2ecf20Sopenharmony_ci		mmsg_vector++;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci	qi->queue_depth = 0;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic struct vector_device *find_device(int n)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct vector_device *device;
7028c2ecf20Sopenharmony_ci	struct list_head *ele;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	spin_lock(&vector_devices_lock);
7058c2ecf20Sopenharmony_ci	list_for_each(ele, &vector_devices) {
7068c2ecf20Sopenharmony_ci		device = list_entry(ele, struct vector_device, list);
7078c2ecf20Sopenharmony_ci		if (device->unit == n)
7088c2ecf20Sopenharmony_ci			goto out;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci	device = NULL;
7118c2ecf20Sopenharmony_ci out:
7128c2ecf20Sopenharmony_ci	spin_unlock(&vector_devices_lock);
7138c2ecf20Sopenharmony_ci	return device;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic int vector_parse(char *str, int *index_out, char **str_out,
7178c2ecf20Sopenharmony_ci			char **error_out)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	int n, len, err;
7208c2ecf20Sopenharmony_ci	char *start = str;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	len = strlen(str);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	while ((*str != ':') && (strlen(str) > 1))
7258c2ecf20Sopenharmony_ci		str++;
7268c2ecf20Sopenharmony_ci	if (*str != ':') {
7278c2ecf20Sopenharmony_ci		*error_out = "Expected ':' after device number";
7288c2ecf20Sopenharmony_ci		return -EINVAL;
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci	*str = '\0';
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	err = kstrtouint(start, 0, &n);
7338c2ecf20Sopenharmony_ci	if (err < 0) {
7348c2ecf20Sopenharmony_ci		*error_out = "Bad device number";
7358c2ecf20Sopenharmony_ci		return err;
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	str++;
7398c2ecf20Sopenharmony_ci	if (find_device(n)) {
7408c2ecf20Sopenharmony_ci		*error_out = "Device already configured";
7418c2ecf20Sopenharmony_ci		return -EINVAL;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	*index_out = n;
7458c2ecf20Sopenharmony_ci	*str_out = str;
7468c2ecf20Sopenharmony_ci	return 0;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int vector_config(char *str, char **error_out)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	int err, n;
7528c2ecf20Sopenharmony_ci	char *params;
7538c2ecf20Sopenharmony_ci	struct arglist *parsed;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	err = vector_parse(str, &n, &params, error_out);
7568c2ecf20Sopenharmony_ci	if (err != 0)
7578c2ecf20Sopenharmony_ci		return err;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* This string is broken up and the pieces used by the underlying
7608c2ecf20Sopenharmony_ci	 * driver. We should copy it to make sure things do not go wrong
7618c2ecf20Sopenharmony_ci	 * later.
7628c2ecf20Sopenharmony_ci	 */
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	params = kstrdup(params, GFP_KERNEL);
7658c2ecf20Sopenharmony_ci	if (params == NULL) {
7668c2ecf20Sopenharmony_ci		*error_out = "vector_config failed to strdup string";
7678c2ecf20Sopenharmony_ci		return -ENOMEM;
7688c2ecf20Sopenharmony_ci	}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	parsed = uml_parse_vector_ifspec(params);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (parsed == NULL) {
7738c2ecf20Sopenharmony_ci		*error_out = "vector_config failed to parse parameters";
7748c2ecf20Sopenharmony_ci		kfree(params);
7758c2ecf20Sopenharmony_ci		return -EINVAL;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	vector_eth_configure(n, parsed);
7798c2ecf20Sopenharmony_ci	return 0;
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cistatic int vector_id(char **str, int *start_out, int *end_out)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	char *end;
7858c2ecf20Sopenharmony_ci	int n;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	n = simple_strtoul(*str, &end, 0);
7888c2ecf20Sopenharmony_ci	if ((*end != '\0') || (end == *str))
7898c2ecf20Sopenharmony_ci		return -1;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	*start_out = n;
7928c2ecf20Sopenharmony_ci	*end_out = n;
7938c2ecf20Sopenharmony_ci	*str = end;
7948c2ecf20Sopenharmony_ci	return n;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic int vector_remove(int n, char **error_out)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct vector_device *vec_d;
8008c2ecf20Sopenharmony_ci	struct net_device *dev;
8018c2ecf20Sopenharmony_ci	struct vector_private *vp;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	vec_d = find_device(n);
8048c2ecf20Sopenharmony_ci	if (vec_d == NULL)
8058c2ecf20Sopenharmony_ci		return -ENODEV;
8068c2ecf20Sopenharmony_ci	dev = vec_d->dev;
8078c2ecf20Sopenharmony_ci	vp = netdev_priv(dev);
8088c2ecf20Sopenharmony_ci	if (vp->fds != NULL)
8098c2ecf20Sopenharmony_ci		return -EBUSY;
8108c2ecf20Sopenharmony_ci	unregister_netdev(dev);
8118c2ecf20Sopenharmony_ci	platform_device_unregister(&vec_d->pdev);
8128c2ecf20Sopenharmony_ci	return 0;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci/*
8168c2ecf20Sopenharmony_ci * There is no shared per-transport initialization code, so
8178c2ecf20Sopenharmony_ci * we will just initialize each interface one by one and
8188c2ecf20Sopenharmony_ci * add them to a list
8198c2ecf20Sopenharmony_ci */
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic struct platform_driver uml_net_driver = {
8228c2ecf20Sopenharmony_ci	.driver = {
8238c2ecf20Sopenharmony_ci		.name = DRIVER_NAME,
8248c2ecf20Sopenharmony_ci	},
8258c2ecf20Sopenharmony_ci};
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic void vector_device_release(struct device *dev)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	struct vector_device *device = dev_get_drvdata(dev);
8318c2ecf20Sopenharmony_ci	struct net_device *netdev = device->dev;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	list_del(&device->list);
8348c2ecf20Sopenharmony_ci	kfree(device);
8358c2ecf20Sopenharmony_ci	free_netdev(netdev);
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci/* Bog standard recv using recvmsg - not used normally unless the user
8398c2ecf20Sopenharmony_ci * explicitly specifies not to use recvmmsg vector RX.
8408c2ecf20Sopenharmony_ci */
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic int vector_legacy_rx(struct vector_private *vp)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	int pkt_len;
8458c2ecf20Sopenharmony_ci	struct user_msghdr hdr;
8468c2ecf20Sopenharmony_ci	struct iovec iov[2 + MAX_IOV_SIZE]; /* header + data use case only */
8478c2ecf20Sopenharmony_ci	int iovpos = 0;
8488c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8498c2ecf20Sopenharmony_ci	int header_check;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	hdr.msg_name = NULL;
8528c2ecf20Sopenharmony_ci	hdr.msg_namelen = 0;
8538c2ecf20Sopenharmony_ci	hdr.msg_iov = (struct iovec *) &iov;
8548c2ecf20Sopenharmony_ci	hdr.msg_control = NULL;
8558c2ecf20Sopenharmony_ci	hdr.msg_controllen = 0;
8568c2ecf20Sopenharmony_ci	hdr.msg_flags = 0;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (vp->header_size > 0) {
8598c2ecf20Sopenharmony_ci		iov[0].iov_base = vp->header_rxbuffer;
8608c2ecf20Sopenharmony_ci		iov[0].iov_len = vp->header_size;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	skb = prep_skb(vp, &hdr);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (skb == NULL) {
8668c2ecf20Sopenharmony_ci		/* Read a packet into drop_buffer and don't do
8678c2ecf20Sopenharmony_ci		 * anything with it.
8688c2ecf20Sopenharmony_ci		 */
8698c2ecf20Sopenharmony_ci		iov[iovpos].iov_base = drop_buffer;
8708c2ecf20Sopenharmony_ci		iov[iovpos].iov_len = DROP_BUFFER_SIZE;
8718c2ecf20Sopenharmony_ci		hdr.msg_iovlen = 1;
8728c2ecf20Sopenharmony_ci		vp->dev->stats.rx_dropped++;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	pkt_len = uml_vector_recvmsg(vp->fds->rx_fd, &hdr, 0);
8768c2ecf20Sopenharmony_ci	if (pkt_len < 0) {
8778c2ecf20Sopenharmony_ci		vp->in_error = true;
8788c2ecf20Sopenharmony_ci		return pkt_len;
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	if (skb != NULL) {
8828c2ecf20Sopenharmony_ci		if (pkt_len > vp->header_size) {
8838c2ecf20Sopenharmony_ci			if (vp->header_size > 0) {
8848c2ecf20Sopenharmony_ci				header_check = vp->verify_header(
8858c2ecf20Sopenharmony_ci					vp->header_rxbuffer, skb, vp);
8868c2ecf20Sopenharmony_ci				if (header_check < 0) {
8878c2ecf20Sopenharmony_ci					dev_kfree_skb_irq(skb);
8888c2ecf20Sopenharmony_ci					vp->dev->stats.rx_dropped++;
8898c2ecf20Sopenharmony_ci					vp->estats.rx_encaps_errors++;
8908c2ecf20Sopenharmony_ci					return 0;
8918c2ecf20Sopenharmony_ci				}
8928c2ecf20Sopenharmony_ci				if (header_check > 0) {
8938c2ecf20Sopenharmony_ci					vp->estats.rx_csum_offload_good++;
8948c2ecf20Sopenharmony_ci					skb->ip_summed = CHECKSUM_UNNECESSARY;
8958c2ecf20Sopenharmony_ci				}
8968c2ecf20Sopenharmony_ci			}
8978c2ecf20Sopenharmony_ci			pskb_trim(skb, pkt_len - vp->rx_header_size);
8988c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, skb->dev);
8998c2ecf20Sopenharmony_ci			vp->dev->stats.rx_bytes += skb->len;
9008c2ecf20Sopenharmony_ci			vp->dev->stats.rx_packets++;
9018c2ecf20Sopenharmony_ci			netif_rx(skb);
9028c2ecf20Sopenharmony_ci		} else {
9038c2ecf20Sopenharmony_ci			dev_kfree_skb_irq(skb);
9048c2ecf20Sopenharmony_ci		}
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci	return pkt_len;
9078c2ecf20Sopenharmony_ci}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci/*
9108c2ecf20Sopenharmony_ci * Packet at a time TX which falls back to vector TX if the
9118c2ecf20Sopenharmony_ci * underlying transport is busy.
9128c2ecf20Sopenharmony_ci */
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cistatic int writev_tx(struct vector_private *vp, struct sk_buff *skb)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct iovec iov[3 + MAX_IOV_SIZE];
9198c2ecf20Sopenharmony_ci	int iov_count, pkt_len = 0;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	iov[0].iov_base = vp->header_txbuffer;
9228c2ecf20Sopenharmony_ci	iov_count = prep_msg(vp, skb, (struct iovec *) &iov);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (iov_count < 1)
9258c2ecf20Sopenharmony_ci		goto drop;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	pkt_len = uml_vector_writev(
9288c2ecf20Sopenharmony_ci		vp->fds->tx_fd,
9298c2ecf20Sopenharmony_ci		(struct iovec *) &iov,
9308c2ecf20Sopenharmony_ci		iov_count
9318c2ecf20Sopenharmony_ci	);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	if (pkt_len < 0)
9348c2ecf20Sopenharmony_ci		goto drop;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	netif_trans_update(vp->dev);
9378c2ecf20Sopenharmony_ci	netif_wake_queue(vp->dev);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	if (pkt_len > 0) {
9408c2ecf20Sopenharmony_ci		vp->dev->stats.tx_bytes += skb->len;
9418c2ecf20Sopenharmony_ci		vp->dev->stats.tx_packets++;
9428c2ecf20Sopenharmony_ci	} else {
9438c2ecf20Sopenharmony_ci		vp->dev->stats.tx_dropped++;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci	consume_skb(skb);
9468c2ecf20Sopenharmony_ci	return pkt_len;
9478c2ecf20Sopenharmony_cidrop:
9488c2ecf20Sopenharmony_ci	vp->dev->stats.tx_dropped++;
9498c2ecf20Sopenharmony_ci	consume_skb(skb);
9508c2ecf20Sopenharmony_ci	if (pkt_len < 0)
9518c2ecf20Sopenharmony_ci		vp->in_error = true;
9528c2ecf20Sopenharmony_ci	return pkt_len;
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci/*
9568c2ecf20Sopenharmony_ci * Receive as many messages as we can in one call using the special
9578c2ecf20Sopenharmony_ci * mmsg vector matched to an skb vector which we prepared earlier.
9588c2ecf20Sopenharmony_ci */
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic int vector_mmsg_rx(struct vector_private *vp)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	int packet_count, i;
9638c2ecf20Sopenharmony_ci	struct vector_queue *qi = vp->rx_queue;
9648c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9658c2ecf20Sopenharmony_ci	struct mmsghdr *mmsg_vector = qi->mmsg_vector;
9668c2ecf20Sopenharmony_ci	void **skbuff_vector = qi->skbuff_vector;
9678c2ecf20Sopenharmony_ci	int header_check;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	/* Refresh the vector and make sure it is with new skbs and the
9708c2ecf20Sopenharmony_ci	 * iovs are updated to point to them.
9718c2ecf20Sopenharmony_ci	 */
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	prep_queue_for_rx(qi);
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	/* Fire the Lazy Gun - get as many packets as we can in one go. */
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	packet_count = uml_vector_recvmmsg(
9788c2ecf20Sopenharmony_ci		vp->fds->rx_fd, qi->mmsg_vector, qi->max_depth, 0);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (packet_count < 0)
9818c2ecf20Sopenharmony_ci		vp->in_error = true;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (packet_count <= 0)
9848c2ecf20Sopenharmony_ci		return packet_count;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	/* We treat packet processing as enqueue, buffer refresh as dequeue
9878c2ecf20Sopenharmony_ci	 * The queue_depth tells us how many buffers have been used and how
9888c2ecf20Sopenharmony_ci	 * many do we need to prep the next time prep_queue_for_rx() is called.
9898c2ecf20Sopenharmony_ci	 */
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	qi->queue_depth = packet_count;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	for (i = 0; i < packet_count; i++) {
9948c2ecf20Sopenharmony_ci		skb = (*skbuff_vector);
9958c2ecf20Sopenharmony_ci		if (mmsg_vector->msg_len > vp->header_size) {
9968c2ecf20Sopenharmony_ci			if (vp->header_size > 0) {
9978c2ecf20Sopenharmony_ci				header_check = vp->verify_header(
9988c2ecf20Sopenharmony_ci					mmsg_vector->msg_hdr.msg_iov->iov_base,
9998c2ecf20Sopenharmony_ci					skb,
10008c2ecf20Sopenharmony_ci					vp
10018c2ecf20Sopenharmony_ci				);
10028c2ecf20Sopenharmony_ci				if (header_check < 0) {
10038c2ecf20Sopenharmony_ci				/* Overlay header failed to verify - discard.
10048c2ecf20Sopenharmony_ci				 * We can actually keep this skb and reuse it,
10058c2ecf20Sopenharmony_ci				 * but that will make the prep logic too
10068c2ecf20Sopenharmony_ci				 * complex.
10078c2ecf20Sopenharmony_ci				 */
10088c2ecf20Sopenharmony_ci					dev_kfree_skb_irq(skb);
10098c2ecf20Sopenharmony_ci					vp->estats.rx_encaps_errors++;
10108c2ecf20Sopenharmony_ci					continue;
10118c2ecf20Sopenharmony_ci				}
10128c2ecf20Sopenharmony_ci				if (header_check > 0) {
10138c2ecf20Sopenharmony_ci					vp->estats.rx_csum_offload_good++;
10148c2ecf20Sopenharmony_ci					skb->ip_summed = CHECKSUM_UNNECESSARY;
10158c2ecf20Sopenharmony_ci				}
10168c2ecf20Sopenharmony_ci			}
10178c2ecf20Sopenharmony_ci			pskb_trim(skb,
10188c2ecf20Sopenharmony_ci				mmsg_vector->msg_len - vp->rx_header_size);
10198c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, skb->dev);
10208c2ecf20Sopenharmony_ci			/*
10218c2ecf20Sopenharmony_ci			 * We do not need to lock on updating stats here
10228c2ecf20Sopenharmony_ci			 * The interrupt loop is non-reentrant.
10238c2ecf20Sopenharmony_ci			 */
10248c2ecf20Sopenharmony_ci			vp->dev->stats.rx_bytes += skb->len;
10258c2ecf20Sopenharmony_ci			vp->dev->stats.rx_packets++;
10268c2ecf20Sopenharmony_ci			netif_rx(skb);
10278c2ecf20Sopenharmony_ci		} else {
10288c2ecf20Sopenharmony_ci			/* Overlay header too short to do anything - discard.
10298c2ecf20Sopenharmony_ci			 * We can actually keep this skb and reuse it,
10308c2ecf20Sopenharmony_ci			 * but that will make the prep logic too complex.
10318c2ecf20Sopenharmony_ci			 */
10328c2ecf20Sopenharmony_ci			if (skb != NULL)
10338c2ecf20Sopenharmony_ci				dev_kfree_skb_irq(skb);
10348c2ecf20Sopenharmony_ci		}
10358c2ecf20Sopenharmony_ci		(*skbuff_vector) = NULL;
10368c2ecf20Sopenharmony_ci		/* Move to the next buffer element */
10378c2ecf20Sopenharmony_ci		mmsg_vector++;
10388c2ecf20Sopenharmony_ci		skbuff_vector++;
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci	if (packet_count > 0) {
10418c2ecf20Sopenharmony_ci		if (vp->estats.rx_queue_max < packet_count)
10428c2ecf20Sopenharmony_ci			vp->estats.rx_queue_max = packet_count;
10438c2ecf20Sopenharmony_ci		vp->estats.rx_queue_running_average =
10448c2ecf20Sopenharmony_ci			(vp->estats.rx_queue_running_average + packet_count) >> 1;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci	return packet_count;
10478c2ecf20Sopenharmony_ci}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cistatic void vector_rx(struct vector_private *vp)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	int err;
10528c2ecf20Sopenharmony_ci	int iter = 0;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	if ((vp->options & VECTOR_RX) > 0)
10558c2ecf20Sopenharmony_ci		while (((err = vector_mmsg_rx(vp)) > 0) && (iter < MAX_ITERATIONS))
10568c2ecf20Sopenharmony_ci			iter++;
10578c2ecf20Sopenharmony_ci	else
10588c2ecf20Sopenharmony_ci		while (((err = vector_legacy_rx(vp)) > 0) && (iter < MAX_ITERATIONS))
10598c2ecf20Sopenharmony_ci			iter++;
10608c2ecf20Sopenharmony_ci	if ((err != 0) && net_ratelimit())
10618c2ecf20Sopenharmony_ci		netdev_err(vp->dev, "vector_rx: error(%d)\n", err);
10628c2ecf20Sopenharmony_ci	if (iter == MAX_ITERATIONS)
10638c2ecf20Sopenharmony_ci		netdev_err(vp->dev, "vector_rx: device stuck, remote end may have closed the connection\n");
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_cistatic int vector_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
10698c2ecf20Sopenharmony_ci	int queue_depth = 0;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	if (vp->in_error) {
10728c2ecf20Sopenharmony_ci		deactivate_fd(vp->fds->rx_fd, vp->rx_irq);
10738c2ecf20Sopenharmony_ci		if ((vp->fds->rx_fd != vp->fds->tx_fd) && (vp->tx_irq != 0))
10748c2ecf20Sopenharmony_ci			deactivate_fd(vp->fds->tx_fd, vp->tx_irq);
10758c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
10768c2ecf20Sopenharmony_ci	}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	if ((vp->options & VECTOR_TX) == 0) {
10798c2ecf20Sopenharmony_ci		writev_tx(vp, skb);
10808c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	/* We do BQL only in the vector path, no point doing it in
10848c2ecf20Sopenharmony_ci	 * packet at a time mode as there is no device queue
10858c2ecf20Sopenharmony_ci	 */
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	netdev_sent_queue(vp->dev, skb->len);
10888c2ecf20Sopenharmony_ci	queue_depth = vector_enqueue(vp->tx_queue, skb);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	/* if the device queue is full, stop the upper layers and
10918c2ecf20Sopenharmony_ci	 * flush it.
10928c2ecf20Sopenharmony_ci	 */
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	if (queue_depth >= vp->tx_queue->max_depth - 1) {
10958c2ecf20Sopenharmony_ci		vp->estats.tx_kicks++;
10968c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
10978c2ecf20Sopenharmony_ci		vector_send(vp->tx_queue);
10988c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci	if (netdev_xmit_more()) {
11018c2ecf20Sopenharmony_ci		mod_timer(&vp->tl, vp->coalesce);
11028c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci	if (skb->len < TX_SMALL_PACKET) {
11058c2ecf20Sopenharmony_ci		vp->estats.tx_kicks++;
11068c2ecf20Sopenharmony_ci		vector_send(vp->tx_queue);
11078c2ecf20Sopenharmony_ci	} else
11088c2ecf20Sopenharmony_ci		tasklet_schedule(&vp->tx_poll);
11098c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic irqreturn_t vector_rx_interrupt(int irq, void *dev_id)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
11158c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	if (!netif_running(dev))
11188c2ecf20Sopenharmony_ci		return IRQ_NONE;
11198c2ecf20Sopenharmony_ci	vector_rx(vp);
11208c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cistatic irqreturn_t vector_tx_interrupt(int irq, void *dev_id)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
11278c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	if (!netif_running(dev))
11308c2ecf20Sopenharmony_ci		return IRQ_NONE;
11318c2ecf20Sopenharmony_ci	/* We need to pay attention to it only if we got
11328c2ecf20Sopenharmony_ci	 * -EAGAIN or -ENOBUFFS from sendmmsg. Otherwise
11338c2ecf20Sopenharmony_ci	 * we ignore it. In the future, it may be worth
11348c2ecf20Sopenharmony_ci	 * it to improve the IRQ controller a bit to make
11358c2ecf20Sopenharmony_ci	 * tweaking the IRQ mask less costly
11368c2ecf20Sopenharmony_ci	 */
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	if (vp->in_write_poll)
11398c2ecf20Sopenharmony_ci		tasklet_schedule(&vp->tx_poll);
11408c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic int irq_rr;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_cistatic int vector_net_close(struct net_device *dev)
11478c2ecf20Sopenharmony_ci{
11488c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
11498c2ecf20Sopenharmony_ci	unsigned long flags;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
11528c2ecf20Sopenharmony_ci	del_timer(&vp->tl);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	if (vp->fds == NULL)
11558c2ecf20Sopenharmony_ci		return 0;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	/* Disable and free all IRQS */
11588c2ecf20Sopenharmony_ci	if (vp->rx_irq > 0) {
11598c2ecf20Sopenharmony_ci		um_free_irq(vp->rx_irq, dev);
11608c2ecf20Sopenharmony_ci		vp->rx_irq = 0;
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci	if (vp->tx_irq > 0) {
11638c2ecf20Sopenharmony_ci		um_free_irq(vp->tx_irq, dev);
11648c2ecf20Sopenharmony_ci		vp->tx_irq = 0;
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci	tasklet_kill(&vp->tx_poll);
11678c2ecf20Sopenharmony_ci	if (vp->fds->rx_fd > 0) {
11688c2ecf20Sopenharmony_ci		if (vp->bpf)
11698c2ecf20Sopenharmony_ci			uml_vector_detach_bpf(vp->fds->rx_fd, vp->bpf);
11708c2ecf20Sopenharmony_ci		os_close_file(vp->fds->rx_fd);
11718c2ecf20Sopenharmony_ci		vp->fds->rx_fd = -1;
11728c2ecf20Sopenharmony_ci	}
11738c2ecf20Sopenharmony_ci	if (vp->fds->tx_fd > 0) {
11748c2ecf20Sopenharmony_ci		os_close_file(vp->fds->tx_fd);
11758c2ecf20Sopenharmony_ci		vp->fds->tx_fd = -1;
11768c2ecf20Sopenharmony_ci	}
11778c2ecf20Sopenharmony_ci	if (vp->bpf != NULL)
11788c2ecf20Sopenharmony_ci		kfree(vp->bpf->filter);
11798c2ecf20Sopenharmony_ci	kfree(vp->bpf);
11808c2ecf20Sopenharmony_ci	vp->bpf = NULL;
11818c2ecf20Sopenharmony_ci	kfree(vp->fds->remote_addr);
11828c2ecf20Sopenharmony_ci	kfree(vp->transport_data);
11838c2ecf20Sopenharmony_ci	kfree(vp->header_rxbuffer);
11848c2ecf20Sopenharmony_ci	kfree(vp->header_txbuffer);
11858c2ecf20Sopenharmony_ci	if (vp->rx_queue != NULL)
11868c2ecf20Sopenharmony_ci		destroy_queue(vp->rx_queue);
11878c2ecf20Sopenharmony_ci	if (vp->tx_queue != NULL)
11888c2ecf20Sopenharmony_ci		destroy_queue(vp->tx_queue);
11898c2ecf20Sopenharmony_ci	kfree(vp->fds);
11908c2ecf20Sopenharmony_ci	vp->fds = NULL;
11918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vp->lock, flags);
11928c2ecf20Sopenharmony_ci	vp->opened = false;
11938c2ecf20Sopenharmony_ci	vp->in_error = false;
11948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vp->lock, flags);
11958c2ecf20Sopenharmony_ci	return 0;
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci/* TX tasklet */
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_cistatic void vector_tx_poll(unsigned long data)
12018c2ecf20Sopenharmony_ci{
12028c2ecf20Sopenharmony_ci	struct vector_private *vp = (struct vector_private *)data;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	vp->estats.tx_kicks++;
12058c2ecf20Sopenharmony_ci	vector_send(vp->tx_queue);
12068c2ecf20Sopenharmony_ci}
12078c2ecf20Sopenharmony_cistatic void vector_reset_tx(struct work_struct *work)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	struct vector_private *vp =
12108c2ecf20Sopenharmony_ci		container_of(work, struct vector_private, reset_tx);
12118c2ecf20Sopenharmony_ci	netdev_reset_queue(vp->dev);
12128c2ecf20Sopenharmony_ci	netif_start_queue(vp->dev);
12138c2ecf20Sopenharmony_ci	netif_wake_queue(vp->dev);
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_cistatic int vector_net_open(struct net_device *dev)
12178c2ecf20Sopenharmony_ci{
12188c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
12198c2ecf20Sopenharmony_ci	unsigned long flags;
12208c2ecf20Sopenharmony_ci	int err = -EINVAL;
12218c2ecf20Sopenharmony_ci	struct vector_device *vdevice;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vp->lock, flags);
12248c2ecf20Sopenharmony_ci	if (vp->opened) {
12258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vp->lock, flags);
12268c2ecf20Sopenharmony_ci		return -ENXIO;
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci	vp->opened = true;
12298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vp->lock, flags);
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	vp->bpf = uml_vector_user_bpf(get_bpf_file(vp->parsed));
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	vp->fds = uml_vector_user_open(vp->unit, vp->parsed);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (vp->fds == NULL)
12368c2ecf20Sopenharmony_ci		goto out_close;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	if (build_transport_data(vp) < 0)
12398c2ecf20Sopenharmony_ci		goto out_close;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	if ((vp->options & VECTOR_RX) > 0) {
12428c2ecf20Sopenharmony_ci		vp->rx_queue = create_queue(
12438c2ecf20Sopenharmony_ci			vp,
12448c2ecf20Sopenharmony_ci			get_depth(vp->parsed),
12458c2ecf20Sopenharmony_ci			vp->rx_header_size,
12468c2ecf20Sopenharmony_ci			MAX_IOV_SIZE
12478c2ecf20Sopenharmony_ci		);
12488c2ecf20Sopenharmony_ci		vp->rx_queue->queue_depth = get_depth(vp->parsed);
12498c2ecf20Sopenharmony_ci	} else {
12508c2ecf20Sopenharmony_ci		vp->header_rxbuffer = kmalloc(
12518c2ecf20Sopenharmony_ci			vp->rx_header_size,
12528c2ecf20Sopenharmony_ci			GFP_KERNEL
12538c2ecf20Sopenharmony_ci		);
12548c2ecf20Sopenharmony_ci		if (vp->header_rxbuffer == NULL)
12558c2ecf20Sopenharmony_ci			goto out_close;
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci	if ((vp->options & VECTOR_TX) > 0) {
12588c2ecf20Sopenharmony_ci		vp->tx_queue = create_queue(
12598c2ecf20Sopenharmony_ci			vp,
12608c2ecf20Sopenharmony_ci			get_depth(vp->parsed),
12618c2ecf20Sopenharmony_ci			vp->header_size,
12628c2ecf20Sopenharmony_ci			MAX_IOV_SIZE
12638c2ecf20Sopenharmony_ci		);
12648c2ecf20Sopenharmony_ci	} else {
12658c2ecf20Sopenharmony_ci		vp->header_txbuffer = kmalloc(vp->header_size, GFP_KERNEL);
12668c2ecf20Sopenharmony_ci		if (vp->header_txbuffer == NULL)
12678c2ecf20Sopenharmony_ci			goto out_close;
12688c2ecf20Sopenharmony_ci	}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	/* READ IRQ */
12718c2ecf20Sopenharmony_ci	err = um_request_irq(
12728c2ecf20Sopenharmony_ci		irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd,
12738c2ecf20Sopenharmony_ci			IRQ_READ, vector_rx_interrupt,
12748c2ecf20Sopenharmony_ci			IRQF_SHARED, dev->name, dev);
12758c2ecf20Sopenharmony_ci	if (err != 0) {
12768c2ecf20Sopenharmony_ci		netdev_err(dev, "vector_open: failed to get rx irq(%d)\n", err);
12778c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
12788c2ecf20Sopenharmony_ci		goto out_close;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci	vp->rx_irq = irq_rr + VECTOR_BASE_IRQ;
12818c2ecf20Sopenharmony_ci	dev->irq = irq_rr + VECTOR_BASE_IRQ;
12828c2ecf20Sopenharmony_ci	irq_rr = (irq_rr + 1) % VECTOR_IRQ_SPACE;
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	/* WRITE IRQ - we need it only if we have vector TX */
12858c2ecf20Sopenharmony_ci	if ((vp->options & VECTOR_TX) > 0) {
12868c2ecf20Sopenharmony_ci		err = um_request_irq(
12878c2ecf20Sopenharmony_ci			irq_rr + VECTOR_BASE_IRQ, vp->fds->tx_fd,
12888c2ecf20Sopenharmony_ci				IRQ_WRITE, vector_tx_interrupt,
12898c2ecf20Sopenharmony_ci				IRQF_SHARED, dev->name, dev);
12908c2ecf20Sopenharmony_ci		if (err != 0) {
12918c2ecf20Sopenharmony_ci			netdev_err(dev,
12928c2ecf20Sopenharmony_ci				"vector_open: failed to get tx irq(%d)\n", err);
12938c2ecf20Sopenharmony_ci			err = -ENETUNREACH;
12948c2ecf20Sopenharmony_ci			goto out_close;
12958c2ecf20Sopenharmony_ci		}
12968c2ecf20Sopenharmony_ci		vp->tx_irq = irq_rr + VECTOR_BASE_IRQ;
12978c2ecf20Sopenharmony_ci		irq_rr = (irq_rr + 1) % VECTOR_IRQ_SPACE;
12988c2ecf20Sopenharmony_ci	}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	if ((vp->options & VECTOR_QDISC_BYPASS) != 0) {
13018c2ecf20Sopenharmony_ci		if (!uml_raw_enable_qdisc_bypass(vp->fds->rx_fd))
13028c2ecf20Sopenharmony_ci			vp->options |= VECTOR_BPF;
13038c2ecf20Sopenharmony_ci	}
13048c2ecf20Sopenharmony_ci	if (((vp->options & VECTOR_BPF) != 0) && (vp->bpf == NULL))
13058c2ecf20Sopenharmony_ci		vp->bpf = uml_vector_default_bpf(dev->dev_addr);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	if (vp->bpf != NULL)
13088c2ecf20Sopenharmony_ci		uml_vector_attach_bpf(vp->fds->rx_fd, vp->bpf);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	netif_start_queue(dev);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	/* clear buffer - it can happen that the host side of the interface
13138c2ecf20Sopenharmony_ci	 * is full when we get here. In this case, new data is never queued,
13148c2ecf20Sopenharmony_ci	 * SIGIOs never arrive, and the net never works.
13158c2ecf20Sopenharmony_ci	 */
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	vector_rx(vp);
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	vector_reset_stats(vp);
13208c2ecf20Sopenharmony_ci	vdevice = find_device(vp->unit);
13218c2ecf20Sopenharmony_ci	vdevice->opened = 1;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	if ((vp->options & VECTOR_TX) != 0)
13248c2ecf20Sopenharmony_ci		add_timer(&vp->tl);
13258c2ecf20Sopenharmony_ci	return 0;
13268c2ecf20Sopenharmony_ciout_close:
13278c2ecf20Sopenharmony_ci	vector_net_close(dev);
13288c2ecf20Sopenharmony_ci	return err;
13298c2ecf20Sopenharmony_ci}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_cistatic void vector_net_set_multicast_list(struct net_device *dev)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	/* TODO: - we can do some BPF games here */
13358c2ecf20Sopenharmony_ci	return;
13368c2ecf20Sopenharmony_ci}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_cistatic void vector_net_tx_timeout(struct net_device *dev, unsigned int txqueue)
13398c2ecf20Sopenharmony_ci{
13408c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	vp->estats.tx_timeout_count++;
13438c2ecf20Sopenharmony_ci	netif_trans_update(dev);
13448c2ecf20Sopenharmony_ci	schedule_work(&vp->reset_tx);
13458c2ecf20Sopenharmony_ci}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_cistatic netdev_features_t vector_fix_features(struct net_device *dev,
13488c2ecf20Sopenharmony_ci	netdev_features_t features)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
13518c2ecf20Sopenharmony_ci	return features;
13528c2ecf20Sopenharmony_ci}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_cistatic int vector_set_features(struct net_device *dev,
13558c2ecf20Sopenharmony_ci	netdev_features_t features)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
13588c2ecf20Sopenharmony_ci	/* Adjust buffer sizes for GSO/GRO. Unfortunately, there is
13598c2ecf20Sopenharmony_ci	 * no way to negotiate it on raw sockets, so we can change
13608c2ecf20Sopenharmony_ci	 * only our side.
13618c2ecf20Sopenharmony_ci	 */
13628c2ecf20Sopenharmony_ci	if (features & NETIF_F_GRO)
13638c2ecf20Sopenharmony_ci		/* All new frame buffers will be GRO-sized */
13648c2ecf20Sopenharmony_ci		vp->req_size = 65536;
13658c2ecf20Sopenharmony_ci	else
13668c2ecf20Sopenharmony_ci		/* All new frame buffers will be normal sized */
13678c2ecf20Sopenharmony_ci		vp->req_size = vp->max_packet + vp->headroom + SAFETY_MARGIN;
13688c2ecf20Sopenharmony_ci	return 0;
13698c2ecf20Sopenharmony_ci}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
13728c2ecf20Sopenharmony_cistatic void vector_net_poll_controller(struct net_device *dev)
13738c2ecf20Sopenharmony_ci{
13748c2ecf20Sopenharmony_ci	disable_irq(dev->irq);
13758c2ecf20Sopenharmony_ci	vector_rx_interrupt(dev->irq, dev);
13768c2ecf20Sopenharmony_ci	enable_irq(dev->irq);
13778c2ecf20Sopenharmony_ci}
13788c2ecf20Sopenharmony_ci#endif
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cistatic void vector_net_get_drvinfo(struct net_device *dev,
13818c2ecf20Sopenharmony_ci				struct ethtool_drvinfo *info)
13828c2ecf20Sopenharmony_ci{
13838c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic int vector_net_load_bpf_flash(struct net_device *dev,
13878c2ecf20Sopenharmony_ci				struct ethtool_flash *efl)
13888c2ecf20Sopenharmony_ci{
13898c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
13908c2ecf20Sopenharmony_ci	struct vector_device *vdevice;
13918c2ecf20Sopenharmony_ci	const struct firmware *fw;
13928c2ecf20Sopenharmony_ci	int result = 0;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	if (!(vp->options & VECTOR_BPF_FLASH)) {
13958c2ecf20Sopenharmony_ci		netdev_err(dev, "loading firmware not permitted: %s\n", efl->data);
13968c2ecf20Sopenharmony_ci		return -1;
13978c2ecf20Sopenharmony_ci	}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	spin_lock(&vp->lock);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	if (vp->bpf != NULL) {
14028c2ecf20Sopenharmony_ci		if (vp->opened)
14038c2ecf20Sopenharmony_ci			uml_vector_detach_bpf(vp->fds->rx_fd, vp->bpf);
14048c2ecf20Sopenharmony_ci		kfree(vp->bpf->filter);
14058c2ecf20Sopenharmony_ci		vp->bpf->filter = NULL;
14068c2ecf20Sopenharmony_ci	} else {
14078c2ecf20Sopenharmony_ci		vp->bpf = kmalloc(sizeof(struct sock_fprog), GFP_ATOMIC);
14088c2ecf20Sopenharmony_ci		if (vp->bpf == NULL) {
14098c2ecf20Sopenharmony_ci			netdev_err(dev, "failed to allocate memory for firmware\n");
14108c2ecf20Sopenharmony_ci			goto flash_fail;
14118c2ecf20Sopenharmony_ci		}
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	vdevice = find_device(vp->unit);
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	if (request_firmware(&fw, efl->data, &vdevice->pdev.dev))
14178c2ecf20Sopenharmony_ci		goto flash_fail;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	vp->bpf->filter = kmemdup(fw->data, fw->size, GFP_ATOMIC);
14208c2ecf20Sopenharmony_ci	if (!vp->bpf->filter)
14218c2ecf20Sopenharmony_ci		goto free_buffer;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	vp->bpf->len = fw->size / sizeof(struct sock_filter);
14248c2ecf20Sopenharmony_ci	release_firmware(fw);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	if (vp->opened)
14278c2ecf20Sopenharmony_ci		result = uml_vector_attach_bpf(vp->fds->rx_fd, vp->bpf);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	spin_unlock(&vp->lock);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	return result;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_cifree_buffer:
14348c2ecf20Sopenharmony_ci	release_firmware(fw);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ciflash_fail:
14378c2ecf20Sopenharmony_ci	spin_unlock(&vp->lock);
14388c2ecf20Sopenharmony_ci	if (vp->bpf != NULL)
14398c2ecf20Sopenharmony_ci		kfree(vp->bpf->filter);
14408c2ecf20Sopenharmony_ci	kfree(vp->bpf);
14418c2ecf20Sopenharmony_ci	vp->bpf = NULL;
14428c2ecf20Sopenharmony_ci	return -1;
14438c2ecf20Sopenharmony_ci}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_cistatic void vector_get_ringparam(struct net_device *netdev,
14468c2ecf20Sopenharmony_ci				struct ethtool_ringparam *ring)
14478c2ecf20Sopenharmony_ci{
14488c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(netdev);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	ring->rx_max_pending = vp->rx_queue->max_depth;
14518c2ecf20Sopenharmony_ci	ring->tx_max_pending = vp->tx_queue->max_depth;
14528c2ecf20Sopenharmony_ci	ring->rx_pending = vp->rx_queue->max_depth;
14538c2ecf20Sopenharmony_ci	ring->tx_pending = vp->tx_queue->max_depth;
14548c2ecf20Sopenharmony_ci}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_cistatic void vector_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
14578c2ecf20Sopenharmony_ci{
14588c2ecf20Sopenharmony_ci	switch (stringset) {
14598c2ecf20Sopenharmony_ci	case ETH_SS_TEST:
14608c2ecf20Sopenharmony_ci		*buf = '\0';
14618c2ecf20Sopenharmony_ci		break;
14628c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
14638c2ecf20Sopenharmony_ci		memcpy(buf, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
14648c2ecf20Sopenharmony_ci		break;
14658c2ecf20Sopenharmony_ci	default:
14668c2ecf20Sopenharmony_ci		WARN_ON(1);
14678c2ecf20Sopenharmony_ci		break;
14688c2ecf20Sopenharmony_ci	}
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_cistatic int vector_get_sset_count(struct net_device *dev, int sset)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	switch (sset) {
14748c2ecf20Sopenharmony_ci	case ETH_SS_TEST:
14758c2ecf20Sopenharmony_ci		return 0;
14768c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
14778c2ecf20Sopenharmony_ci		return VECTOR_NUM_STATS;
14788c2ecf20Sopenharmony_ci	default:
14798c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
14808c2ecf20Sopenharmony_ci	}
14818c2ecf20Sopenharmony_ci}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_cistatic void vector_get_ethtool_stats(struct net_device *dev,
14848c2ecf20Sopenharmony_ci	struct ethtool_stats *estats,
14858c2ecf20Sopenharmony_ci	u64 *tmp_stats)
14868c2ecf20Sopenharmony_ci{
14878c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(dev);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	memcpy(tmp_stats, &vp->estats, sizeof(struct vector_estats));
14908c2ecf20Sopenharmony_ci}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_cistatic int vector_get_coalesce(struct net_device *netdev,
14938c2ecf20Sopenharmony_ci					struct ethtool_coalesce *ec)
14948c2ecf20Sopenharmony_ci{
14958c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(netdev);
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	ec->tx_coalesce_usecs = (vp->coalesce * 1000000) / HZ;
14988c2ecf20Sopenharmony_ci	return 0;
14998c2ecf20Sopenharmony_ci}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_cistatic int vector_set_coalesce(struct net_device *netdev,
15028c2ecf20Sopenharmony_ci					struct ethtool_coalesce *ec)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	struct vector_private *vp = netdev_priv(netdev);
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	vp->coalesce = (ec->tx_coalesce_usecs * HZ) / 1000000;
15078c2ecf20Sopenharmony_ci	if (vp->coalesce == 0)
15088c2ecf20Sopenharmony_ci		vp->coalesce = 1;
15098c2ecf20Sopenharmony_ci	return 0;
15108c2ecf20Sopenharmony_ci}
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_cistatic const struct ethtool_ops vector_net_ethtool_ops = {
15138c2ecf20Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_TX_USECS,
15148c2ecf20Sopenharmony_ci	.get_drvinfo	= vector_net_get_drvinfo,
15158c2ecf20Sopenharmony_ci	.get_link	= ethtool_op_get_link,
15168c2ecf20Sopenharmony_ci	.get_ts_info	= ethtool_op_get_ts_info,
15178c2ecf20Sopenharmony_ci	.get_ringparam	= vector_get_ringparam,
15188c2ecf20Sopenharmony_ci	.get_strings	= vector_get_strings,
15198c2ecf20Sopenharmony_ci	.get_sset_count	= vector_get_sset_count,
15208c2ecf20Sopenharmony_ci	.get_ethtool_stats = vector_get_ethtool_stats,
15218c2ecf20Sopenharmony_ci	.get_coalesce	= vector_get_coalesce,
15228c2ecf20Sopenharmony_ci	.set_coalesce	= vector_set_coalesce,
15238c2ecf20Sopenharmony_ci	.flash_device	= vector_net_load_bpf_flash,
15248c2ecf20Sopenharmony_ci};
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_cistatic const struct net_device_ops vector_netdev_ops = {
15288c2ecf20Sopenharmony_ci	.ndo_open		= vector_net_open,
15298c2ecf20Sopenharmony_ci	.ndo_stop		= vector_net_close,
15308c2ecf20Sopenharmony_ci	.ndo_start_xmit		= vector_net_start_xmit,
15318c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= vector_net_set_multicast_list,
15328c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= vector_net_tx_timeout,
15338c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
15348c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
15358c2ecf20Sopenharmony_ci	.ndo_fix_features	= vector_fix_features,
15368c2ecf20Sopenharmony_ci	.ndo_set_features	= vector_set_features,
15378c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
15388c2ecf20Sopenharmony_ci	.ndo_poll_controller = vector_net_poll_controller,
15398c2ecf20Sopenharmony_ci#endif
15408c2ecf20Sopenharmony_ci};
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_cistatic void vector_timer_expire(struct timer_list *t)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	struct vector_private *vp = from_timer(vp, t, tl);
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	vp->estats.tx_kicks++;
15488c2ecf20Sopenharmony_ci	vector_send(vp->tx_queue);
15498c2ecf20Sopenharmony_ci}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_cistatic void vector_eth_configure(
15528c2ecf20Sopenharmony_ci		int n,
15538c2ecf20Sopenharmony_ci		struct arglist *def
15548c2ecf20Sopenharmony_ci	)
15558c2ecf20Sopenharmony_ci{
15568c2ecf20Sopenharmony_ci	struct vector_device *device;
15578c2ecf20Sopenharmony_ci	struct net_device *dev;
15588c2ecf20Sopenharmony_ci	struct vector_private *vp;
15598c2ecf20Sopenharmony_ci	int err;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	device = kzalloc(sizeof(*device), GFP_KERNEL);
15628c2ecf20Sopenharmony_ci	if (device == NULL) {
15638c2ecf20Sopenharmony_ci		printk(KERN_ERR "eth_configure failed to allocate struct "
15648c2ecf20Sopenharmony_ci				 "vector_device\n");
15658c2ecf20Sopenharmony_ci		return;
15668c2ecf20Sopenharmony_ci	}
15678c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct vector_private));
15688c2ecf20Sopenharmony_ci	if (dev == NULL) {
15698c2ecf20Sopenharmony_ci		printk(KERN_ERR "eth_configure: failed to allocate struct "
15708c2ecf20Sopenharmony_ci				 "net_device for vec%d\n", n);
15718c2ecf20Sopenharmony_ci		goto out_free_device;
15728c2ecf20Sopenharmony_ci	}
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	dev->mtu = get_mtu(def);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&device->list);
15778c2ecf20Sopenharmony_ci	device->unit = n;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	/* If this name ends up conflicting with an existing registered
15808c2ecf20Sopenharmony_ci	 * netdevice, that is OK, register_netdev{,ice}() will notice this
15818c2ecf20Sopenharmony_ci	 * and fail.
15828c2ecf20Sopenharmony_ci	 */
15838c2ecf20Sopenharmony_ci	snprintf(dev->name, sizeof(dev->name), "vec%d", n);
15848c2ecf20Sopenharmony_ci	uml_net_setup_etheraddr(dev, uml_vector_fetch_arg(def, "mac"));
15858c2ecf20Sopenharmony_ci	vp = netdev_priv(dev);
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	/* sysfs register */
15888c2ecf20Sopenharmony_ci	if (!driver_registered) {
15898c2ecf20Sopenharmony_ci		platform_driver_register(&uml_net_driver);
15908c2ecf20Sopenharmony_ci		driver_registered = 1;
15918c2ecf20Sopenharmony_ci	}
15928c2ecf20Sopenharmony_ci	device->pdev.id = n;
15938c2ecf20Sopenharmony_ci	device->pdev.name = DRIVER_NAME;
15948c2ecf20Sopenharmony_ci	device->pdev.dev.release = vector_device_release;
15958c2ecf20Sopenharmony_ci	dev_set_drvdata(&device->pdev.dev, device);
15968c2ecf20Sopenharmony_ci	if (platform_device_register(&device->pdev))
15978c2ecf20Sopenharmony_ci		goto out_free_netdev;
15988c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &device->pdev.dev);
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	device->dev = dev;
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	*vp = ((struct vector_private)
16038c2ecf20Sopenharmony_ci		{
16048c2ecf20Sopenharmony_ci		.list			= LIST_HEAD_INIT(vp->list),
16058c2ecf20Sopenharmony_ci		.dev			= dev,
16068c2ecf20Sopenharmony_ci		.unit			= n,
16078c2ecf20Sopenharmony_ci		.options		= get_transport_options(def),
16088c2ecf20Sopenharmony_ci		.rx_irq			= 0,
16098c2ecf20Sopenharmony_ci		.tx_irq			= 0,
16108c2ecf20Sopenharmony_ci		.parsed			= def,
16118c2ecf20Sopenharmony_ci		.max_packet		= get_mtu(def) + ETH_HEADER_OTHER,
16128c2ecf20Sopenharmony_ci		/* TODO - we need to calculate headroom so that ip header
16138c2ecf20Sopenharmony_ci		 * is 16 byte aligned all the time
16148c2ecf20Sopenharmony_ci		 */
16158c2ecf20Sopenharmony_ci		.headroom		= get_headroom(def),
16168c2ecf20Sopenharmony_ci		.form_header		= NULL,
16178c2ecf20Sopenharmony_ci		.verify_header		= NULL,
16188c2ecf20Sopenharmony_ci		.header_rxbuffer	= NULL,
16198c2ecf20Sopenharmony_ci		.header_txbuffer	= NULL,
16208c2ecf20Sopenharmony_ci		.header_size		= 0,
16218c2ecf20Sopenharmony_ci		.rx_header_size		= 0,
16228c2ecf20Sopenharmony_ci		.rexmit_scheduled	= false,
16238c2ecf20Sopenharmony_ci		.opened			= false,
16248c2ecf20Sopenharmony_ci		.transport_data		= NULL,
16258c2ecf20Sopenharmony_ci		.in_write_poll		= false,
16268c2ecf20Sopenharmony_ci		.coalesce		= 2,
16278c2ecf20Sopenharmony_ci		.req_size		= get_req_size(def),
16288c2ecf20Sopenharmony_ci		.in_error		= false,
16298c2ecf20Sopenharmony_ci		.bpf			= NULL
16308c2ecf20Sopenharmony_ci	});
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST);
16338c2ecf20Sopenharmony_ci	tasklet_init(&vp->tx_poll, vector_tx_poll, (unsigned long)vp);
16348c2ecf20Sopenharmony_ci	INIT_WORK(&vp->reset_tx, vector_reset_tx);
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	timer_setup(&vp->tl, vector_timer_expire, 0);
16378c2ecf20Sopenharmony_ci	spin_lock_init(&vp->lock);
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci	/* FIXME */
16408c2ecf20Sopenharmony_ci	dev->netdev_ops = &vector_netdev_ops;
16418c2ecf20Sopenharmony_ci	dev->ethtool_ops = &vector_net_ethtool_ops;
16428c2ecf20Sopenharmony_ci	dev->watchdog_timeo = (HZ >> 1);
16438c2ecf20Sopenharmony_ci	/* primary IRQ - fixme */
16448c2ecf20Sopenharmony_ci	dev->irq = 0; /* we will adjust this once opened */
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	rtnl_lock();
16478c2ecf20Sopenharmony_ci	err = register_netdevice(dev);
16488c2ecf20Sopenharmony_ci	rtnl_unlock();
16498c2ecf20Sopenharmony_ci	if (err)
16508c2ecf20Sopenharmony_ci		goto out_undo_user_init;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	spin_lock(&vector_devices_lock);
16538c2ecf20Sopenharmony_ci	list_add(&device->list, &vector_devices);
16548c2ecf20Sopenharmony_ci	spin_unlock(&vector_devices_lock);
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	return;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ciout_undo_user_init:
16598c2ecf20Sopenharmony_ci	return;
16608c2ecf20Sopenharmony_ciout_free_netdev:
16618c2ecf20Sopenharmony_ci	free_netdev(dev);
16628c2ecf20Sopenharmony_ciout_free_device:
16638c2ecf20Sopenharmony_ci	kfree(device);
16648c2ecf20Sopenharmony_ci}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci/*
16708c2ecf20Sopenharmony_ci * Invoked late in the init
16718c2ecf20Sopenharmony_ci */
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_cistatic int __init vector_init(void)
16748c2ecf20Sopenharmony_ci{
16758c2ecf20Sopenharmony_ci	struct list_head *ele;
16768c2ecf20Sopenharmony_ci	struct vector_cmd_line_arg *def;
16778c2ecf20Sopenharmony_ci	struct arglist *parsed;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	list_for_each(ele, &vec_cmd_line) {
16808c2ecf20Sopenharmony_ci		def = list_entry(ele, struct vector_cmd_line_arg, list);
16818c2ecf20Sopenharmony_ci		parsed = uml_parse_vector_ifspec(def->arguments);
16828c2ecf20Sopenharmony_ci		if (parsed != NULL)
16838c2ecf20Sopenharmony_ci			vector_eth_configure(def->unit, parsed);
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci	return 0;
16868c2ecf20Sopenharmony_ci}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci/* Invoked at initial argument parsing, only stores
16908c2ecf20Sopenharmony_ci * arguments until a proper vector_init is called
16918c2ecf20Sopenharmony_ci * later
16928c2ecf20Sopenharmony_ci */
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_cistatic int __init vector_setup(char *str)
16958c2ecf20Sopenharmony_ci{
16968c2ecf20Sopenharmony_ci	char *error;
16978c2ecf20Sopenharmony_ci	int n, err;
16988c2ecf20Sopenharmony_ci	struct vector_cmd_line_arg *new;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	err = vector_parse(str, &n, &str, &error);
17018c2ecf20Sopenharmony_ci	if (err) {
17028c2ecf20Sopenharmony_ci		printk(KERN_ERR "vector_setup - Couldn't parse '%s' : %s\n",
17038c2ecf20Sopenharmony_ci				 str, error);
17048c2ecf20Sopenharmony_ci		return 1;
17058c2ecf20Sopenharmony_ci	}
17068c2ecf20Sopenharmony_ci	new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES);
17078c2ecf20Sopenharmony_ci	if (!new)
17088c2ecf20Sopenharmony_ci		panic("%s: Failed to allocate %zu bytes\n", __func__,
17098c2ecf20Sopenharmony_ci		      sizeof(*new));
17108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&new->list);
17118c2ecf20Sopenharmony_ci	new->unit = n;
17128c2ecf20Sopenharmony_ci	new->arguments = str;
17138c2ecf20Sopenharmony_ci	list_add_tail(&new->list, &vec_cmd_line);
17148c2ecf20Sopenharmony_ci	return 1;
17158c2ecf20Sopenharmony_ci}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci__setup("vec", vector_setup);
17188c2ecf20Sopenharmony_ci__uml_help(vector_setup,
17198c2ecf20Sopenharmony_ci"vec[0-9]+:<option>=<value>,<option>=<value>\n"
17208c2ecf20Sopenharmony_ci"	 Configure a vector io network device.\n\n"
17218c2ecf20Sopenharmony_ci);
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_cilate_initcall(vector_init);
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_cistatic struct mc_device vector_mc = {
17268c2ecf20Sopenharmony_ci	.list		= LIST_HEAD_INIT(vector_mc.list),
17278c2ecf20Sopenharmony_ci	.name		= "vec",
17288c2ecf20Sopenharmony_ci	.config		= vector_config,
17298c2ecf20Sopenharmony_ci	.get_config	= NULL,
17308c2ecf20Sopenharmony_ci	.id		= vector_id,
17318c2ecf20Sopenharmony_ci	.remove		= vector_remove,
17328c2ecf20Sopenharmony_ci};
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci#ifdef CONFIG_INET
17358c2ecf20Sopenharmony_cistatic int vector_inetaddr_event(
17368c2ecf20Sopenharmony_ci	struct notifier_block *this,
17378c2ecf20Sopenharmony_ci	unsigned long event,
17388c2ecf20Sopenharmony_ci	void *ptr)
17398c2ecf20Sopenharmony_ci{
17408c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
17418c2ecf20Sopenharmony_ci}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_cistatic struct notifier_block vector_inetaddr_notifier = {
17448c2ecf20Sopenharmony_ci	.notifier_call		= vector_inetaddr_event,
17458c2ecf20Sopenharmony_ci};
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_cistatic void inet_register(void)
17488c2ecf20Sopenharmony_ci{
17498c2ecf20Sopenharmony_ci	register_inetaddr_notifier(&vector_inetaddr_notifier);
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci#else
17528c2ecf20Sopenharmony_cistatic inline void inet_register(void)
17538c2ecf20Sopenharmony_ci{
17548c2ecf20Sopenharmony_ci}
17558c2ecf20Sopenharmony_ci#endif
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_cistatic int vector_net_init(void)
17588c2ecf20Sopenharmony_ci{
17598c2ecf20Sopenharmony_ci	mconsole_register_dev(&vector_mc);
17608c2ecf20Sopenharmony_ci	inet_register();
17618c2ecf20Sopenharmony_ci	return 0;
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci__initcall(vector_net_init);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci
1768