.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * INET An implementation of the TCP/IP protocol suite for the LINUX |
---|
3 | 4 | * operating system. INET is implemented using the BSD Socket |
---|
.. | .. |
---|
22 | 23 | * interface. |
---|
23 | 24 | * Alexey Kuznetsov: Potential hang under some extreme |
---|
24 | 25 | * cases removed. |
---|
25 | | - * |
---|
26 | | - * This program is free software; you can redistribute it and/or |
---|
27 | | - * modify it under the terms of the GNU General Public License |
---|
28 | | - * as published by the Free Software Foundation; either version |
---|
29 | | - * 2 of the License, or (at your option) any later version. |
---|
30 | 26 | */ |
---|
31 | 27 | #include <linux/kernel.h> |
---|
32 | 28 | #include <linux/jiffies.h> |
---|
.. | .. |
---|
59 | 55 | #include <net/net_namespace.h> |
---|
60 | 56 | #include <linux/u64_stats_sync.h> |
---|
61 | 57 | |
---|
62 | | -struct pcpu_lstats { |
---|
63 | | - u64 packets; |
---|
64 | | - u64 bytes; |
---|
65 | | - struct u64_stats_sync syncp; |
---|
66 | | -}; |
---|
| 58 | +/* blackhole_netdev - a device used for dsts that are marked expired! |
---|
| 59 | + * This is global device (instead of per-net-ns) since it's not needed |
---|
| 60 | + * to be per-ns and gets initialized at boot time. |
---|
| 61 | + */ |
---|
| 62 | +struct net_device *blackhole_netdev; |
---|
| 63 | +EXPORT_SYMBOL(blackhole_netdev); |
---|
67 | 64 | |
---|
68 | 65 | /* The higher levels take care of making this non-reentrant (it's |
---|
69 | 66 | * called with bh's disabled). |
---|
.. | .. |
---|
71 | 68 | static netdev_tx_t loopback_xmit(struct sk_buff *skb, |
---|
72 | 69 | struct net_device *dev) |
---|
73 | 70 | { |
---|
74 | | - struct pcpu_lstats *lb_stats; |
---|
75 | 71 | int len; |
---|
76 | 72 | |
---|
77 | 73 | skb_tx_timestamp(skb); |
---|
.. | .. |
---|
88 | 84 | |
---|
89 | 85 | skb->protocol = eth_type_trans(skb, dev); |
---|
90 | 86 | |
---|
91 | | - /* it's OK to use per_cpu_ptr() because BHs are off */ |
---|
92 | | - lb_stats = this_cpu_ptr(dev->lstats); |
---|
93 | | - |
---|
94 | 87 | len = skb->len; |
---|
95 | | - if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { |
---|
96 | | - u64_stats_update_begin(&lb_stats->syncp); |
---|
97 | | - lb_stats->bytes += len; |
---|
98 | | - lb_stats->packets++; |
---|
99 | | - u64_stats_update_end(&lb_stats->syncp); |
---|
100 | | - } |
---|
| 88 | + if (likely(netif_rx(skb) == NET_RX_SUCCESS)) |
---|
| 89 | + dev_lstats_add(dev, len); |
---|
101 | 90 | |
---|
102 | 91 | return NETDEV_TX_OK; |
---|
103 | 92 | } |
---|
104 | 93 | |
---|
105 | | -static void loopback_get_stats64(struct net_device *dev, |
---|
106 | | - struct rtnl_link_stats64 *stats) |
---|
| 94 | +void dev_lstats_read(struct net_device *dev, u64 *packets, u64 *bytes) |
---|
107 | 95 | { |
---|
108 | | - u64 bytes = 0; |
---|
109 | | - u64 packets = 0; |
---|
110 | 96 | int i; |
---|
| 97 | + |
---|
| 98 | + *packets = 0; |
---|
| 99 | + *bytes = 0; |
---|
111 | 100 | |
---|
112 | 101 | for_each_possible_cpu(i) { |
---|
113 | 102 | const struct pcpu_lstats *lb_stats; |
---|
.. | .. |
---|
117 | 106 | lb_stats = per_cpu_ptr(dev->lstats, i); |
---|
118 | 107 | do { |
---|
119 | 108 | start = u64_stats_fetch_begin_irq(&lb_stats->syncp); |
---|
120 | | - tbytes = lb_stats->bytes; |
---|
121 | | - tpackets = lb_stats->packets; |
---|
| 109 | + tpackets = u64_stats_read(&lb_stats->packets); |
---|
| 110 | + tbytes = u64_stats_read(&lb_stats->bytes); |
---|
122 | 111 | } while (u64_stats_fetch_retry_irq(&lb_stats->syncp, start)); |
---|
123 | | - bytes += tbytes; |
---|
124 | | - packets += tpackets; |
---|
| 112 | + *bytes += tbytes; |
---|
| 113 | + *packets += tpackets; |
---|
125 | 114 | } |
---|
| 115 | +} |
---|
| 116 | +EXPORT_SYMBOL(dev_lstats_read); |
---|
| 117 | + |
---|
| 118 | +static void loopback_get_stats64(struct net_device *dev, |
---|
| 119 | + struct rtnl_link_stats64 *stats) |
---|
| 120 | +{ |
---|
| 121 | + u64 packets, bytes; |
---|
| 122 | + |
---|
| 123 | + dev_lstats_read(dev, &packets, &bytes); |
---|
| 124 | + |
---|
126 | 125 | stats->rx_packets = packets; |
---|
127 | 126 | stats->tx_packets = packets; |
---|
128 | 127 | stats->rx_bytes = bytes; |
---|
.. | .. |
---|
134 | 133 | return 1; |
---|
135 | 134 | } |
---|
136 | 135 | |
---|
137 | | -static int loopback_get_ts_info(struct net_device *netdev, |
---|
138 | | - struct ethtool_ts_info *ts_info) |
---|
139 | | -{ |
---|
140 | | - ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | |
---|
141 | | - SOF_TIMESTAMPING_RX_SOFTWARE | |
---|
142 | | - SOF_TIMESTAMPING_SOFTWARE; |
---|
143 | | - |
---|
144 | | - ts_info->phc_index = -1; |
---|
145 | | - |
---|
146 | | - return 0; |
---|
147 | | -}; |
---|
148 | | - |
---|
149 | 136 | static const struct ethtool_ops loopback_ethtool_ops = { |
---|
150 | 137 | .get_link = always_on, |
---|
151 | | - .get_ts_info = loopback_get_ts_info, |
---|
| 138 | + .get_ts_info = ethtool_op_get_ts_info, |
---|
152 | 139 | }; |
---|
153 | 140 | |
---|
154 | 141 | static int loopback_dev_init(struct net_device *dev) |
---|
.. | .. |
---|
172 | 159 | .ndo_set_mac_address = eth_mac_addr, |
---|
173 | 160 | }; |
---|
174 | 161 | |
---|
175 | | -/* The loopback device is special. There is only one instance |
---|
176 | | - * per network namespace. |
---|
177 | | - */ |
---|
178 | | -static void loopback_setup(struct net_device *dev) |
---|
| 162 | +static void gen_lo_setup(struct net_device *dev, |
---|
| 163 | + unsigned int mtu, |
---|
| 164 | + const struct ethtool_ops *eth_ops, |
---|
| 165 | + const struct header_ops *hdr_ops, |
---|
| 166 | + const struct net_device_ops *dev_ops, |
---|
| 167 | + void (*dev_destructor)(struct net_device *dev)) |
---|
179 | 168 | { |
---|
180 | | - dev->mtu = 64 * 1024; |
---|
| 169 | + dev->mtu = mtu; |
---|
181 | 170 | dev->hard_header_len = ETH_HLEN; /* 14 */ |
---|
182 | 171 | dev->min_header_len = ETH_HLEN; /* 14 */ |
---|
183 | 172 | dev->addr_len = ETH_ALEN; /* 6 */ |
---|
.. | .. |
---|
196 | 185 | | NETIF_F_NETNS_LOCAL |
---|
197 | 186 | | NETIF_F_VLAN_CHALLENGED |
---|
198 | 187 | | NETIF_F_LOOPBACK; |
---|
199 | | - dev->ethtool_ops = &loopback_ethtool_ops; |
---|
200 | | - dev->header_ops = ð_header_ops; |
---|
201 | | - dev->netdev_ops = &loopback_ops; |
---|
| 188 | + dev->ethtool_ops = eth_ops; |
---|
| 189 | + dev->header_ops = hdr_ops; |
---|
| 190 | + dev->netdev_ops = dev_ops; |
---|
202 | 191 | dev->needs_free_netdev = true; |
---|
203 | | - dev->priv_destructor = loopback_dev_free; |
---|
| 192 | + dev->priv_destructor = dev_destructor; |
---|
| 193 | +} |
---|
| 194 | + |
---|
| 195 | +/* The loopback device is special. There is only one instance |
---|
| 196 | + * per network namespace. |
---|
| 197 | + */ |
---|
| 198 | +static void loopback_setup(struct net_device *dev) |
---|
| 199 | +{ |
---|
| 200 | + gen_lo_setup(dev, (64 * 1024), &loopback_ethtool_ops, ð_header_ops, |
---|
| 201 | + &loopback_ops, loopback_dev_free); |
---|
204 | 202 | } |
---|
205 | 203 | |
---|
206 | 204 | /* Setup and register the loopback device. */ |
---|
.. | .. |
---|
210 | 208 | int err; |
---|
211 | 209 | |
---|
212 | 210 | err = -ENOMEM; |
---|
213 | | - dev = alloc_netdev(0, "lo", NET_NAME_UNKNOWN, loopback_setup); |
---|
| 211 | + dev = alloc_netdev(0, "lo", NET_NAME_PREDICTABLE, loopback_setup); |
---|
214 | 212 | if (!dev) |
---|
215 | 213 | goto out; |
---|
216 | 214 | |
---|
.. | .. |
---|
235 | 233 | struct pernet_operations __net_initdata loopback_net_ops = { |
---|
236 | 234 | .init = loopback_net_init, |
---|
237 | 235 | }; |
---|
| 236 | + |
---|
| 237 | +/* blackhole netdevice */ |
---|
| 238 | +static netdev_tx_t blackhole_netdev_xmit(struct sk_buff *skb, |
---|
| 239 | + struct net_device *dev) |
---|
| 240 | +{ |
---|
| 241 | + kfree_skb(skb); |
---|
| 242 | + net_warn_ratelimited("%s(): Dropping skb.\n", __func__); |
---|
| 243 | + return NETDEV_TX_OK; |
---|
| 244 | +} |
---|
| 245 | + |
---|
| 246 | +static const struct net_device_ops blackhole_netdev_ops = { |
---|
| 247 | + .ndo_start_xmit = blackhole_netdev_xmit, |
---|
| 248 | +}; |
---|
| 249 | + |
---|
| 250 | +/* This is a dst-dummy device used specifically for invalidated |
---|
| 251 | + * DSTs and unlike loopback, this is not per-ns. |
---|
| 252 | + */ |
---|
| 253 | +static void blackhole_netdev_setup(struct net_device *dev) |
---|
| 254 | +{ |
---|
| 255 | + gen_lo_setup(dev, ETH_MIN_MTU, NULL, NULL, &blackhole_netdev_ops, NULL); |
---|
| 256 | +} |
---|
| 257 | + |
---|
| 258 | +/* Setup and register the blackhole_netdev. */ |
---|
| 259 | +static int __init blackhole_netdev_init(void) |
---|
| 260 | +{ |
---|
| 261 | + blackhole_netdev = alloc_netdev(0, "blackhole_dev", NET_NAME_UNKNOWN, |
---|
| 262 | + blackhole_netdev_setup); |
---|
| 263 | + if (!blackhole_netdev) |
---|
| 264 | + return -ENOMEM; |
---|
| 265 | + |
---|
| 266 | + rtnl_lock(); |
---|
| 267 | + dev_init_scheduler(blackhole_netdev); |
---|
| 268 | + dev_activate(blackhole_netdev); |
---|
| 269 | + rtnl_unlock(); |
---|
| 270 | + |
---|
| 271 | + blackhole_netdev->flags |= IFF_UP | IFF_RUNNING; |
---|
| 272 | + dev_net_set(blackhole_netdev, &init_net); |
---|
| 273 | + |
---|
| 274 | + return 0; |
---|
| 275 | +} |
---|
| 276 | + |
---|
| 277 | +device_initcall(blackhole_netdev_init); |
---|