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, ¶ms, 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, ðtool_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(¨_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