Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at:
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #include <config.h>
18 : :
19 : : #include "netdev-linux.h"
20 : :
21 : : #include <errno.h>
22 : : #include <fcntl.h>
23 : : #include <arpa/inet.h>
24 : : #include <inttypes.h>
25 : : #include <linux/filter.h>
26 : : #include <linux/gen_stats.h>
27 : : #include <linux/if_ether.h>
28 : : #include <linux/if_tun.h>
29 : : #include <linux/types.h>
30 : : #include <linux/ethtool.h>
31 : : #include <linux/mii.h>
32 : : #include <linux/pkt_cls.h>
33 : : #include <linux/pkt_sched.h>
34 : : #include <linux/rtnetlink.h>
35 : : #include <linux/sockios.h>
36 : : #include <sys/types.h>
37 : : #include <sys/ioctl.h>
38 : : #include <sys/socket.h>
39 : : #include <sys/utsname.h>
40 : : #include <netpacket/packet.h>
41 : : #include <net/if.h>
42 : : #include <net/if_arp.h>
43 : : #include <net/if_packet.h>
44 : : #include <net/route.h>
45 : : #include <netinet/in.h>
46 : : #include <poll.h>
47 : : #include <stdlib.h>
48 : : #include <string.h>
49 : : #include <unistd.h>
50 : :
51 : : #include "coverage.h"
52 : : #include "dp-packet.h"
53 : : #include "dpif-netlink.h"
54 : : #include "dpif-netdev.h"
55 : : #include "openvswitch/dynamic-string.h"
56 : : #include "fatal-signal.h"
57 : : #include "hash.h"
58 : : #include "openvswitch/hmap.h"
59 : : #include "netdev-provider.h"
60 : : #include "netdev-vport.h"
61 : : #include "netlink-notifier.h"
62 : : #include "netlink-socket.h"
63 : : #include "netlink.h"
64 : : #include "openvswitch/ofpbuf.h"
65 : : #include "openflow/openflow.h"
66 : : #include "ovs-atomic.h"
67 : : #include "packets.h"
68 : : #include "poll-loop.h"
69 : : #include "rtnetlink.h"
70 : : #include "openvswitch/shash.h"
71 : : #include "socket-util.h"
72 : : #include "sset.h"
73 : : #include "timer.h"
74 : : #include "unaligned.h"
75 : : #include "openvswitch/vlog.h"
76 : : #include "util.h"
77 : :
78 : 20190 : VLOG_DEFINE_THIS_MODULE(netdev_linux);
79 : :
80 : 98358 : COVERAGE_DEFINE(netdev_set_policing);
81 : 92814 : COVERAGE_DEFINE(netdev_arp_lookup);
82 : 94890 : COVERAGE_DEFINE(netdev_get_ifindex);
83 : 109716 : COVERAGE_DEFINE(netdev_get_hwaddr);
84 : 93468 : COVERAGE_DEFINE(netdev_set_hwaddr);
85 : 99984 : COVERAGE_DEFINE(netdev_get_ethtool);
86 : 93720 : COVERAGE_DEFINE(netdev_set_ethtool);
87 : :
88 : :
89 : : /* These were introduced in Linux 2.6.14, so they might be missing if we have
90 : : * old headers. */
91 : : #ifndef ADVERTISED_Pause
92 : : #define ADVERTISED_Pause (1 << 13)
93 : : #endif
94 : : #ifndef ADVERTISED_Asym_Pause
95 : : #define ADVERTISED_Asym_Pause (1 << 14)
96 : : #endif
97 : :
98 : : /* These were introduced in Linux 2.6.24, so they might be missing if we
99 : : * have old headers. */
100 : : #ifndef ETHTOOL_GFLAGS
101 : : #define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */
102 : : #endif
103 : : #ifndef ETHTOOL_SFLAGS
104 : : #define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */
105 : : #endif
106 : :
107 : : /* This was introduced in Linux 2.6.25, so it might be missing if we have old
108 : : * headers. */
109 : : #ifndef TC_RTAB_SIZE
110 : : #define TC_RTAB_SIZE 1024
111 : : #endif
112 : :
113 : : /* Linux 2.6.21 introduced struct tpacket_auxdata.
114 : : * Linux 2.6.27 added the tp_vlan_tci member.
115 : : * Linux 3.0 defined TP_STATUS_VLAN_VALID.
116 : : * Linux 3.13 repurposed a padding member for tp_vlan_tpid and defined
117 : : * TP_STATUS_VLAN_TPID_VALID.
118 : : *
119 : : * With all this churn it's easiest to unconditionally define a replacement
120 : : * structure that has everything we want.
121 : : */
122 : : #ifndef PACKET_AUXDATA
123 : : #define PACKET_AUXDATA 8
124 : : #endif
125 : : #ifndef TP_STATUS_VLAN_VALID
126 : : #define TP_STATUS_VLAN_VALID (1 << 4)
127 : : #endif
128 : : #ifndef TP_STATUS_VLAN_TPID_VALID
129 : : #define TP_STATUS_VLAN_TPID_VALID (1 << 6)
130 : : #endif
131 : : #undef tpacket_auxdata
132 : : #define tpacket_auxdata rpl_tpacket_auxdata
133 : : struct tpacket_auxdata {
134 : : uint32_t tp_status;
135 : : uint32_t tp_len;
136 : : uint32_t tp_snaplen;
137 : : uint16_t tp_mac;
138 : : uint16_t tp_net;
139 : : uint16_t tp_vlan_tci;
140 : : uint16_t tp_vlan_tpid;
141 : : };
142 : :
143 : : /* Linux 2.6.27 introduced ethtool_cmd_speed
144 : : *
145 : : * To avoid revisiting problems reported with using configure to detect
146 : : * compatibility (see report at
147 : : * http://openvswitch.org/pipermail/dev/2014-October/047978.html)
148 : : * unconditionally replace ethtool_cmd_speed. */
149 : : #define ethtool_cmd_speed rpl_ethtool_cmd_speed
150 : 776 : static inline uint32_t rpl_ethtool_cmd_speed(const struct ethtool_cmd *ep)
151 : : {
152 : 776 : return ep->speed | (ep->speed_hi << 16);
153 : : }
154 : :
155 : : /* Linux 2.6.30 introduced supported and advertised flags for
156 : : * 1G base KX, and 10G base KX4, KR and R. */
157 : : #ifndef SUPPORTED_1000baseKX_Full
158 : : #define SUPPORTED_1000baseKX_Full (1 << 17)
159 : : #define SUPPORTED_10000baseKX4_Full (1 << 18)
160 : : #define SUPPORTED_10000baseKR_Full (1 << 19)
161 : : #define SUPPORTED_10000baseR_FEC (1 << 20)
162 : : #define ADVERTISED_1000baseKX_Full (1 << 17)
163 : : #define ADVERTISED_10000baseKX4_Full (1 << 18)
164 : : #define ADVERTISED_10000baseKR_Full (1 << 19)
165 : : #define ADVERTISED_10000baseR_FEC (1 << 20)
166 : : #endif
167 : :
168 : : /* Linux 3.5 introduced supported and advertised flags for
169 : : * 40G base KR4, CR4, SR4 and LR4. */
170 : : #ifndef SUPPORTED_40000baseKR4_Full
171 : : #define SUPPORTED_40000baseKR4_Full (1 << 23)
172 : : #define SUPPORTED_40000baseCR4_Full (1 << 24)
173 : : #define SUPPORTED_40000baseSR4_Full (1 << 25)
174 : : #define SUPPORTED_40000baseLR4_Full (1 << 26)
175 : : #define ADVERTISED_40000baseKR4_Full (1 << 23)
176 : : #define ADVERTISED_40000baseCR4_Full (1 << 24)
177 : : #define ADVERTISED_40000baseSR4_Full (1 << 25)
178 : : #define ADVERTISED_40000baseLR4_Full (1 << 26)
179 : : #endif
180 : :
181 : : /* Linux 2.6.35 introduced IFLA_STATS64 and rtnl_link_stats64.
182 : : *
183 : : * Tests for rtnl_link_stats64 don't seem to consistently work, e.g. on
184 : : * 2.6.32-431.29.2.el6.x86_64 (see report at
185 : : * http://openvswitch.org/pipermail/dev/2014-October/047978.html). Maybe
186 : : * if_link.h is not self-contained on those kernels. It is easiest to
187 : : * unconditionally define a replacement. */
188 : : #ifndef IFLA_STATS64
189 : : #define IFLA_STATS64 23
190 : : #endif
191 : : #define rtnl_link_stats64 rpl_rtnl_link_stats64
192 : : struct rtnl_link_stats64 {
193 : : uint64_t rx_packets;
194 : : uint64_t tx_packets;
195 : : uint64_t rx_bytes;
196 : : uint64_t tx_bytes;
197 : : uint64_t rx_errors;
198 : : uint64_t tx_errors;
199 : : uint64_t rx_dropped;
200 : : uint64_t tx_dropped;
201 : : uint64_t multicast;
202 : : uint64_t collisions;
203 : :
204 : : uint64_t rx_length_errors;
205 : : uint64_t rx_over_errors;
206 : : uint64_t rx_crc_errors;
207 : : uint64_t rx_frame_errors;
208 : : uint64_t rx_fifo_errors;
209 : : uint64_t rx_missed_errors;
210 : :
211 : : uint64_t tx_aborted_errors;
212 : : uint64_t tx_carrier_errors;
213 : : uint64_t tx_fifo_errors;
214 : : uint64_t tx_heartbeat_errors;
215 : : uint64_t tx_window_errors;
216 : :
217 : : uint64_t rx_compressed;
218 : : uint64_t tx_compressed;
219 : : };
220 : :
221 : : enum {
222 : : VALID_IFINDEX = 1 << 0,
223 : : VALID_ETHERADDR = 1 << 1,
224 : : VALID_IN = 1 << 2,
225 : : VALID_MTU = 1 << 3,
226 : : VALID_POLICING = 1 << 4,
227 : : VALID_VPORT_STAT_ERROR = 1 << 5,
228 : : VALID_DRVINFO = 1 << 6,
229 : : VALID_FEATURES = 1 << 7,
230 : : };
231 : :
232 : : /* Traffic control. */
233 : :
234 : : /* An instance of a traffic control class. Always associated with a particular
235 : : * network device.
236 : : *
237 : : * Each TC implementation subclasses this with whatever additional data it
238 : : * needs. */
239 : : struct tc {
240 : : const struct tc_ops *ops;
241 : : struct hmap queues; /* Contains "struct tc_queue"s.
242 : : * Read by generic TC layer.
243 : : * Written only by TC implementation. */
244 : : };
245 : :
246 : : #define TC_INITIALIZER(TC, OPS) { OPS, HMAP_INITIALIZER(&(TC)->queues) }
247 : :
248 : : /* One traffic control queue.
249 : : *
250 : : * Each TC implementation subclasses this with whatever additional data it
251 : : * needs. */
252 : : struct tc_queue {
253 : : struct hmap_node hmap_node; /* In struct tc's "queues" hmap. */
254 : : unsigned int queue_id; /* OpenFlow queue ID. */
255 : : long long int created; /* Time queue was created, in msecs. */
256 : : };
257 : :
258 : : /* A particular kind of traffic control. Each implementation generally maps to
259 : : * one particular Linux qdisc class.
260 : : *
261 : : * The functions below return 0 if successful or a positive errno value on
262 : : * failure, except where otherwise noted. All of them must be provided, except
263 : : * where otherwise noted. */
264 : : struct tc_ops {
265 : : /* Name used by kernel in the TCA_KIND attribute of tcmsg, e.g. "htb".
266 : : * This is null for tc_ops_default and tc_ops_other, for which there are no
267 : : * appropriate values. */
268 : : const char *linux_name;
269 : :
270 : : /* Name used in OVS database, e.g. "linux-htb". Must be nonnull. */
271 : : const char *ovs_name;
272 : :
273 : : /* Number of supported OpenFlow queues, 0 for qdiscs that have no
274 : : * queues. The queues are numbered 0 through n_queues - 1. */
275 : : unsigned int n_queues;
276 : :
277 : : /* Called to install this TC class on 'netdev'. The implementation should
278 : : * make the Netlink calls required to set up 'netdev' with the right qdisc
279 : : * and configure it according to 'details'. The implementation may assume
280 : : * that the current qdisc is the default; that is, there is no need for it
281 : : * to delete the current qdisc before installing itself.
282 : : *
283 : : * The contents of 'details' should be documented as valid for 'ovs_name'
284 : : * in the "other_config" column in the "QoS" table in vswitchd/vswitch.xml
285 : : * (which is built as ovs-vswitchd.conf.db(8)).
286 : : *
287 : : * This function must return 0 if and only if it sets 'netdev->tc' to an
288 : : * initialized 'struct tc'.
289 : : *
290 : : * (This function is null for tc_ops_other, which cannot be installed. For
291 : : * other TC classes it should always be nonnull.) */
292 : : int (*tc_install)(struct netdev *netdev, const struct smap *details);
293 : :
294 : : /* Called when the netdev code determines (through a Netlink query) that
295 : : * this TC class's qdisc is installed on 'netdev', but we didn't install
296 : : * it ourselves and so don't know any of the details.
297 : : *
298 : : * 'nlmsg' is the kernel reply to a RTM_GETQDISC Netlink message for
299 : : * 'netdev'. The TCA_KIND attribute of 'nlmsg' is 'linux_name'. The
300 : : * implementation should parse the other attributes of 'nlmsg' as
301 : : * necessary to determine its configuration. If necessary it should also
302 : : * use Netlink queries to determine the configuration of queues on
303 : : * 'netdev'.
304 : : *
305 : : * This function must return 0 if and only if it sets 'netdev->tc' to an
306 : : * initialized 'struct tc'. */
307 : : int (*tc_load)(struct netdev *netdev, struct ofpbuf *nlmsg);
308 : :
309 : : /* Destroys the data structures allocated by the implementation as part of
310 : : * 'tc'. (This includes destroying 'tc->queues' by calling
311 : : * tc_destroy(tc).
312 : : *
313 : : * The implementation should not need to perform any Netlink calls. If
314 : : * desirable, the caller is responsible for deconfiguring the kernel qdisc.
315 : : * (But it may not be desirable.)
316 : : *
317 : : * This function may be null if 'tc' is trivial. */
318 : : void (*tc_destroy)(struct tc *tc);
319 : :
320 : : /* Retrieves details of 'netdev->tc' configuration into 'details'.
321 : : *
322 : : * The implementation should not need to perform any Netlink calls, because
323 : : * the 'tc_install' or 'tc_load' that instantiated 'netdev->tc' should have
324 : : * cached the configuration.
325 : : *
326 : : * The contents of 'details' should be documented as valid for 'ovs_name'
327 : : * in the "other_config" column in the "QoS" table in vswitchd/vswitch.xml
328 : : * (which is built as ovs-vswitchd.conf.db(8)).
329 : : *
330 : : * This function may be null if 'tc' is not configurable.
331 : : */
332 : : int (*qdisc_get)(const struct netdev *netdev, struct smap *details);
333 : :
334 : : /* Reconfigures 'netdev->tc' according to 'details', performing any
335 : : * required Netlink calls to complete the reconfiguration.
336 : : *
337 : : * The contents of 'details' should be documented as valid for 'ovs_name'
338 : : * in the "other_config" column in the "QoS" table in vswitchd/vswitch.xml
339 : : * (which is built as ovs-vswitchd.conf.db(8)).
340 : : *
341 : : * This function may be null if 'tc' is not configurable.
342 : : */
343 : : int (*qdisc_set)(struct netdev *, const struct smap *details);
344 : :
345 : : /* Retrieves details of 'queue' on 'netdev->tc' into 'details'. 'queue' is
346 : : * one of the 'struct tc_queue's within 'netdev->tc->queues'.
347 : : *
348 : : * The contents of 'details' should be documented as valid for 'ovs_name'
349 : : * in the "other_config" column in the "Queue" table in
350 : : * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
351 : : *
352 : : * The implementation should not need to perform any Netlink calls, because
353 : : * the 'tc_install' or 'tc_load' that instantiated 'netdev->tc' should have
354 : : * cached the queue configuration.
355 : : *
356 : : * This function may be null if 'tc' does not have queues ('n_queues' is
357 : : * 0). */
358 : : int (*class_get)(const struct netdev *netdev, const struct tc_queue *queue,
359 : : struct smap *details);
360 : :
361 : : /* Configures or reconfigures 'queue_id' on 'netdev->tc' according to
362 : : * 'details', perfoming any required Netlink calls to complete the
363 : : * reconfiguration. The caller ensures that 'queue_id' is less than
364 : : * 'n_queues'.
365 : : *
366 : : * The contents of 'details' should be documented as valid for 'ovs_name'
367 : : * in the "other_config" column in the "Queue" table in
368 : : * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
369 : : *
370 : : * This function may be null if 'tc' does not have queues or its queues are
371 : : * not configurable. */
372 : : int (*class_set)(struct netdev *, unsigned int queue_id,
373 : : const struct smap *details);
374 : :
375 : : /* Deletes 'queue' from 'netdev->tc'. 'queue' is one of the 'struct
376 : : * tc_queue's within 'netdev->tc->queues'.
377 : : *
378 : : * This function may be null if 'tc' does not have queues or its queues
379 : : * cannot be deleted. */
380 : : int (*class_delete)(struct netdev *, struct tc_queue *queue);
381 : :
382 : : /* Obtains stats for 'queue' from 'netdev->tc'. 'queue' is one of the
383 : : * 'struct tc_queue's within 'netdev->tc->queues'.
384 : : *
385 : : * On success, initializes '*stats'.
386 : : *
387 : : * This function may be null if 'tc' does not have queues or if it cannot
388 : : * report queue statistics. */
389 : : int (*class_get_stats)(const struct netdev *netdev,
390 : : const struct tc_queue *queue,
391 : : struct netdev_queue_stats *stats);
392 : :
393 : : /* Extracts queue stats from 'nlmsg', which is a response to a
394 : : * RTM_GETTCLASS message, and passes them to 'cb' along with 'aux'.
395 : : *
396 : : * This function may be null if 'tc' does not have queues or if it cannot
397 : : * report queue statistics. */
398 : : int (*class_dump_stats)(const struct netdev *netdev,
399 : : const struct ofpbuf *nlmsg,
400 : : netdev_dump_queue_stats_cb *cb, void *aux);
401 : : };
402 : :
403 : : static void
404 : 233 : tc_init(struct tc *tc, const struct tc_ops *ops)
405 : : {
406 : 233 : tc->ops = ops;
407 : 233 : hmap_init(&tc->queues);
408 : 233 : }
409 : :
410 : : static void
411 : 233 : tc_destroy(struct tc *tc)
412 : : {
413 : 233 : hmap_destroy(&tc->queues);
414 : 233 : }
415 : :
416 : : static const struct tc_ops tc_ops_htb;
417 : : static const struct tc_ops tc_ops_hfsc;
418 : : static const struct tc_ops tc_ops_codel;
419 : : static const struct tc_ops tc_ops_fqcodel;
420 : : static const struct tc_ops tc_ops_sfq;
421 : : static const struct tc_ops tc_ops_default;
422 : : static const struct tc_ops tc_ops_noop;
423 : : static const struct tc_ops tc_ops_other;
424 : :
425 : : static const struct tc_ops *const tcs[] = {
426 : : &tc_ops_htb, /* Hierarchy token bucket (see tc-htb(8)). */
427 : : &tc_ops_hfsc, /* Hierarchical fair service curve. */
428 : : &tc_ops_codel, /* Controlled delay */
429 : : &tc_ops_fqcodel, /* Fair queue controlled delay */
430 : : &tc_ops_sfq, /* Stochastic fair queueing */
431 : : &tc_ops_noop, /* Non operating qos type. */
432 : : &tc_ops_default, /* Default qdisc (see tc-pfifo_fast(8)). */
433 : : &tc_ops_other, /* Some other qdisc. */
434 : : NULL
435 : : };
436 : :
437 : : static unsigned int tc_make_handle(unsigned int major, unsigned int minor);
438 : : static unsigned int tc_get_major(unsigned int handle);
439 : : static unsigned int tc_get_minor(unsigned int handle);
440 : :
441 : : static unsigned int tc_ticks_to_bytes(unsigned int rate, unsigned int ticks);
442 : : static unsigned int tc_bytes_to_ticks(unsigned int rate, unsigned int size);
443 : : static unsigned int tc_buffer_per_jiffy(unsigned int rate);
444 : :
445 : : static struct tcmsg *tc_make_request(const struct netdev *, int type,
446 : : unsigned int flags, struct ofpbuf *);
447 : : static int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
448 : : static int tc_add_del_ingress_qdisc(struct netdev *netdev, bool add);
449 : : static int tc_add_policer(struct netdev *,
450 : : uint32_t kbits_rate, uint32_t kbits_burst);
451 : :
452 : : static int tc_parse_qdisc(const struct ofpbuf *, const char **kind,
453 : : struct nlattr **options);
454 : : static int tc_parse_class(const struct ofpbuf *, unsigned int *queue_id,
455 : : struct nlattr **options,
456 : : struct netdev_queue_stats *);
457 : : static int tc_query_class(const struct netdev *,
458 : : unsigned int handle, unsigned int parent,
459 : : struct ofpbuf **replyp);
460 : : static int tc_delete_class(const struct netdev *, unsigned int handle);
461 : :
462 : : static int tc_del_qdisc(struct netdev *netdev);
463 : : static int tc_query_qdisc(const struct netdev *netdev);
464 : :
465 : : static int tc_calc_cell_log(unsigned int mtu);
466 : : static void tc_fill_rate(struct tc_ratespec *rate, uint64_t bps, int mtu);
467 : : static void tc_put_rtab(struct ofpbuf *, uint16_t type,
468 : : const struct tc_ratespec *rate);
469 : : static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes);
470 : :
471 : : struct netdev_linux {
472 : : struct netdev up;
473 : :
474 : : /* Protects all members below. */
475 : : struct ovs_mutex mutex;
476 : :
477 : : unsigned int cache_valid;
478 : :
479 : : bool miimon; /* Link status of last poll. */
480 : : long long int miimon_interval; /* Miimon Poll rate. Disabled if <= 0. */
481 : : struct timer miimon_timer;
482 : :
483 : : /* The following are figured out "on demand" only. They are only valid
484 : : * when the corresponding VALID_* bit in 'cache_valid' is set. */
485 : : int ifindex;
486 : : struct eth_addr etheraddr;
487 : : int mtu;
488 : : unsigned int ifi_flags;
489 : : long long int carrier_resets;
490 : : uint32_t kbits_rate; /* Policing data. */
491 : : uint32_t kbits_burst;
492 : : int vport_stats_error; /* Cached error code from vport_get_stats().
493 : : 0 or an errno value. */
494 : : int netdev_mtu_error; /* Cached error code from SIOCGIFMTU or SIOCSIFMTU. */
495 : : int ether_addr_error; /* Cached error code from set/get etheraddr. */
496 : : int netdev_policing_error; /* Cached error code from set policing. */
497 : : int get_features_error; /* Cached error code from ETHTOOL_GSET. */
498 : : int get_ifindex_error; /* Cached error code from SIOCGIFINDEX. */
499 : :
500 : : enum netdev_features current; /* Cached from ETHTOOL_GSET. */
501 : : enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */
502 : : enum netdev_features supported; /* Cached from ETHTOOL_GSET. */
503 : :
504 : : struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */
505 : : struct tc *tc;
506 : :
507 : : /* For devices of class netdev_tap_class only. */
508 : : int tap_fd;
509 : : };
510 : :
511 : : struct netdev_rxq_linux {
512 : : struct netdev_rxq up;
513 : : bool is_tap;
514 : : int fd;
515 : : };
516 : :
517 : : /* This is set pretty low because we probably won't learn anything from the
518 : : * additional log messages. */
519 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
520 : :
521 : : /* Polling miimon status for all ports causes performance degradation when
522 : : * handling a large number of ports. If there are no devices using miimon, then
523 : : * we skip netdev_linux_miimon_run() and netdev_linux_miimon_wait().
524 : : *
525 : : * Readers do not depend on this variable synchronizing with the related
526 : : * changes in the device miimon status, so we can use atomic_count. */
527 : : static atomic_count miimon_cnt = ATOMIC_COUNT_INIT(0);
528 : :
529 : : static void netdev_linux_run(const struct netdev_class *);
530 : :
531 : : static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *,
532 : : int cmd, const char *cmd_name);
533 : : static int get_flags(const struct netdev *, unsigned int *flags);
534 : : static int set_flags(const char *, unsigned int flags);
535 : : static int update_flags(struct netdev_linux *netdev, enum netdev_flags off,
536 : : enum netdev_flags on, enum netdev_flags *old_flagsp)
537 : : OVS_REQUIRES(netdev->mutex);
538 : : static int do_get_ifindex(const char *netdev_name);
539 : : static int get_ifindex(const struct netdev *, int *ifindexp);
540 : : static int do_set_addr(struct netdev *netdev,
541 : : int ioctl_nr, const char *ioctl_name,
542 : : struct in_addr addr);
543 : : static int get_etheraddr(const char *netdev_name, struct eth_addr *ea);
544 : : static int set_etheraddr(const char *netdev_name, const struct eth_addr);
545 : : static int get_stats_via_netlink(const struct netdev *, struct netdev_stats *);
546 : : static int af_packet_sock(void);
547 : : static bool netdev_linux_miimon_enabled(void);
548 : : static void netdev_linux_miimon_run(void);
549 : : static void netdev_linux_miimon_wait(void);
550 : : static int netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup);
551 : :
552 : : static bool
553 : 285309 : is_netdev_linux_class(const struct netdev_class *netdev_class)
554 : : {
555 : 285309 : return netdev_class->run == netdev_linux_run;
556 : : }
557 : :
558 : : static bool
559 : 1548 : is_tap_netdev(const struct netdev *netdev)
560 : : {
561 : 1548 : return netdev_get_class(netdev) == &netdev_tap_class;
562 : : }
563 : :
564 : : static struct netdev_linux *
565 : 247830 : netdev_linux_cast(const struct netdev *netdev)
566 : : {
567 [ - + ]: 247830 : ovs_assert(is_netdev_linux_class(netdev_get_class(netdev)));
568 : :
569 : 247830 : return CONTAINER_OF(netdev, struct netdev_linux, up);
570 : : }
571 : :
572 : : static struct netdev_rxq_linux *
573 : 35928 : netdev_rxq_linux_cast(const struct netdev_rxq *rx)
574 : : {
575 [ - + ]: 35928 : ovs_assert(is_netdev_linux_class(netdev_get_class(rx->netdev)));
576 : 35928 : return CONTAINER_OF(rx, struct netdev_rxq_linux, up);
577 : : }
578 : :
579 : : static void netdev_linux_update(struct netdev_linux *netdev,
580 : : const struct rtnetlink_change *)
581 : : OVS_REQUIRES(netdev->mutex);
582 : : static void netdev_linux_changed(struct netdev_linux *netdev,
583 : : unsigned int ifi_flags, unsigned int mask)
584 : : OVS_REQUIRES(netdev->mutex);
585 : :
586 : : /* Returns a NETLINK_ROUTE socket listening for RTNLGRP_LINK,
587 : : * RTNLGRP_IPV4_IFADDR and RTNLGRP_IPV6_IFADDR changes, or NULL
588 : : * if no such socket could be created. */
589 : : static struct nl_sock *
590 : 563416 : netdev_linux_notify_sock(void)
591 : : {
592 : : static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
593 : : static struct nl_sock *sock;
594 : 563416 : unsigned int mcgroups[] = {RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR,
595 : : RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_IFINFO};
596 : :
597 [ + + ]: 563416 : if (ovsthread_once_start(&once)) {
598 : : int error;
599 : :
600 : 616 : error = nl_sock_create(NETLINK_ROUTE, &sock);
601 [ + - ]: 616 : if (!error) {
602 : : size_t i;
603 : :
604 [ + + ]: 3080 : for (i = 0; i < ARRAY_SIZE(mcgroups); i++) {
605 : 2464 : error = nl_sock_join_mcgroup(sock, mcgroups[i]);
606 [ - + ]: 2464 : if (error) {
607 : 0 : nl_sock_destroy(sock);
608 : 0 : sock = NULL;
609 : 0 : break;
610 : : }
611 : : }
612 : : }
613 : 616 : ovsthread_once_done(&once);
614 : : }
615 : :
616 : 563416 : return sock;
617 : : }
618 : :
619 : : static bool
620 : 563416 : netdev_linux_miimon_enabled(void)
621 : : {
622 : 563416 : return atomic_count_get(&miimon_cnt) > 0;
623 : : }
624 : :
625 : : static void
626 : 281708 : netdev_linux_run(const struct netdev_class *netdev_class OVS_UNUSED)
627 : : {
628 : : struct nl_sock *sock;
629 : : int error;
630 : :
631 [ - + ]: 281708 : if (netdev_linux_miimon_enabled()) {
632 : 0 : netdev_linux_miimon_run();
633 : : }
634 : :
635 : 281708 : sock = netdev_linux_notify_sock();
636 [ - + ]: 281708 : if (!sock) {
637 : 0 : return;
638 : : }
639 : :
640 : : do {
641 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
642 : : uint64_t buf_stub[4096 / 8];
643 : : struct ofpbuf buf;
644 : :
645 : 284383 : ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
646 : 284383 : error = nl_sock_recv(sock, &buf, false);
647 [ + + ]: 284383 : if (!error) {
648 : : struct rtnetlink_change change;
649 : :
650 [ + - ]: 2675 : if (rtnetlink_parse(&buf, &change)) {
651 : 2675 : struct netdev *netdev_ = NULL;
652 : : char dev_name[IFNAMSIZ];
653 : :
654 [ + + ]: 2675 : if (!change.ifname) {
655 : 190 : change.ifname = if_indextoname(change.if_index, dev_name);
656 : : }
657 : :
658 [ + - ]: 2675 : if (change.ifname) {
659 : 2675 : netdev_ = netdev_from_name(change.ifname);
660 : : }
661 [ + + ][ + - ]: 2675 : if (netdev_ && is_netdev_linux_class(netdev_->netdev_class)) {
662 : 1551 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
663 : :
664 : 1551 : ovs_mutex_lock(&netdev->mutex);
665 : 1551 : netdev_linux_update(netdev, &change);
666 : 1551 : ovs_mutex_unlock(&netdev->mutex);
667 : : }
668 : 2675 : netdev_close(netdev_);
669 : : }
670 [ - + ]: 281708 : } else if (error == ENOBUFS) {
671 : : struct shash device_shash;
672 : : struct shash_node *node;
673 : :
674 : 0 : nl_sock_drain(sock);
675 : :
676 : 0 : shash_init(&device_shash);
677 : 0 : netdev_get_devices(&netdev_linux_class, &device_shash);
678 [ # # ][ # # ]: 0 : SHASH_FOR_EACH (node, &device_shash) {
679 : 0 : struct netdev *netdev_ = node->data;
680 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
681 : : unsigned int flags;
682 : :
683 : 0 : ovs_mutex_lock(&netdev->mutex);
684 : 0 : get_flags(netdev_, &flags);
685 : 0 : netdev_linux_changed(netdev, flags, 0);
686 : 0 : ovs_mutex_unlock(&netdev->mutex);
687 : :
688 : 0 : netdev_close(netdev_);
689 : : }
690 : 0 : shash_destroy(&device_shash);
691 [ - + ]: 281708 : } else if (error != EAGAIN) {
692 [ # # ]: 0 : VLOG_WARN_RL(&rl, "error reading or parsing netlink (%s)",
693 : : ovs_strerror(error));
694 : : }
695 : 284383 : ofpbuf_uninit(&buf);
696 [ + + ]: 284383 : } while (!error);
697 : : }
698 : :
699 : : static void
700 : 281708 : netdev_linux_wait(const struct netdev_class *netdev_class OVS_UNUSED)
701 : : {
702 : : struct nl_sock *sock;
703 : :
704 [ - + ]: 281708 : if (netdev_linux_miimon_enabled()) {
705 : 0 : netdev_linux_miimon_wait();
706 : : }
707 : 281708 : sock = netdev_linux_notify_sock();
708 [ + - ]: 281708 : if (sock) {
709 : 281708 : nl_sock_wait(sock, POLLIN);
710 : : }
711 : 281708 : }
712 : :
713 : : static void
714 : 1551 : netdev_linux_changed(struct netdev_linux *dev,
715 : : unsigned int ifi_flags, unsigned int mask)
716 : : OVS_REQUIRES(dev->mutex)
717 : : {
718 : 1551 : netdev_change_seq_changed(&dev->up);
719 : :
720 [ + + ]: 1551 : if ((dev->ifi_flags ^ ifi_flags) & IFF_RUNNING) {
721 : 105 : dev->carrier_resets++;
722 : : }
723 : 1551 : dev->ifi_flags = ifi_flags;
724 : :
725 : 1551 : dev->cache_valid &= mask;
726 [ + + ]: 1551 : if (!(mask & VALID_IN)) {
727 : 210 : netdev_get_addrs_list_flush();
728 : : }
729 : 1551 : }
730 : :
731 : : static void
732 : 1551 : netdev_linux_update(struct netdev_linux *dev,
733 : : const struct rtnetlink_change *change)
734 : : OVS_REQUIRES(dev->mutex)
735 : : {
736 [ + + ]: 1551 : if (rtnetlink_type_is_rtnlgrp_link(change->nlmsg_type)){
737 [ + + ]: 1343 : if (change->nlmsg_type == RTM_NEWLINK) {
738 : : /* Keep drv-info, and ip addresses. */
739 : 1341 : netdev_linux_changed(dev, change->ifi_flags,
740 : : VALID_DRVINFO | VALID_IN);
741 : :
742 : : /* Update netdev from rtnl-change msg. */
743 [ + - ]: 1341 : if (change->mtu) {
744 : 1341 : dev->mtu = change->mtu;
745 : 1341 : dev->cache_valid |= VALID_MTU;
746 : 1341 : dev->netdev_mtu_error = 0;
747 : : }
748 : :
749 [ + - ]: 1341 : if (!eth_addr_is_zero(change->mac)) {
750 : 1341 : dev->etheraddr = change->mac;
751 : 1341 : dev->cache_valid |= VALID_ETHERADDR;
752 : 1341 : dev->ether_addr_error = 0;
753 : : }
754 : :
755 : 1341 : dev->ifindex = change->if_index;
756 : 1341 : dev->cache_valid |= VALID_IFINDEX;
757 : 1341 : dev->get_ifindex_error = 0;
758 : : } else {
759 : 1343 : netdev_linux_changed(dev, change->ifi_flags, 0);
760 : : }
761 [ + - ]: 208 : } else if (rtnetlink_type_is_rtnlgrp_addr(change->nlmsg_type)) {
762 : : /* Invalidates in4, in6. */
763 : 208 : netdev_linux_changed(dev, dev->ifi_flags, ~VALID_IN);
764 : : } else {
765 : 0 : OVS_NOT_REACHED();
766 : : }
767 : 1551 : }
768 : :
769 : : static struct netdev *
770 : 25396 : netdev_linux_alloc(void)
771 : : {
772 : 25396 : struct netdev_linux *netdev = xzalloc(sizeof *netdev);
773 : 25396 : return &netdev->up;
774 : : }
775 : :
776 : : static void
777 : 25396 : netdev_linux_common_construct(struct netdev_linux *netdev)
778 : : {
779 : 25396 : ovs_mutex_init(&netdev->mutex);
780 : 25396 : }
781 : :
782 : : /* Creates system and internal devices. */
783 : : static int
784 : 25330 : netdev_linux_construct(struct netdev *netdev_)
785 : : {
786 : 25330 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
787 : : int error;
788 : :
789 : 25330 : netdev_linux_common_construct(netdev);
790 : :
791 : 25330 : error = get_flags(&netdev->up, &netdev->ifi_flags);
792 [ + + ]: 25330 : if (error == ENODEV) {
793 [ - + ]: 76 : if (netdev->up.netdev_class != &netdev_internal_class) {
794 : : /* The device does not exist, so don't allow it to be opened. */
795 : 0 : return ENODEV;
796 : : } else {
797 : : /* "Internal" netdevs have to be created as netdev objects before
798 : : * they exist in the kernel, because creating them in the kernel
799 : : * happens by passing a netdev object to dpif_port_add().
800 : : * Therefore, ignore the error. */
801 : : }
802 : : }
803 : :
804 : 25330 : return 0;
805 : : }
806 : :
807 : : /* For most types of netdevs we open the device for each call of
808 : : * netdev_open(). However, this is not the case with tap devices,
809 : : * since it is only possible to open the device once. In this
810 : : * situation we share a single file descriptor, and consequently
811 : : * buffers, across all readers. Therefore once data is read it will
812 : : * be unavailable to other reads for tap devices. */
813 : : static int
814 : 66 : netdev_linux_construct_tap(struct netdev *netdev_)
815 : : {
816 : 66 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
817 : : static const char tap_dev[] = "/dev/net/tun";
818 : 66 : const char *name = netdev_->name;
819 : : struct ifreq ifr;
820 : : int error;
821 : :
822 : 66 : netdev_linux_common_construct(netdev);
823 : :
824 : : /* Open tap device. */
825 : 66 : netdev->tap_fd = open(tap_dev, O_RDWR);
826 [ - + ]: 66 : if (netdev->tap_fd < 0) {
827 : 0 : error = errno;
828 [ # # ]: 0 : VLOG_WARN("opening \"%s\" failed: %s", tap_dev, ovs_strerror(error));
829 : 0 : return error;
830 : : }
831 : :
832 : : /* Create tap device. */
833 : 66 : ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
834 : 66 : ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
835 [ - + ]: 66 : if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) {
836 [ # # ]: 0 : VLOG_WARN("%s: creating tap device failed: %s", name,
837 : : ovs_strerror(errno));
838 : 0 : error = errno;
839 : 0 : goto error_close;
840 : : }
841 : :
842 : : /* Make non-blocking. */
843 : 66 : error = set_nonblocking(netdev->tap_fd);
844 [ - + ]: 66 : if (error) {
845 : 0 : goto error_close;
846 : : }
847 : :
848 : 66 : return 0;
849 : :
850 : : error_close:
851 : 0 : close(netdev->tap_fd);
852 : 66 : return error;
853 : : }
854 : :
855 : : static void
856 : 24457 : netdev_linux_destruct(struct netdev *netdev_)
857 : : {
858 : 24457 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
859 : :
860 [ + + ][ - + ]: 24457 : if (netdev->tc && netdev->tc->ops->tc_destroy) {
861 : 0 : netdev->tc->ops->tc_destroy(netdev->tc);
862 : : }
863 : :
864 [ - + ]: 24457 : if (netdev_get_class(netdev_) == &netdev_tap_class
865 [ # # ]: 0 : && netdev->tap_fd >= 0)
866 : : {
867 : 0 : close(netdev->tap_fd);
868 : : }
869 : :
870 [ - + ]: 24457 : if (netdev->miimon_interval > 0) {
871 : 0 : atomic_count_dec(&miimon_cnt);
872 : : }
873 : :
874 : 24457 : ovs_mutex_destroy(&netdev->mutex);
875 : 24457 : }
876 : :
877 : : static void
878 : 24457 : netdev_linux_dealloc(struct netdev *netdev_)
879 : : {
880 : 24457 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
881 : 24457 : free(netdev);
882 : 24457 : }
883 : :
884 : : static struct netdev_rxq *
885 : 148 : netdev_linux_rxq_alloc(void)
886 : : {
887 : 148 : struct netdev_rxq_linux *rx = xzalloc(sizeof *rx);
888 : 148 : return &rx->up;
889 : : }
890 : :
891 : : static int
892 : 148 : netdev_linux_rxq_construct(struct netdev_rxq *rxq_)
893 : : {
894 : 148 : struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
895 : 148 : struct netdev *netdev_ = rx->up.netdev;
896 : 148 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
897 : : int error;
898 : :
899 : 148 : ovs_mutex_lock(&netdev->mutex);
900 : 148 : rx->is_tap = is_tap_netdev(netdev_);
901 [ + + ]: 148 : if (rx->is_tap) {
902 : 66 : rx->fd = netdev->tap_fd;
903 : : } else {
904 : : struct sockaddr_ll sll;
905 : : int ifindex, val;
906 : : /* Result of tcpdump -dd inbound */
907 : : static const struct sock_filter filt[] = {
908 : : { 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */
909 : : { 0x15, 0, 1, 0x00000004 }, /* jeq #4 jt 2 jf 3 */
910 : : { 0x6, 0, 0, 0x00000000 }, /* ret #0 */
911 : : { 0x6, 0, 0, 0x0000ffff } /* ret #65535 */
912 : : };
913 : : static const struct sock_fprog fprog = {
914 : : ARRAY_SIZE(filt), (struct sock_filter *) filt
915 : : };
916 : :
917 : : /* Create file descriptor. */
918 : 82 : rx->fd = socket(PF_PACKET, SOCK_RAW, 0);
919 [ - + ]: 82 : if (rx->fd < 0) {
920 : 0 : error = errno;
921 [ # # ]: 0 : VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error));
922 : 0 : goto error;
923 : : }
924 : :
925 : 82 : val = 1;
926 [ - + ]: 82 : if (setsockopt(rx->fd, SOL_PACKET, PACKET_AUXDATA, &val, sizeof val)) {
927 : 0 : error = errno;
928 [ # # ]: 0 : VLOG_ERR("%s: failed to mark socket for auxdata (%s)",
929 : : netdev_get_name(netdev_), ovs_strerror(error));
930 : 0 : goto error;
931 : : }
932 : :
933 : : /* Set non-blocking mode. */
934 : 82 : error = set_nonblocking(rx->fd);
935 [ - + ]: 82 : if (error) {
936 : 0 : goto error;
937 : : }
938 : :
939 : : /* Get ethernet device index. */
940 : 82 : error = get_ifindex(&netdev->up, &ifindex);
941 [ - + ]: 82 : if (error) {
942 : 0 : goto error;
943 : : }
944 : :
945 : : /* Bind to specific ethernet device. */
946 : 82 : memset(&sll, 0, sizeof sll);
947 : 82 : sll.sll_family = AF_PACKET;
948 : 82 : sll.sll_ifindex = ifindex;
949 : 82 : sll.sll_protocol = htons(ETH_P_ALL);
950 [ - + ]: 82 : if (bind(rx->fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
951 : 0 : error = errno;
952 [ # # ]: 0 : VLOG_ERR("%s: failed to bind raw socket (%s)",
953 : : netdev_get_name(netdev_), ovs_strerror(error));
954 : 0 : goto error;
955 : : }
956 : :
957 : : /* Filter for only inbound packets. */
958 : 82 : error = setsockopt(rx->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog,
959 : : sizeof fprog);
960 [ - + ]: 82 : if (error) {
961 : 0 : error = errno;
962 [ # # ]: 0 : VLOG_ERR("%s: failed to attach filter (%s)",
963 : : netdev_get_name(netdev_), ovs_strerror(error));
964 : 82 : goto error;
965 : : }
966 : : }
967 : 148 : ovs_mutex_unlock(&netdev->mutex);
968 : :
969 : 148 : return 0;
970 : :
971 : : error:
972 [ # # ]: 0 : if (rx->fd >= 0) {
973 : 0 : close(rx->fd);
974 : : }
975 : 0 : ovs_mutex_unlock(&netdev->mutex);
976 : 0 : return error;
977 : : }
978 : :
979 : : static void
980 : 0 : netdev_linux_rxq_destruct(struct netdev_rxq *rxq_)
981 : : {
982 : 0 : struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
983 : :
984 [ # # ]: 0 : if (!rx->is_tap) {
985 : 0 : close(rx->fd);
986 : : }
987 : 0 : }
988 : :
989 : : static void
990 : 0 : netdev_linux_rxq_dealloc(struct netdev_rxq *rxq_)
991 : : {
992 : 0 : struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
993 : :
994 : 0 : free(rx);
995 : 0 : }
996 : :
997 : : static ovs_be16
998 : 108 : auxdata_to_vlan_tpid(const struct tpacket_auxdata *aux)
999 : : {
1000 [ + - ]: 108 : if (aux->tp_status & TP_STATUS_VLAN_TPID_VALID) {
1001 : 108 : return htons(aux->tp_vlan_tpid);
1002 : : } else {
1003 : 0 : return htons(ETH_TYPE_VLAN);
1004 : : }
1005 : : }
1006 : :
1007 : : static bool
1008 : 1338 : auxdata_has_vlan_tci(const struct tpacket_auxdata *aux)
1009 : : {
1010 [ + + ][ - + ]: 1338 : return aux->tp_vlan_tci || aux->tp_status & TP_STATUS_VLAN_VALID;
1011 : : }
1012 : :
1013 : : static int
1014 : 9153 : netdev_linux_rxq_recv_sock(int fd, struct dp_packet *buffer)
1015 : : {
1016 : : size_t size;
1017 : : ssize_t retval;
1018 : : struct iovec iov;
1019 : : struct cmsghdr *cmsg;
1020 : : union {
1021 : : struct cmsghdr cmsg;
1022 : : char buffer[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
1023 : : } cmsg_buffer;
1024 : : struct msghdr msgh;
1025 : :
1026 : : /* Reserve headroom for a single VLAN tag */
1027 : 9153 : dp_packet_reserve(buffer, VLAN_HEADER_LEN);
1028 : 9153 : size = dp_packet_tailroom(buffer);
1029 : :
1030 : 9153 : iov.iov_base = dp_packet_data(buffer);
1031 : 9153 : iov.iov_len = size;
1032 : 9153 : msgh.msg_name = NULL;
1033 : 9153 : msgh.msg_namelen = 0;
1034 : 9153 : msgh.msg_iov = &iov;
1035 : 9153 : msgh.msg_iovlen = 1;
1036 : 9153 : msgh.msg_control = &cmsg_buffer;
1037 : 9153 : msgh.msg_controllen = sizeof cmsg_buffer;
1038 : 9153 : msgh.msg_flags = 0;
1039 : :
1040 : : do {
1041 : 9153 : retval = recvmsg(fd, &msgh, MSG_TRUNC);
1042 [ + + ][ - + ]: 9153 : } while (retval < 0 && errno == EINTR);
1043 : :
1044 [ + + ]: 9153 : if (retval < 0) {
1045 : 7815 : return errno;
1046 [ - + ]: 1338 : } else if (retval > size) {
1047 : 0 : return EMSGSIZE;
1048 : : }
1049 : :
1050 : 1338 : dp_packet_set_size(buffer, dp_packet_size(buffer) + retval);
1051 : :
1052 [ + - ][ + + ]: 2568 : for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
1053 : : const struct tpacket_auxdata *aux;
1054 : :
1055 [ + - ]: 1338 : if (cmsg->cmsg_level != SOL_PACKET
1056 [ + - ]: 1338 : || cmsg->cmsg_type != PACKET_AUXDATA
1057 [ - + ]: 1338 : || cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1058 : 0 : continue;
1059 : : }
1060 : :
1061 : 1338 : aux = ALIGNED_CAST(struct tpacket_auxdata *, CMSG_DATA(cmsg));
1062 [ + + ]: 1338 : if (auxdata_has_vlan_tci(aux)) {
1063 [ - + ]: 108 : if (retval < ETH_HEADER_LEN) {
1064 : 0 : return EINVAL;
1065 : : }
1066 : :
1067 : 108 : eth_push_vlan(buffer, auxdata_to_vlan_tpid(aux),
1068 : 108 : htons(aux->tp_vlan_tci));
1069 : 108 : break;
1070 : : }
1071 : : }
1072 : :
1073 : 9153 : return 0;
1074 : : }
1075 : :
1076 : : static int
1077 : 9760 : netdev_linux_rxq_recv_tap(int fd, struct dp_packet *buffer)
1078 : : {
1079 : : ssize_t retval;
1080 : 9760 : size_t size = dp_packet_tailroom(buffer);
1081 : :
1082 : : do {
1083 : 9760 : retval = read(fd, dp_packet_data(buffer), size);
1084 [ + + ][ - + ]: 9760 : } while (retval < 0 && errno == EINTR);
1085 : :
1086 [ + + ]: 9760 : if (retval < 0) {
1087 : 9684 : return errno;
1088 : : }
1089 : :
1090 : 76 : dp_packet_set_size(buffer, dp_packet_size(buffer) + retval);
1091 : 76 : return 0;
1092 : : }
1093 : :
1094 : : static int
1095 : 18913 : netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
1096 : : {
1097 : 18913 : struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
1098 : 18913 : struct netdev *netdev = rx->up.netdev;
1099 : : struct dp_packet *buffer;
1100 : : ssize_t retval;
1101 : : int mtu;
1102 : :
1103 [ - + ]: 18913 : if (netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu)) {
1104 : 0 : mtu = ETH_PAYLOAD_MAX;
1105 : : }
1106 : :
1107 : 18913 : buffer = dp_packet_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu,
1108 : : DP_NETDEV_HEADROOM);
1109 [ + + ]: 37826 : retval = (rx->is_tap
1110 : 9760 : ? netdev_linux_rxq_recv_tap(rx->fd, buffer)
1111 : 9153 : : netdev_linux_rxq_recv_sock(rx->fd, buffer));
1112 : :
1113 [ + + ]: 18913 : if (retval) {
1114 [ - + ][ # # ]: 17499 : if (retval != EAGAIN && retval != EMSGSIZE) {
1115 [ # # ]: 0 : VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
1116 : : netdev_rxq_get_name(rxq_), ovs_strerror(errno));
1117 : : }
1118 : 17499 : dp_packet_delete(buffer);
1119 : : } else {
1120 : 1414 : batch->packets[0] = buffer;
1121 : 1414 : batch->count = 1;
1122 : : }
1123 : :
1124 : 18913 : return retval;
1125 : : }
1126 : :
1127 : : static void
1128 : 16867 : netdev_linux_rxq_wait(struct netdev_rxq *rxq_)
1129 : : {
1130 : 16867 : struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
1131 : 16867 : poll_fd_wait(rx->fd, POLLIN);
1132 : 16867 : }
1133 : :
1134 : : static int
1135 : 0 : netdev_linux_rxq_drain(struct netdev_rxq *rxq_)
1136 : : {
1137 : 0 : struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
1138 [ # # ]: 0 : if (rx->is_tap) {
1139 : : struct ifreq ifr;
1140 : 0 : int error = af_inet_ifreq_ioctl(netdev_rxq_get_name(rxq_), &ifr,
1141 : : SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
1142 [ # # ]: 0 : if (error) {
1143 : 0 : return error;
1144 : : }
1145 : 0 : drain_fd(rx->fd, ifr.ifr_qlen);
1146 : 0 : return 0;
1147 : : } else {
1148 : 0 : return drain_rcvbuf(rx->fd);
1149 : : }
1150 : : }
1151 : :
1152 : : /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive
1153 : : * errno value. Returns EAGAIN without blocking if the packet cannot be queued
1154 : : * immediately. Returns EMSGSIZE if a partial packet was transmitted or if
1155 : : * the packet is too big or too small to transmit on the device.
1156 : : *
1157 : : * The caller retains ownership of 'buffer' in all cases.
1158 : : *
1159 : : * The kernel maintains a packet transmission queue, so the caller is not
1160 : : * expected to do additional queuing of packets. */
1161 : : static int
1162 : 1182 : netdev_linux_send(struct netdev *netdev_, int qid OVS_UNUSED,
1163 : : struct dp_packet_batch *batch, bool may_steal,
1164 : : bool concurrent_txq OVS_UNUSED)
1165 : : {
1166 : : int i;
1167 : 1182 : int error = 0;
1168 : :
1169 : : /* 'i' is incremented only if there's no error */
1170 [ + + ]: 2364 : for (i = 0; i < batch->count;) {
1171 : 1182 : const void *data = dp_packet_data(batch->packets[i]);
1172 : 1182 : size_t size = dp_packet_size(batch->packets[i]);
1173 : : ssize_t retval;
1174 : :
1175 : : /* Truncate the packet if it is configured. */
1176 : 1182 : size -= dp_packet_get_cutlen(batch->packets[i]);
1177 : :
1178 [ + + ]: 1182 : if (!is_tap_netdev(netdev_)) {
1179 : : /* Use our AF_PACKET socket to send to this device. */
1180 : : struct sockaddr_ll sll;
1181 : : struct msghdr msg;
1182 : : struct iovec iov;
1183 : : int ifindex;
1184 : : int sock;
1185 : :
1186 : 930 : sock = af_packet_sock();
1187 [ - + ]: 930 : if (sock < 0) {
1188 : 0 : return -sock;
1189 : : }
1190 : :
1191 : 930 : ifindex = netdev_get_ifindex(netdev_);
1192 [ - + ]: 930 : if (ifindex < 0) {
1193 : 0 : return -ifindex;
1194 : : }
1195 : :
1196 : : /* We don't bother setting most fields in sockaddr_ll because the
1197 : : * kernel ignores them for SOCK_RAW. */
1198 : 930 : memset(&sll, 0, sizeof sll);
1199 : 930 : sll.sll_family = AF_PACKET;
1200 : 930 : sll.sll_ifindex = ifindex;
1201 : :
1202 : 930 : iov.iov_base = CONST_CAST(void *, data);
1203 : 930 : iov.iov_len = size;
1204 : :
1205 : 930 : msg.msg_name = &sll;
1206 : 930 : msg.msg_namelen = sizeof sll;
1207 : 930 : msg.msg_iov = &iov;
1208 : 930 : msg.msg_iovlen = 1;
1209 : 930 : msg.msg_control = NULL;
1210 : 930 : msg.msg_controllen = 0;
1211 : 930 : msg.msg_flags = 0;
1212 : :
1213 : 930 : retval = sendmsg(sock, &msg, 0);
1214 : : } else {
1215 : : /* Use the tap fd to send to this device. This is essential for
1216 : : * tap devices, because packets sent to a tap device with an
1217 : : * AF_PACKET socket will loop back to be *received* again on the
1218 : : * tap device. This doesn't occur on other interface types
1219 : : * because we attach a socket filter to the rx socket. */
1220 : 252 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1221 : :
1222 : 252 : retval = write(netdev->tap_fd, data, size);
1223 : : }
1224 : :
1225 [ - + ]: 1182 : if (retval < 0) {
1226 [ # # ]: 0 : if (errno == EINTR) {
1227 : : /* The send was interrupted by a signal. Retry the packet by
1228 : : * continuing without incrementing 'i'.*/
1229 : 0 : continue;
1230 [ # # ][ # # ]: 0 : } else if (errno == EIO && is_tap_netdev(netdev_)) {
1231 : : /* The Linux tap driver returns EIO if the device is not up.
1232 : : * From the OVS side this is not an error, so ignore it. */
1233 : : } else {
1234 : : /* The Linux AF_PACKET implementation never blocks waiting for
1235 : : * room for packets, instead returning ENOBUFS. Translate this
1236 : : * into EAGAIN for the caller. */
1237 [ # # ]: 0 : error = errno == ENOBUFS ? EAGAIN : errno;
1238 : 0 : break;
1239 : : }
1240 [ - + ]: 1182 : } else if (retval != size) {
1241 [ # # ]: 0 : VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIuSIZE" bytes"
1242 : : " of %"PRIuSIZE") on %s", retval, size,
1243 : : netdev_get_name(netdev_));
1244 : 0 : error = EMSGSIZE;
1245 : 0 : break;
1246 : : }
1247 : :
1248 : : /* Process the next packet in the batch */
1249 : 1182 : i++;
1250 : : }
1251 : :
1252 : 1182 : dp_packet_delete_batch(batch, may_steal);
1253 : :
1254 [ - + ][ # # ]: 1182 : if (error && error != EAGAIN) {
1255 [ # # ]: 0 : VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
1256 : : netdev_get_name(netdev_), ovs_strerror(error));
1257 : : }
1258 : :
1259 : 1182 : return error;
1260 : :
1261 : : }
1262 : :
1263 : : /* Registers with the poll loop to wake up from the next call to poll_block()
1264 : : * when the packet transmission queue has sufficient room to transmit a packet
1265 : : * with netdev_send().
1266 : : *
1267 : : * The kernel maintains a packet transmission queue, so the client is not
1268 : : * expected to do additional queuing of packets. Thus, this function is
1269 : : * unlikely to ever be used. It is included for completeness. */
1270 : : static void
1271 : 0 : netdev_linux_send_wait(struct netdev *netdev, int qid OVS_UNUSED)
1272 : : {
1273 [ # # ]: 0 : if (is_tap_netdev(netdev)) {
1274 : : /* TAP device always accepts packets.*/
1275 : 0 : poll_immediate_wake();
1276 : : }
1277 : 0 : }
1278 : :
1279 : : /* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful,
1280 : : * otherwise a positive errno value. */
1281 : : static int
1282 : 2072 : netdev_linux_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac)
1283 : : {
1284 : 2072 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1285 : 2072 : enum netdev_flags old_flags = 0;
1286 : : int error;
1287 : :
1288 : 2072 : ovs_mutex_lock(&netdev->mutex);
1289 : :
1290 [ + - ]: 2072 : if (netdev->cache_valid & VALID_ETHERADDR) {
1291 : 2072 : error = netdev->ether_addr_error;
1292 [ + - ][ + + ]: 2072 : if (error || eth_addr_equals(netdev->etheraddr, mac)) {
1293 : : goto exit;
1294 : : }
1295 : 109 : netdev->cache_valid &= ~VALID_ETHERADDR;
1296 : : }
1297 : :
1298 : : /* Tap devices must be brought down before setting the address. */
1299 [ + + ]: 109 : if (is_tap_netdev(netdev_)) {
1300 : 35 : update_flags(netdev, NETDEV_UP, 0, &old_flags);
1301 : : }
1302 : 109 : error = set_etheraddr(netdev_get_name(netdev_), mac);
1303 [ - + ][ # # ]: 109 : if (!error || error == ENODEV) {
1304 : 109 : netdev->ether_addr_error = error;
1305 : 109 : netdev->cache_valid |= VALID_ETHERADDR;
1306 [ + - ]: 109 : if (!error) {
1307 : 109 : netdev->etheraddr = mac;
1308 : : }
1309 : : }
1310 : :
1311 [ + + ][ - + ]: 109 : if (is_tap_netdev(netdev_) && old_flags & NETDEV_UP) {
1312 : 0 : update_flags(netdev, 0, NETDEV_UP, &old_flags);
1313 : : }
1314 : :
1315 : : exit:
1316 : 2072 : ovs_mutex_unlock(&netdev->mutex);
1317 : 2072 : return error;
1318 : : }
1319 : :
1320 : : /* Copies 'netdev''s MAC address to 'mac' which is passed as param. */
1321 : : static int
1322 : 12232 : netdev_linux_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac)
1323 : : {
1324 : 12232 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1325 : : int error;
1326 : :
1327 : 12232 : ovs_mutex_lock(&netdev->mutex);
1328 [ + + ]: 12232 : if (!(netdev->cache_valid & VALID_ETHERADDR)) {
1329 : 2817 : netdev->ether_addr_error = get_etheraddr(netdev_get_name(netdev_),
1330 : : &netdev->etheraddr);
1331 : 2817 : netdev->cache_valid |= VALID_ETHERADDR;
1332 : : }
1333 : :
1334 : 12232 : error = netdev->ether_addr_error;
1335 [ + + ]: 12232 : if (!error) {
1336 : 12222 : *mac = netdev->etheraddr;
1337 : : }
1338 : 12232 : ovs_mutex_unlock(&netdev->mutex);
1339 : :
1340 : 12232 : return error;
1341 : : }
1342 : :
1343 : : static int
1344 : 26133 : netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup)
1345 : : {
1346 : : int error;
1347 : :
1348 [ + + ]: 26133 : if (!(netdev->cache_valid & VALID_MTU)) {
1349 : : struct ifreq ifr;
1350 : :
1351 : 377 : netdev->netdev_mtu_error = af_inet_ifreq_ioctl(
1352 : 377 : netdev_get_name(&netdev->up), &ifr, SIOCGIFMTU, "SIOCGIFMTU");
1353 : 377 : netdev->mtu = ifr.ifr_mtu;
1354 : 377 : netdev->cache_valid |= VALID_MTU;
1355 : : }
1356 : :
1357 : 26133 : error = netdev->netdev_mtu_error;
1358 [ + + ]: 26133 : if (!error) {
1359 : 26128 : *mtup = netdev->mtu;
1360 : : }
1361 : :
1362 : 26133 : return error;
1363 : : }
1364 : :
1365 : : /* Returns the maximum size of transmitted (and received) packets on 'netdev',
1366 : : * in bytes, not including the hardware header; thus, this is typically 1500
1367 : : * bytes for Ethernet devices. */
1368 : : static int
1369 : 7220 : netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup)
1370 : : {
1371 : 7220 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1372 : : int error;
1373 : :
1374 : 7220 : ovs_mutex_lock(&netdev->mutex);
1375 : 7220 : error = netdev_linux_get_mtu__(netdev, mtup);
1376 : 7220 : ovs_mutex_unlock(&netdev->mutex);
1377 : :
1378 : 7220 : return error;
1379 : : }
1380 : :
1381 : : /* Sets the maximum size of transmitted (MTU) for given device using linux
1382 : : * networking ioctl interface.
1383 : : */
1384 : : static int
1385 : 97 : netdev_linux_set_mtu(struct netdev *netdev_, int mtu)
1386 : : {
1387 : 97 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1388 : : struct ifreq ifr;
1389 : : int error;
1390 : :
1391 : 97 : ovs_mutex_lock(&netdev->mutex);
1392 [ + - ]: 97 : if (netdev->cache_valid & VALID_MTU) {
1393 : 97 : error = netdev->netdev_mtu_error;
1394 [ + - ][ + + ]: 97 : if (error || netdev->mtu == mtu) {
1395 : : goto exit;
1396 : : }
1397 : 2 : netdev->cache_valid &= ~VALID_MTU;
1398 : : }
1399 : 2 : ifr.ifr_mtu = mtu;
1400 : 2 : error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr,
1401 : : SIOCSIFMTU, "SIOCSIFMTU");
1402 [ - + ][ # # ]: 2 : if (!error || error == ENODEV) {
1403 : 2 : netdev->netdev_mtu_error = error;
1404 : 2 : netdev->mtu = ifr.ifr_mtu;
1405 : 2 : netdev->cache_valid |= VALID_MTU;
1406 : : }
1407 : : exit:
1408 : 97 : ovs_mutex_unlock(&netdev->mutex);
1409 : 97 : return error;
1410 : : }
1411 : :
1412 : : /* Returns the ifindex of 'netdev', if successful, as a positive number.
1413 : : * On failure, returns a negative errno value. */
1414 : : static int
1415 : 5166 : netdev_linux_get_ifindex(const struct netdev *netdev_)
1416 : : {
1417 : 5166 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1418 : : int ifindex, error;
1419 : :
1420 : 5166 : ovs_mutex_lock(&netdev->mutex);
1421 : 5166 : error = get_ifindex(netdev_, &ifindex);
1422 : 5166 : ovs_mutex_unlock(&netdev->mutex);
1423 : :
1424 [ + + ]: 5166 : return error ? -error : ifindex;
1425 : : }
1426 : :
1427 : : static int
1428 : 7942 : netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier)
1429 : : {
1430 : 7942 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1431 : :
1432 : 7942 : ovs_mutex_lock(&netdev->mutex);
1433 [ - + ]: 7942 : if (netdev->miimon_interval > 0) {
1434 : 0 : *carrier = netdev->miimon;
1435 : : } else {
1436 : 7942 : *carrier = (netdev->ifi_flags & IFF_RUNNING) != 0;
1437 : : }
1438 : 7942 : ovs_mutex_unlock(&netdev->mutex);
1439 : :
1440 : 7942 : return 0;
1441 : : }
1442 : :
1443 : : static long long int
1444 : 11021 : netdev_linux_get_carrier_resets(const struct netdev *netdev_)
1445 : : {
1446 : 11021 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1447 : : long long int carrier_resets;
1448 : :
1449 : 11021 : ovs_mutex_lock(&netdev->mutex);
1450 : 11021 : carrier_resets = netdev->carrier_resets;
1451 : 11021 : ovs_mutex_unlock(&netdev->mutex);
1452 : :
1453 : 11021 : return carrier_resets;
1454 : : }
1455 : :
1456 : : static int
1457 : 0 : netdev_linux_do_miimon(const char *name, int cmd, const char *cmd_name,
1458 : : struct mii_ioctl_data *data)
1459 : : {
1460 : : struct ifreq ifr;
1461 : : int error;
1462 : :
1463 : 0 : memset(&ifr, 0, sizeof ifr);
1464 : 0 : memcpy(&ifr.ifr_data, data, sizeof *data);
1465 : 0 : error = af_inet_ifreq_ioctl(name, &ifr, cmd, cmd_name);
1466 : 0 : memcpy(data, &ifr.ifr_data, sizeof *data);
1467 : :
1468 : 0 : return error;
1469 : : }
1470 : :
1471 : : static int
1472 : 0 : netdev_linux_get_miimon(const char *name, bool *miimon)
1473 : : {
1474 : : struct mii_ioctl_data data;
1475 : : int error;
1476 : :
1477 : 0 : *miimon = false;
1478 : :
1479 : 0 : memset(&data, 0, sizeof data);
1480 : 0 : error = netdev_linux_do_miimon(name, SIOCGMIIPHY, "SIOCGMIIPHY", &data);
1481 [ # # ]: 0 : if (!error) {
1482 : : /* data.phy_id is filled out by previous SIOCGMIIPHY miimon call. */
1483 : 0 : data.reg_num = MII_BMSR;
1484 : 0 : error = netdev_linux_do_miimon(name, SIOCGMIIREG, "SIOCGMIIREG",
1485 : : &data);
1486 : :
1487 [ # # ]: 0 : if (!error) {
1488 : 0 : *miimon = !!(data.val_out & BMSR_LSTATUS);
1489 : : } else {
1490 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: failed to query MII", name);
1491 : : }
1492 : : } else {
1493 : : struct ethtool_cmd ecmd;
1494 : :
1495 [ # # ]: 0 : VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool",
1496 : : name);
1497 : :
1498 : 0 : COVERAGE_INC(netdev_get_ethtool);
1499 : 0 : memset(&ecmd, 0, sizeof ecmd);
1500 : 0 : error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK,
1501 : : "ETHTOOL_GLINK");
1502 [ # # ]: 0 : if (!error) {
1503 : : struct ethtool_value eval;
1504 : :
1505 : 0 : memcpy(&eval, &ecmd, sizeof eval);
1506 : 0 : *miimon = !!eval.data;
1507 : : } else {
1508 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: ethtool link status failed", name);
1509 : : }
1510 : : }
1511 : :
1512 : 0 : return error;
1513 : : }
1514 : :
1515 : : static int
1516 : 4175 : netdev_linux_set_miimon_interval(struct netdev *netdev_,
1517 : : long long int interval)
1518 : : {
1519 : 4175 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1520 : :
1521 : 4175 : ovs_mutex_lock(&netdev->mutex);
1522 [ - + ]: 4175 : interval = interval > 0 ? MAX(interval, 100) : 0;
1523 [ - + ]: 4175 : if (netdev->miimon_interval != interval) {
1524 [ # # ][ # # ]: 0 : if (interval && !netdev->miimon_interval) {
1525 : 0 : atomic_count_inc(&miimon_cnt);
1526 [ # # ][ # # ]: 0 : } else if (!interval && netdev->miimon_interval) {
1527 : 0 : atomic_count_dec(&miimon_cnt);
1528 : : }
1529 : :
1530 : 0 : netdev->miimon_interval = interval;
1531 : 0 : timer_set_expired(&netdev->miimon_timer);
1532 : : }
1533 : 4175 : ovs_mutex_unlock(&netdev->mutex);
1534 : :
1535 : 4175 : return 0;
1536 : : }
1537 : :
1538 : : static void
1539 : 0 : netdev_linux_miimon_run(void)
1540 : : {
1541 : : struct shash device_shash;
1542 : : struct shash_node *node;
1543 : :
1544 : 0 : shash_init(&device_shash);
1545 : 0 : netdev_get_devices(&netdev_linux_class, &device_shash);
1546 [ # # ][ # # ]: 0 : SHASH_FOR_EACH (node, &device_shash) {
1547 : 0 : struct netdev *netdev = node->data;
1548 : 0 : struct netdev_linux *dev = netdev_linux_cast(netdev);
1549 : : bool miimon;
1550 : :
1551 : 0 : ovs_mutex_lock(&dev->mutex);
1552 [ # # ][ # # ]: 0 : if (dev->miimon_interval > 0 && timer_expired(&dev->miimon_timer)) {
1553 : 0 : netdev_linux_get_miimon(dev->up.name, &miimon);
1554 [ # # ]: 0 : if (miimon != dev->miimon) {
1555 : 0 : dev->miimon = miimon;
1556 : 0 : netdev_linux_changed(dev, dev->ifi_flags, 0);
1557 : : }
1558 : :
1559 : 0 : timer_set_duration(&dev->miimon_timer, dev->miimon_interval);
1560 : : }
1561 : 0 : ovs_mutex_unlock(&dev->mutex);
1562 : 0 : netdev_close(netdev);
1563 : : }
1564 : :
1565 : 0 : shash_destroy(&device_shash);
1566 : 0 : }
1567 : :
1568 : : static void
1569 : 0 : netdev_linux_miimon_wait(void)
1570 : : {
1571 : : struct shash device_shash;
1572 : : struct shash_node *node;
1573 : :
1574 : 0 : shash_init(&device_shash);
1575 : 0 : netdev_get_devices(&netdev_linux_class, &device_shash);
1576 [ # # ][ # # ]: 0 : SHASH_FOR_EACH (node, &device_shash) {
1577 : 0 : struct netdev *netdev = node->data;
1578 : 0 : struct netdev_linux *dev = netdev_linux_cast(netdev);
1579 : :
1580 : 0 : ovs_mutex_lock(&dev->mutex);
1581 [ # # ]: 0 : if (dev->miimon_interval > 0) {
1582 : 0 : timer_wait(&dev->miimon_timer);
1583 : : }
1584 : 0 : ovs_mutex_unlock(&dev->mutex);
1585 : 0 : netdev_close(netdev);
1586 : : }
1587 : 0 : shash_destroy(&device_shash);
1588 : 0 : }
1589 : :
1590 : : static void
1591 : 220 : swap_uint64(uint64_t *a, uint64_t *b)
1592 : : {
1593 : 220 : uint64_t tmp = *a;
1594 : 220 : *a = *b;
1595 : 220 : *b = tmp;
1596 : 220 : }
1597 : :
1598 : : /* Copies 'src' into 'dst', performing format conversion in the process.
1599 : : *
1600 : : * 'src' is allowed to be misaligned. */
1601 : : static void
1602 : 335 : netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
1603 : : const struct ovs_vport_stats *src)
1604 : : {
1605 : 335 : dst->rx_packets = get_32aligned_u64(&src->rx_packets);
1606 : 335 : dst->tx_packets = get_32aligned_u64(&src->tx_packets);
1607 : 335 : dst->rx_bytes = get_32aligned_u64(&src->rx_bytes);
1608 : 335 : dst->tx_bytes = get_32aligned_u64(&src->tx_bytes);
1609 : 335 : dst->rx_errors = get_32aligned_u64(&src->rx_errors);
1610 : 335 : dst->tx_errors = get_32aligned_u64(&src->tx_errors);
1611 : 335 : dst->rx_dropped = get_32aligned_u64(&src->rx_dropped);
1612 : 335 : dst->tx_dropped = get_32aligned_u64(&src->tx_dropped);
1613 : 335 : dst->multicast = 0;
1614 : 335 : dst->collisions = 0;
1615 : 335 : dst->rx_length_errors = 0;
1616 : 335 : dst->rx_over_errors = 0;
1617 : 335 : dst->rx_crc_errors = 0;
1618 : 335 : dst->rx_frame_errors = 0;
1619 : 335 : dst->rx_fifo_errors = 0;
1620 : 335 : dst->rx_missed_errors = 0;
1621 : 335 : dst->tx_aborted_errors = 0;
1622 : 335 : dst->tx_carrier_errors = 0;
1623 : 335 : dst->tx_fifo_errors = 0;
1624 : 335 : dst->tx_heartbeat_errors = 0;
1625 : 335 : dst->tx_window_errors = 0;
1626 : 335 : }
1627 : :
1628 : : static int
1629 : 501 : get_stats_via_vport__(const struct netdev *netdev, struct netdev_stats *stats)
1630 : : {
1631 : : struct dpif_netlink_vport reply;
1632 : : struct ofpbuf *buf;
1633 : : int error;
1634 : :
1635 : 501 : error = dpif_netlink_vport_get(netdev_get_name(netdev), &reply, &buf);
1636 [ + + ]: 501 : if (error) {
1637 : 166 : return error;
1638 [ - + ]: 335 : } else if (!reply.stats) {
1639 : 0 : ofpbuf_delete(buf);
1640 : 0 : return EOPNOTSUPP;
1641 : : }
1642 : :
1643 : 335 : netdev_stats_from_ovs_vport_stats(stats, reply.stats);
1644 : :
1645 : 335 : ofpbuf_delete(buf);
1646 : :
1647 : 501 : return 0;
1648 : : }
1649 : :
1650 : : static void
1651 : 531 : get_stats_via_vport(const struct netdev *netdev_,
1652 : : struct netdev_stats *stats)
1653 : : {
1654 : 531 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1655 : :
1656 [ + + ][ + + ]: 531 : if (!netdev->vport_stats_error ||
1657 : 79 : !(netdev->cache_valid & VALID_VPORT_STAT_ERROR)) {
1658 : : int error;
1659 : :
1660 : 501 : error = get_stats_via_vport__(netdev_, stats);
1661 [ + + ][ - + ]: 501 : if (error && error != ENOENT && error != ENODEV) {
[ # # ]
1662 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed "
1663 : : "(%s)",
1664 : : netdev_get_name(netdev_), ovs_strerror(error));
1665 : : }
1666 : 501 : netdev->vport_stats_error = error;
1667 : 501 : netdev->cache_valid |= VALID_VPORT_STAT_ERROR;
1668 : : }
1669 : 531 : }
1670 : :
1671 : : /* Retrieves current device stats for 'netdev-linux'. */
1672 : : static int
1673 : 370 : netdev_linux_get_stats(const struct netdev *netdev_,
1674 : : struct netdev_stats *stats)
1675 : : {
1676 : 370 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1677 : : struct netdev_stats dev_stats;
1678 : : int error;
1679 : :
1680 : 370 : ovs_mutex_lock(&netdev->mutex);
1681 : 370 : get_stats_via_vport(netdev_, stats);
1682 : 370 : error = get_stats_via_netlink(netdev_, &dev_stats);
1683 [ - + ]: 370 : if (error) {
1684 [ # # ]: 0 : if (!netdev->vport_stats_error) {
1685 : 0 : error = 0;
1686 : : }
1687 [ + + ]: 370 : } else if (netdev->vport_stats_error) {
1688 : : /* stats not available from OVS then use netdev stats. */
1689 : 141 : *stats = dev_stats;
1690 : : } else {
1691 : : /* Use kernel netdev's packet and byte counts since vport's counters
1692 : : * do not reflect packet counts on the wire when GSO, TSO or GRO are
1693 : : * enabled. */
1694 : 229 : stats->rx_packets = dev_stats.rx_packets;
1695 : 229 : stats->rx_bytes = dev_stats.rx_bytes;
1696 : 229 : stats->tx_packets = dev_stats.tx_packets;
1697 : 229 : stats->tx_bytes = dev_stats.tx_bytes;
1698 : :
1699 : 229 : stats->rx_errors += dev_stats.rx_errors;
1700 : 229 : stats->tx_errors += dev_stats.tx_errors;
1701 : 229 : stats->rx_dropped += dev_stats.rx_dropped;
1702 : 229 : stats->tx_dropped += dev_stats.tx_dropped;
1703 : 229 : stats->multicast += dev_stats.multicast;
1704 : 229 : stats->collisions += dev_stats.collisions;
1705 : 229 : stats->rx_length_errors += dev_stats.rx_length_errors;
1706 : 229 : stats->rx_over_errors += dev_stats.rx_over_errors;
1707 : 229 : stats->rx_crc_errors += dev_stats.rx_crc_errors;
1708 : 229 : stats->rx_frame_errors += dev_stats.rx_frame_errors;
1709 : 229 : stats->rx_fifo_errors += dev_stats.rx_fifo_errors;
1710 : 229 : stats->rx_missed_errors += dev_stats.rx_missed_errors;
1711 : 229 : stats->tx_aborted_errors += dev_stats.tx_aborted_errors;
1712 : 229 : stats->tx_carrier_errors += dev_stats.tx_carrier_errors;
1713 : 229 : stats->tx_fifo_errors += dev_stats.tx_fifo_errors;
1714 : 229 : stats->tx_heartbeat_errors += dev_stats.tx_heartbeat_errors;
1715 : 229 : stats->tx_window_errors += dev_stats.tx_window_errors;
1716 : : }
1717 : 370 : ovs_mutex_unlock(&netdev->mutex);
1718 : :
1719 : 370 : return error;
1720 : : }
1721 : :
1722 : : /* Retrieves current device stats for 'netdev-tap' netdev or
1723 : : * netdev-internal. */
1724 : : static int
1725 : 55 : netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
1726 : : {
1727 : 55 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1728 : : struct netdev_stats dev_stats;
1729 : : int error;
1730 : :
1731 : 55 : ovs_mutex_lock(&netdev->mutex);
1732 : 55 : get_stats_via_vport(netdev_, stats);
1733 : 55 : error = get_stats_via_netlink(netdev_, &dev_stats);
1734 [ - + ]: 55 : if (error) {
1735 [ # # ]: 0 : if (!netdev->vport_stats_error) {
1736 : 0 : error = 0;
1737 : : }
1738 [ + - ]: 55 : } else if (netdev->vport_stats_error) {
1739 : : /* Transmit and receive stats will appear to be swapped relative to the
1740 : : * other ports since we are the one sending the data, not a remote
1741 : : * computer. For consistency, we swap them back here. This does not
1742 : : * apply if we are getting stats from the vport layer because it always
1743 : : * tracks stats from the perspective of the switch. */
1744 : :
1745 : 55 : *stats = dev_stats;
1746 : 55 : swap_uint64(&stats->rx_packets, &stats->tx_packets);
1747 : 55 : swap_uint64(&stats->rx_bytes, &stats->tx_bytes);
1748 : 55 : swap_uint64(&stats->rx_errors, &stats->tx_errors);
1749 : 55 : swap_uint64(&stats->rx_dropped, &stats->tx_dropped);
1750 : 55 : stats->rx_length_errors = 0;
1751 : 55 : stats->rx_over_errors = 0;
1752 : 55 : stats->rx_crc_errors = 0;
1753 : 55 : stats->rx_frame_errors = 0;
1754 : 55 : stats->rx_fifo_errors = 0;
1755 : 55 : stats->rx_missed_errors = 0;
1756 : 55 : stats->tx_aborted_errors = 0;
1757 : 55 : stats->tx_carrier_errors = 0;
1758 : 55 : stats->tx_fifo_errors = 0;
1759 : 55 : stats->tx_heartbeat_errors = 0;
1760 : 55 : stats->tx_window_errors = 0;
1761 : : } else {
1762 : : /* Use kernel netdev's packet and byte counts since vport counters
1763 : : * do not reflect packet counts on the wire when GSO, TSO or GRO
1764 : : * are enabled. */
1765 : 0 : stats->rx_packets = dev_stats.tx_packets;
1766 : 0 : stats->rx_bytes = dev_stats.tx_bytes;
1767 : 0 : stats->tx_packets = dev_stats.rx_packets;
1768 : 0 : stats->tx_bytes = dev_stats.rx_bytes;
1769 : :
1770 : 0 : stats->rx_dropped += dev_stats.tx_dropped;
1771 : 0 : stats->tx_dropped += dev_stats.rx_dropped;
1772 : :
1773 : 0 : stats->rx_errors += dev_stats.tx_errors;
1774 : 0 : stats->tx_errors += dev_stats.rx_errors;
1775 : :
1776 : 0 : stats->multicast += dev_stats.multicast;
1777 : 0 : stats->collisions += dev_stats.collisions;
1778 : : }
1779 : 55 : ovs_mutex_unlock(&netdev->mutex);
1780 : :
1781 : 55 : return error;
1782 : : }
1783 : :
1784 : : static int
1785 : 106 : netdev_internal_get_stats(const struct netdev *netdev_,
1786 : : struct netdev_stats *stats)
1787 : : {
1788 : 106 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1789 : : int error;
1790 : :
1791 : 106 : ovs_mutex_lock(&netdev->mutex);
1792 : 106 : get_stats_via_vport(netdev_, stats);
1793 : 106 : error = netdev->vport_stats_error;
1794 : 106 : ovs_mutex_unlock(&netdev->mutex);
1795 : :
1796 : 106 : return error;
1797 : : }
1798 : :
1799 : : static void
1800 : 3955 : netdev_linux_read_features(struct netdev_linux *netdev)
1801 : : {
1802 : : struct ethtool_cmd ecmd;
1803 : : uint32_t speed;
1804 : : int error;
1805 : :
1806 [ + + ]: 3955 : if (netdev->cache_valid & VALID_FEATURES) {
1807 : 3179 : return;
1808 : : }
1809 : :
1810 : 776 : COVERAGE_INC(netdev_get_ethtool);
1811 : 776 : memset(&ecmd, 0, sizeof ecmd);
1812 : 776 : error = netdev_linux_do_ethtool(netdev->up.name, &ecmd,
1813 : : ETHTOOL_GSET, "ETHTOOL_GSET");
1814 [ - + ]: 776 : if (error) {
1815 : 0 : goto out;
1816 : : }
1817 : :
1818 : : /* Supported features. */
1819 : 776 : netdev->supported = 0;
1820 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_10baseT_Half) {
1821 : 0 : netdev->supported |= NETDEV_F_10MB_HD;
1822 : : }
1823 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_10baseT_Full) {
1824 : 0 : netdev->supported |= NETDEV_F_10MB_FD;
1825 : : }
1826 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_100baseT_Half) {
1827 : 0 : netdev->supported |= NETDEV_F_100MB_HD;
1828 : : }
1829 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_100baseT_Full) {
1830 : 0 : netdev->supported |= NETDEV_F_100MB_FD;
1831 : : }
1832 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_1000baseT_Half) {
1833 : 0 : netdev->supported |= NETDEV_F_1GB_HD;
1834 : : }
1835 [ + - ][ - + ]: 776 : if ((ecmd.supported & SUPPORTED_1000baseT_Full) ||
1836 : 776 : (ecmd.supported & SUPPORTED_1000baseKX_Full)) {
1837 : 0 : netdev->supported |= NETDEV_F_1GB_FD;
1838 : : }
1839 [ + - ][ + - ]: 776 : if ((ecmd.supported & SUPPORTED_10000baseT_Full) ||
1840 [ + - ]: 776 : (ecmd.supported & SUPPORTED_10000baseKX4_Full) ||
1841 [ - + ]: 776 : (ecmd.supported & SUPPORTED_10000baseKR_Full) ||
1842 : 776 : (ecmd.supported & SUPPORTED_10000baseR_FEC)) {
1843 : 0 : netdev->supported |= NETDEV_F_10GB_FD;
1844 : : }
1845 [ + - ][ + - ]: 776 : if ((ecmd.supported & SUPPORTED_40000baseKR4_Full) ||
1846 [ + - ]: 776 : (ecmd.supported & SUPPORTED_40000baseCR4_Full) ||
1847 [ - + ]: 776 : (ecmd.supported & SUPPORTED_40000baseSR4_Full) ||
1848 : 776 : (ecmd.supported & SUPPORTED_40000baseLR4_Full)) {
1849 : 0 : netdev->supported |= NETDEV_F_40GB_FD;
1850 : : }
1851 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_TP) {
1852 : 0 : netdev->supported |= NETDEV_F_COPPER;
1853 : : }
1854 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_FIBRE) {
1855 : 0 : netdev->supported |= NETDEV_F_FIBER;
1856 : : }
1857 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_Autoneg) {
1858 : 0 : netdev->supported |= NETDEV_F_AUTONEG;
1859 : : }
1860 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_Pause) {
1861 : 0 : netdev->supported |= NETDEV_F_PAUSE;
1862 : : }
1863 [ - + ]: 776 : if (ecmd.supported & SUPPORTED_Asym_Pause) {
1864 : 0 : netdev->supported |= NETDEV_F_PAUSE_ASYM;
1865 : : }
1866 : :
1867 : : /* Advertised features. */
1868 : 776 : netdev->advertised = 0;
1869 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_10baseT_Half) {
1870 : 0 : netdev->advertised |= NETDEV_F_10MB_HD;
1871 : : }
1872 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_10baseT_Full) {
1873 : 0 : netdev->advertised |= NETDEV_F_10MB_FD;
1874 : : }
1875 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_100baseT_Half) {
1876 : 0 : netdev->advertised |= NETDEV_F_100MB_HD;
1877 : : }
1878 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_100baseT_Full) {
1879 : 0 : netdev->advertised |= NETDEV_F_100MB_FD;
1880 : : }
1881 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_1000baseT_Half) {
1882 : 0 : netdev->advertised |= NETDEV_F_1GB_HD;
1883 : : }
1884 [ + - ][ - + ]: 776 : if ((ecmd.advertising & ADVERTISED_1000baseT_Full) ||
1885 : 776 : (ecmd.advertising & ADVERTISED_1000baseKX_Full)) {
1886 : 0 : netdev->advertised |= NETDEV_F_1GB_FD;
1887 : : }
1888 [ + - ][ + - ]: 776 : if ((ecmd.advertising & ADVERTISED_10000baseT_Full) ||
1889 [ + - ]: 776 : (ecmd.advertising & ADVERTISED_10000baseKX4_Full) ||
1890 [ - + ]: 776 : (ecmd.advertising & ADVERTISED_10000baseKR_Full) ||
1891 : 776 : (ecmd.advertising & ADVERTISED_10000baseR_FEC)) {
1892 : 0 : netdev->advertised |= NETDEV_F_10GB_FD;
1893 : : }
1894 [ + - ][ + - ]: 776 : if ((ecmd.advertising & ADVERTISED_40000baseKR4_Full) ||
1895 [ + - ]: 776 : (ecmd.advertising & ADVERTISED_40000baseCR4_Full) ||
1896 [ - + ]: 776 : (ecmd.advertising & ADVERTISED_40000baseSR4_Full) ||
1897 : 776 : (ecmd.advertising & ADVERTISED_40000baseLR4_Full)) {
1898 : 0 : netdev->advertised |= NETDEV_F_40GB_FD;
1899 : : }
1900 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_TP) {
1901 : 0 : netdev->advertised |= NETDEV_F_COPPER;
1902 : : }
1903 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_FIBRE) {
1904 : 0 : netdev->advertised |= NETDEV_F_FIBER;
1905 : : }
1906 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_Autoneg) {
1907 : 0 : netdev->advertised |= NETDEV_F_AUTONEG;
1908 : : }
1909 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_Pause) {
1910 : 0 : netdev->advertised |= NETDEV_F_PAUSE;
1911 : : }
1912 [ - + ]: 776 : if (ecmd.advertising & ADVERTISED_Asym_Pause) {
1913 : 0 : netdev->advertised |= NETDEV_F_PAUSE_ASYM;
1914 : : }
1915 : :
1916 : : /* Current settings. */
1917 : 776 : speed = ethtool_cmd_speed(&ecmd);
1918 [ + + ]: 776 : if (speed == SPEED_10) {
1919 [ + - ]: 80 : netdev->current = ecmd.duplex ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD;
1920 [ - + ]: 696 : } else if (speed == SPEED_100) {
1921 [ # # ]: 0 : netdev->current = ecmd.duplex ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD;
1922 [ - + ]: 696 : } else if (speed == SPEED_1000) {
1923 [ # # ]: 0 : netdev->current = ecmd.duplex ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD;
1924 [ + - ]: 696 : } else if (speed == SPEED_10000) {
1925 : 696 : netdev->current = NETDEV_F_10GB_FD;
1926 [ # # ]: 0 : } else if (speed == 40000) {
1927 : 0 : netdev->current = NETDEV_F_40GB_FD;
1928 [ # # ]: 0 : } else if (speed == 100000) {
1929 : 0 : netdev->current = NETDEV_F_100GB_FD;
1930 [ # # ]: 0 : } else if (speed == 1000000) {
1931 : 0 : netdev->current = NETDEV_F_1TB_FD;
1932 : : } else {
1933 : 0 : netdev->current = 0;
1934 : : }
1935 : :
1936 [ + - ]: 776 : if (ecmd.port == PORT_TP) {
1937 : 776 : netdev->current |= NETDEV_F_COPPER;
1938 [ # # ]: 0 : } else if (ecmd.port == PORT_FIBRE) {
1939 : 0 : netdev->current |= NETDEV_F_FIBER;
1940 : : }
1941 : :
1942 [ - + ]: 776 : if (ecmd.autoneg) {
1943 : 0 : netdev->current |= NETDEV_F_AUTONEG;
1944 : : }
1945 : :
1946 : : out:
1947 : 776 : netdev->cache_valid |= VALID_FEATURES;
1948 : 776 : netdev->get_features_error = error;
1949 : : }
1950 : :
1951 : : /* Stores the features supported by 'netdev' into of '*current', '*advertised',
1952 : : * '*supported', and '*peer'. Each value is a bitmap of NETDEV_* bits.
1953 : : * Returns 0 if successful, otherwise a positive errno value. */
1954 : : static int
1955 : 3955 : netdev_linux_get_features(const struct netdev *netdev_,
1956 : : enum netdev_features *current,
1957 : : enum netdev_features *advertised,
1958 : : enum netdev_features *supported,
1959 : : enum netdev_features *peer)
1960 : : {
1961 : 3955 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1962 : : int error;
1963 : :
1964 : 3955 : ovs_mutex_lock(&netdev->mutex);
1965 : 3955 : netdev_linux_read_features(netdev);
1966 [ + - ]: 3955 : if (!netdev->get_features_error) {
1967 : 3955 : *current = netdev->current;
1968 : 3955 : *advertised = netdev->advertised;
1969 : 3955 : *supported = netdev->supported;
1970 : 3955 : *peer = 0; /* XXX */
1971 : : }
1972 : 3955 : error = netdev->get_features_error;
1973 : 3955 : ovs_mutex_unlock(&netdev->mutex);
1974 : :
1975 : 3955 : return error;
1976 : : }
1977 : :
1978 : : /* Set the features advertised by 'netdev' to 'advertise'. */
1979 : : static int
1980 : 0 : netdev_linux_set_advertisements(struct netdev *netdev_,
1981 : : enum netdev_features advertise)
1982 : : {
1983 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
1984 : : struct ethtool_cmd ecmd;
1985 : : int error;
1986 : :
1987 : 0 : ovs_mutex_lock(&netdev->mutex);
1988 : :
1989 : 0 : COVERAGE_INC(netdev_get_ethtool);
1990 : 0 : memset(&ecmd, 0, sizeof ecmd);
1991 : 0 : error = netdev_linux_do_ethtool(netdev_get_name(netdev_), &ecmd,
1992 : : ETHTOOL_GSET, "ETHTOOL_GSET");
1993 [ # # ]: 0 : if (error) {
1994 : 0 : goto exit;
1995 : : }
1996 : :
1997 : 0 : ecmd.advertising = 0;
1998 [ # # ]: 0 : if (advertise & NETDEV_F_10MB_HD) {
1999 : 0 : ecmd.advertising |= ADVERTISED_10baseT_Half;
2000 : : }
2001 [ # # ]: 0 : if (advertise & NETDEV_F_10MB_FD) {
2002 : 0 : ecmd.advertising |= ADVERTISED_10baseT_Full;
2003 : : }
2004 [ # # ]: 0 : if (advertise & NETDEV_F_100MB_HD) {
2005 : 0 : ecmd.advertising |= ADVERTISED_100baseT_Half;
2006 : : }
2007 [ # # ]: 0 : if (advertise & NETDEV_F_100MB_FD) {
2008 : 0 : ecmd.advertising |= ADVERTISED_100baseT_Full;
2009 : : }
2010 [ # # ]: 0 : if (advertise & NETDEV_F_1GB_HD) {
2011 : 0 : ecmd.advertising |= ADVERTISED_1000baseT_Half;
2012 : : }
2013 [ # # ]: 0 : if (advertise & NETDEV_F_1GB_FD) {
2014 : 0 : ecmd.advertising |= ADVERTISED_1000baseT_Full;
2015 : : }
2016 [ # # ]: 0 : if (advertise & NETDEV_F_10GB_FD) {
2017 : 0 : ecmd.advertising |= ADVERTISED_10000baseT_Full;
2018 : : }
2019 [ # # ]: 0 : if (advertise & NETDEV_F_COPPER) {
2020 : 0 : ecmd.advertising |= ADVERTISED_TP;
2021 : : }
2022 [ # # ]: 0 : if (advertise & NETDEV_F_FIBER) {
2023 : 0 : ecmd.advertising |= ADVERTISED_FIBRE;
2024 : : }
2025 [ # # ]: 0 : if (advertise & NETDEV_F_AUTONEG) {
2026 : 0 : ecmd.advertising |= ADVERTISED_Autoneg;
2027 : : }
2028 [ # # ]: 0 : if (advertise & NETDEV_F_PAUSE) {
2029 : 0 : ecmd.advertising |= ADVERTISED_Pause;
2030 : : }
2031 [ # # ]: 0 : if (advertise & NETDEV_F_PAUSE_ASYM) {
2032 : 0 : ecmd.advertising |= ADVERTISED_Asym_Pause;
2033 : : }
2034 : 0 : COVERAGE_INC(netdev_set_ethtool);
2035 : 0 : error = netdev_linux_do_ethtool(netdev_get_name(netdev_), &ecmd,
2036 : : ETHTOOL_SSET, "ETHTOOL_SSET");
2037 : :
2038 : : exit:
2039 : 0 : ovs_mutex_unlock(&netdev->mutex);
2040 : 0 : return error;
2041 : : }
2042 : :
2043 : : /* Attempts to set input rate limiting (policing) policy. Returns 0 if
2044 : : * successful, otherwise a positive errno value. */
2045 : : static int
2046 : 4175 : netdev_linux_set_policing(struct netdev *netdev_,
2047 : : uint32_t kbits_rate, uint32_t kbits_burst)
2048 : : {
2049 : 4175 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2050 : 4175 : const char *netdev_name = netdev_get_name(netdev_);
2051 : : int error;
2052 : :
2053 : 4175 : kbits_burst = (!kbits_rate ? 0 /* Force to 0 if no rate specified. */
2054 [ - + ]: 4175 : : !kbits_burst ? 8000 /* Default to 8000 kbits if 0. */
2055 [ # # ]: 0 : : kbits_burst); /* Stick with user-specified value. */
2056 : :
2057 : 4175 : ovs_mutex_lock(&netdev->mutex);
2058 [ + + ]: 4175 : if (netdev->cache_valid & VALID_POLICING) {
2059 : 3251 : error = netdev->netdev_policing_error;
2060 [ + + ][ + - ]: 3251 : if (error || (netdev->kbits_rate == kbits_rate &&
[ - + ]
2061 : 3248 : netdev->kbits_burst == kbits_burst)) {
2062 : : /* Assume that settings haven't changed since we last set them. */
2063 : : goto out;
2064 : : }
2065 : 0 : netdev->cache_valid &= ~VALID_POLICING;
2066 : : }
2067 : :
2068 : 924 : COVERAGE_INC(netdev_set_policing);
2069 : : /* Remove any existing ingress qdisc. */
2070 : 924 : error = tc_add_del_ingress_qdisc(netdev_, false);
2071 [ + + ]: 924 : if (error) {
2072 [ + - ]: 2 : VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
2073 : : netdev_name, ovs_strerror(error));
2074 : 2 : goto out;
2075 : : }
2076 : :
2077 [ - + ]: 922 : if (kbits_rate) {
2078 : 0 : error = tc_add_del_ingress_qdisc(netdev_, true);
2079 [ # # ]: 0 : if (error) {
2080 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
2081 : : netdev_name, ovs_strerror(error));
2082 : 0 : goto out;
2083 : : }
2084 : :
2085 : 0 : error = tc_add_policer(netdev_, kbits_rate, kbits_burst);
2086 [ # # ]: 0 : if (error){
2087 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: adding policing action failed: %s",
2088 : : netdev_name, ovs_strerror(error));
2089 : 0 : goto out;
2090 : : }
2091 : : }
2092 : :
2093 : 922 : netdev->kbits_rate = kbits_rate;
2094 : 922 : netdev->kbits_burst = kbits_burst;
2095 : :
2096 : : out:
2097 [ + + ][ + - ]: 4175 : if (!error || error == ENODEV) {
2098 : 4175 : netdev->netdev_policing_error = error;
2099 : 4175 : netdev->cache_valid |= VALID_POLICING;
2100 : : }
2101 : 4175 : ovs_mutex_unlock(&netdev->mutex);
2102 : 4175 : return error;
2103 : : }
2104 : :
2105 : : static int
2106 : 0 : netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED,
2107 : : struct sset *types)
2108 : : {
2109 : : const struct tc_ops *const *opsp;
2110 [ # # ]: 0 : for (opsp = tcs; *opsp != NULL; opsp++) {
2111 : 0 : const struct tc_ops *ops = *opsp;
2112 [ # # ][ # # ]: 0 : if (ops->tc_install && ops->ovs_name[0] != '\0') {
2113 : 0 : sset_add(types, ops->ovs_name);
2114 : : }
2115 : : }
2116 : 0 : return 0;
2117 : : }
2118 : :
2119 : : static const struct tc_ops *
2120 : 4175 : tc_lookup_ovs_name(const char *name)
2121 : : {
2122 : : const struct tc_ops *const *opsp;
2123 : :
2124 [ + - ]: 29225 : for (opsp = tcs; *opsp != NULL; opsp++) {
2125 : 29225 : const struct tc_ops *ops = *opsp;
2126 [ + + ]: 29225 : if (!strcmp(name, ops->ovs_name)) {
2127 : 4175 : return ops;
2128 : : }
2129 : : }
2130 : 0 : return NULL;
2131 : : }
2132 : :
2133 : : static const struct tc_ops *
2134 : 233 : tc_lookup_linux_name(const char *name)
2135 : : {
2136 : : const struct tc_ops *const *opsp;
2137 : :
2138 [ + - ]: 932 : for (opsp = tcs; *opsp != NULL; opsp++) {
2139 : 932 : const struct tc_ops *ops = *opsp;
2140 [ + - ][ + + ]: 932 : if (ops->linux_name && !strcmp(name, ops->linux_name)) {
2141 : 233 : return ops;
2142 : : }
2143 : : }
2144 : 0 : return NULL;
2145 : : }
2146 : :
2147 : : static struct tc_queue *
2148 : 0 : tc_find_queue__(const struct netdev *netdev_, unsigned int queue_id,
2149 : : size_t hash)
2150 : : {
2151 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2152 : : struct tc_queue *queue;
2153 : :
2154 [ # # ][ # # ]: 0 : HMAP_FOR_EACH_IN_BUCKET (queue, hmap_node, hash, &netdev->tc->queues) {
2155 [ # # ]: 0 : if (queue->queue_id == queue_id) {
2156 : 0 : return queue;
2157 : : }
2158 : : }
2159 : 0 : return NULL;
2160 : : }
2161 : :
2162 : : static struct tc_queue *
2163 : 0 : tc_find_queue(const struct netdev *netdev, unsigned int queue_id)
2164 : : {
2165 : 0 : return tc_find_queue__(netdev, queue_id, hash_int(queue_id, 0));
2166 : : }
2167 : :
2168 : : static int
2169 : 0 : netdev_linux_get_qos_capabilities(const struct netdev *netdev OVS_UNUSED,
2170 : : const char *type,
2171 : : struct netdev_qos_capabilities *caps)
2172 : : {
2173 : 0 : const struct tc_ops *ops = tc_lookup_ovs_name(type);
2174 [ # # ]: 0 : if (!ops) {
2175 : 0 : return EOPNOTSUPP;
2176 : : }
2177 : 0 : caps->n_queues = ops->n_queues;
2178 : 0 : return 0;
2179 : : }
2180 : :
2181 : : static int
2182 : 0 : netdev_linux_get_qos(const struct netdev *netdev_,
2183 : : const char **typep, struct smap *details)
2184 : : {
2185 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2186 : : int error;
2187 : :
2188 : 0 : ovs_mutex_lock(&netdev->mutex);
2189 : 0 : error = tc_query_qdisc(netdev_);
2190 [ # # ]: 0 : if (!error) {
2191 : 0 : *typep = netdev->tc->ops->ovs_name;
2192 : 0 : error = (netdev->tc->ops->qdisc_get
2193 : 0 : ? netdev->tc->ops->qdisc_get(netdev_, details)
2194 [ # # ]: 0 : : 0);
2195 : : }
2196 : 0 : ovs_mutex_unlock(&netdev->mutex);
2197 : :
2198 : 0 : return error;
2199 : : }
2200 : :
2201 : : static int
2202 : 4175 : netdev_linux_set_qos(struct netdev *netdev_,
2203 : : const char *type, const struct smap *details)
2204 : : {
2205 : 4175 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2206 : : const struct tc_ops *new_ops;
2207 : : int error;
2208 : :
2209 : 4175 : new_ops = tc_lookup_ovs_name(type);
2210 [ + - ][ - + ]: 4175 : if (!new_ops || !new_ops->tc_install) {
2211 : 0 : return EOPNOTSUPP;
2212 : : }
2213 : :
2214 [ - + ]: 4175 : if (new_ops == &tc_ops_noop) {
2215 : 0 : return new_ops->tc_install(netdev_, details);
2216 : : }
2217 : :
2218 : 4175 : ovs_mutex_lock(&netdev->mutex);
2219 : 4175 : error = tc_query_qdisc(netdev_);
2220 [ - + ]: 4175 : if (error) {
2221 : 0 : goto exit;
2222 : : }
2223 : :
2224 [ + + ]: 4175 : if (new_ops == netdev->tc->ops) {
2225 [ - + ]: 3942 : error = new_ops->qdisc_set ? new_ops->qdisc_set(netdev_, details) : 0;
2226 : : } else {
2227 : : /* Delete existing qdisc. */
2228 : 233 : error = tc_del_qdisc(netdev_);
2229 [ - + ]: 233 : if (error) {
2230 : 0 : goto exit;
2231 : : }
2232 [ - + ]: 233 : ovs_assert(netdev->tc == NULL);
2233 : :
2234 : : /* Install new qdisc. */
2235 : 233 : error = new_ops->tc_install(netdev_, details);
2236 [ - + ]: 233 : ovs_assert((error == 0) == (netdev->tc != NULL));
2237 : : }
2238 : :
2239 : : exit:
2240 : 4175 : ovs_mutex_unlock(&netdev->mutex);
2241 : 4175 : return error;
2242 : : }
2243 : :
2244 : : static int
2245 : 0 : netdev_linux_get_queue(const struct netdev *netdev_,
2246 : : unsigned int queue_id, struct smap *details)
2247 : : {
2248 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2249 : : int error;
2250 : :
2251 : 0 : ovs_mutex_lock(&netdev->mutex);
2252 : 0 : error = tc_query_qdisc(netdev_);
2253 [ # # ]: 0 : if (!error) {
2254 : 0 : struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
2255 : 0 : error = (queue
2256 : 0 : ? netdev->tc->ops->class_get(netdev_, queue, details)
2257 [ # # ]: 0 : : ENOENT);
2258 : : }
2259 : 0 : ovs_mutex_unlock(&netdev->mutex);
2260 : :
2261 : 0 : return error;
2262 : : }
2263 : :
2264 : : static int
2265 : 0 : netdev_linux_set_queue(struct netdev *netdev_,
2266 : : unsigned int queue_id, const struct smap *details)
2267 : : {
2268 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2269 : : int error;
2270 : :
2271 : 0 : ovs_mutex_lock(&netdev->mutex);
2272 : 0 : error = tc_query_qdisc(netdev_);
2273 [ # # ]: 0 : if (!error) {
2274 : 0 : error = (queue_id < netdev->tc->ops->n_queues
2275 [ # # ]: 0 : && netdev->tc->ops->class_set
2276 : 0 : ? netdev->tc->ops->class_set(netdev_, queue_id, details)
2277 [ # # ]: 0 : : EINVAL);
2278 : : }
2279 : 0 : ovs_mutex_unlock(&netdev->mutex);
2280 : :
2281 : 0 : return error;
2282 : : }
2283 : :
2284 : : static int
2285 : 0 : netdev_linux_delete_queue(struct netdev *netdev_, unsigned int queue_id)
2286 : : {
2287 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2288 : : int error;
2289 : :
2290 : 0 : ovs_mutex_lock(&netdev->mutex);
2291 : 0 : error = tc_query_qdisc(netdev_);
2292 [ # # ]: 0 : if (!error) {
2293 [ # # ]: 0 : if (netdev->tc->ops->class_delete) {
2294 : 0 : struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
2295 : 0 : error = (queue
2296 : 0 : ? netdev->tc->ops->class_delete(netdev_, queue)
2297 [ # # ]: 0 : : ENOENT);
2298 : : } else {
2299 : 0 : error = EINVAL;
2300 : : }
2301 : : }
2302 : 0 : ovs_mutex_unlock(&netdev->mutex);
2303 : :
2304 : 0 : return error;
2305 : : }
2306 : :
2307 : : static int
2308 : 0 : netdev_linux_get_queue_stats(const struct netdev *netdev_,
2309 : : unsigned int queue_id,
2310 : : struct netdev_queue_stats *stats)
2311 : : {
2312 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2313 : : int error;
2314 : :
2315 : 0 : ovs_mutex_lock(&netdev->mutex);
2316 : 0 : error = tc_query_qdisc(netdev_);
2317 [ # # ]: 0 : if (!error) {
2318 [ # # ]: 0 : if (netdev->tc->ops->class_get_stats) {
2319 : 0 : const struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
2320 [ # # ]: 0 : if (queue) {
2321 : 0 : stats->created = queue->created;
2322 : 0 : error = netdev->tc->ops->class_get_stats(netdev_, queue,
2323 : : stats);
2324 : : } else {
2325 : 0 : error = ENOENT;
2326 : : }
2327 : : } else {
2328 : 0 : error = EOPNOTSUPP;
2329 : : }
2330 : : }
2331 : 0 : ovs_mutex_unlock(&netdev->mutex);
2332 : :
2333 : 0 : return error;
2334 : : }
2335 : :
2336 : : struct queue_dump_state {
2337 : : struct nl_dump dump;
2338 : : struct ofpbuf buf;
2339 : : };
2340 : :
2341 : : static bool
2342 : 0 : start_queue_dump(const struct netdev *netdev, struct queue_dump_state *state)
2343 : : {
2344 : : struct ofpbuf request;
2345 : : struct tcmsg *tcmsg;
2346 : :
2347 : 0 : tcmsg = tc_make_request(netdev, RTM_GETTCLASS, 0, &request);
2348 [ # # ]: 0 : if (!tcmsg) {
2349 : 0 : return false;
2350 : : }
2351 : 0 : tcmsg->tcm_parent = 0;
2352 : 0 : nl_dump_start(&state->dump, NETLINK_ROUTE, &request);
2353 : 0 : ofpbuf_uninit(&request);
2354 : :
2355 : 0 : ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE);
2356 : 0 : return true;
2357 : : }
2358 : :
2359 : : static int
2360 : 0 : finish_queue_dump(struct queue_dump_state *state)
2361 : : {
2362 : 0 : ofpbuf_uninit(&state->buf);
2363 : 0 : return nl_dump_done(&state->dump);
2364 : : }
2365 : :
2366 : : struct netdev_linux_queue_state {
2367 : : unsigned int *queues;
2368 : : size_t cur_queue;
2369 : : size_t n_queues;
2370 : : };
2371 : :
2372 : : static int
2373 : 0 : netdev_linux_queue_dump_start(const struct netdev *netdev_, void **statep)
2374 : : {
2375 : 0 : const struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2376 : : int error;
2377 : :
2378 : 0 : ovs_mutex_lock(&netdev->mutex);
2379 : 0 : error = tc_query_qdisc(netdev_);
2380 [ # # ]: 0 : if (!error) {
2381 [ # # ]: 0 : if (netdev->tc->ops->class_get) {
2382 : : struct netdev_linux_queue_state *state;
2383 : : struct tc_queue *queue;
2384 : : size_t i;
2385 : :
2386 : 0 : *statep = state = xmalloc(sizeof *state);
2387 : 0 : state->n_queues = hmap_count(&netdev->tc->queues);
2388 : 0 : state->cur_queue = 0;
2389 : 0 : state->queues = xmalloc(state->n_queues * sizeof *state->queues);
2390 : :
2391 : 0 : i = 0;
2392 [ # # ][ # # ]: 0 : HMAP_FOR_EACH (queue, hmap_node, &netdev->tc->queues) {
2393 : 0 : state->queues[i++] = queue->queue_id;
2394 : : }
2395 : : } else {
2396 : 0 : error = EOPNOTSUPP;
2397 : : }
2398 : : }
2399 : 0 : ovs_mutex_unlock(&netdev->mutex);
2400 : :
2401 : 0 : return error;
2402 : : }
2403 : :
2404 : : static int
2405 : 0 : netdev_linux_queue_dump_next(const struct netdev *netdev_, void *state_,
2406 : : unsigned int *queue_idp, struct smap *details)
2407 : : {
2408 : 0 : const struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2409 : 0 : struct netdev_linux_queue_state *state = state_;
2410 : 0 : int error = EOF;
2411 : :
2412 : 0 : ovs_mutex_lock(&netdev->mutex);
2413 [ # # ]: 0 : while (state->cur_queue < state->n_queues) {
2414 : 0 : unsigned int queue_id = state->queues[state->cur_queue++];
2415 : 0 : struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
2416 : :
2417 [ # # ]: 0 : if (queue) {
2418 : 0 : *queue_idp = queue_id;
2419 : 0 : error = netdev->tc->ops->class_get(netdev_, queue, details);
2420 : 0 : break;
2421 : : }
2422 : : }
2423 : 0 : ovs_mutex_unlock(&netdev->mutex);
2424 : :
2425 : 0 : return error;
2426 : : }
2427 : :
2428 : : static int
2429 : 0 : netdev_linux_queue_dump_done(const struct netdev *netdev OVS_UNUSED,
2430 : : void *state_)
2431 : : {
2432 : 0 : struct netdev_linux_queue_state *state = state_;
2433 : :
2434 : 0 : free(state->queues);
2435 : 0 : free(state);
2436 : 0 : return 0;
2437 : : }
2438 : :
2439 : : static int
2440 : 0 : netdev_linux_dump_queue_stats(const struct netdev *netdev_,
2441 : : netdev_dump_queue_stats_cb *cb, void *aux)
2442 : : {
2443 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2444 : : int error;
2445 : :
2446 : 0 : ovs_mutex_lock(&netdev->mutex);
2447 : 0 : error = tc_query_qdisc(netdev_);
2448 [ # # ]: 0 : if (!error) {
2449 : : struct queue_dump_state state;
2450 : :
2451 [ # # ]: 0 : if (!netdev->tc->ops->class_dump_stats) {
2452 : 0 : error = EOPNOTSUPP;
2453 [ # # ]: 0 : } else if (!start_queue_dump(netdev_, &state)) {
2454 : 0 : error = ENODEV;
2455 : : } else {
2456 : : struct ofpbuf msg;
2457 : : int retval;
2458 : :
2459 [ # # ]: 0 : while (nl_dump_next(&state.dump, &msg, &state.buf)) {
2460 : 0 : retval = netdev->tc->ops->class_dump_stats(netdev_, &msg,
2461 : : cb, aux);
2462 [ # # ]: 0 : if (retval) {
2463 : 0 : error = retval;
2464 : : }
2465 : : }
2466 : :
2467 : 0 : retval = finish_queue_dump(&state);
2468 [ # # ]: 0 : if (retval) {
2469 : 0 : error = retval;
2470 : : }
2471 : : }
2472 : : }
2473 : 0 : ovs_mutex_unlock(&netdev->mutex);
2474 : :
2475 : 0 : return error;
2476 : : }
2477 : :
2478 : : static int
2479 : 0 : netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address,
2480 : : struct in_addr netmask)
2481 : : {
2482 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2483 : : int error;
2484 : :
2485 : 0 : ovs_mutex_lock(&netdev->mutex);
2486 : 0 : error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address);
2487 [ # # ]: 0 : if (!error) {
2488 [ # # ]: 0 : if (address.s_addr != INADDR_ANY) {
2489 : 0 : error = do_set_addr(netdev_, SIOCSIFNETMASK,
2490 : : "SIOCSIFNETMASK", netmask);
2491 : : }
2492 : : }
2493 : :
2494 : 0 : ovs_mutex_unlock(&netdev->mutex);
2495 : :
2496 : 0 : return error;
2497 : : }
2498 : :
2499 : : /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address.
2500 : : * Otherwise, sets '*in6' to 'in6addr_any' and returns the corresponding
2501 : : * error. */
2502 : : static int
2503 : 41940 : netdev_linux_get_addr_list(const struct netdev *netdev_,
2504 : : struct in6_addr **addr, struct in6_addr **mask, int *n_cnt)
2505 : : {
2506 : 41940 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2507 : : int error;
2508 : :
2509 : 41940 : ovs_mutex_lock(&netdev->mutex);
2510 : 41940 : error = netdev_get_addrs(netdev_get_name(netdev_), addr, mask, n_cnt);
2511 : 41940 : ovs_mutex_unlock(&netdev->mutex);
2512 : :
2513 : 41940 : return error;
2514 : : }
2515 : :
2516 : : static void
2517 : 0 : make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
2518 : : {
2519 : : struct sockaddr_in sin;
2520 : 0 : memset(&sin, 0, sizeof sin);
2521 : 0 : sin.sin_family = AF_INET;
2522 : 0 : sin.sin_addr = addr;
2523 : 0 : sin.sin_port = 0;
2524 : :
2525 : 0 : memset(sa, 0, sizeof *sa);
2526 : 0 : memcpy(sa, &sin, sizeof sin);
2527 : 0 : }
2528 : :
2529 : : static int
2530 : 0 : do_set_addr(struct netdev *netdev,
2531 : : int ioctl_nr, const char *ioctl_name, struct in_addr addr)
2532 : : {
2533 : : struct ifreq ifr;
2534 : :
2535 : 0 : make_in4_sockaddr(&ifr.ifr_addr, addr);
2536 : 0 : return af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr,
2537 : : ioctl_name);
2538 : : }
2539 : :
2540 : : /* Adds 'router' as a default IP gateway. */
2541 : : static int
2542 : 0 : netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router)
2543 : : {
2544 : 0 : struct in_addr any = { INADDR_ANY };
2545 : : struct rtentry rt;
2546 : : int error;
2547 : :
2548 : 0 : memset(&rt, 0, sizeof rt);
2549 : 0 : make_in4_sockaddr(&rt.rt_dst, any);
2550 : 0 : make_in4_sockaddr(&rt.rt_gateway, router);
2551 : 0 : make_in4_sockaddr(&rt.rt_genmask, any);
2552 : 0 : rt.rt_flags = RTF_UP | RTF_GATEWAY;
2553 : 0 : error = af_inet_ioctl(SIOCADDRT, &rt);
2554 [ # # ]: 0 : if (error) {
2555 [ # # ]: 0 : VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error));
2556 : : }
2557 : 0 : return error;
2558 : : }
2559 : :
2560 : : static int
2561 : 0 : netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
2562 : : char **netdev_name)
2563 : : {
2564 : : static const char fn[] = "/proc/net/route";
2565 : : FILE *stream;
2566 : : char line[256];
2567 : : int ln;
2568 : :
2569 : 0 : *netdev_name = NULL;
2570 : 0 : stream = fopen(fn, "r");
2571 [ # # ]: 0 : if (stream == NULL) {
2572 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, ovs_strerror(errno));
2573 : 0 : return errno;
2574 : : }
2575 : :
2576 : 0 : ln = 0;
2577 [ # # ]: 0 : while (fgets(line, sizeof line, stream)) {
2578 [ # # ]: 0 : if (++ln >= 2) {
2579 : : char iface[17];
2580 : : ovs_be32 dest, gateway, mask;
2581 : : int refcnt, metric, mtu;
2582 : : unsigned int flags, use, window, irtt;
2583 : :
2584 [ # # ]: 0 : if (!ovs_scan(line,
2585 : : "%16s %"SCNx32" %"SCNx32" %04X %d %u %d %"SCNx32
2586 : : " %d %u %u\n",
2587 : : iface, &dest, &gateway, &flags, &refcnt,
2588 : : &use, &metric, &mask, &mtu, &window, &irtt)) {
2589 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: could not parse line %d: %s",
2590 : : fn, ln, line);
2591 : 0 : continue;
2592 : : }
2593 [ # # ]: 0 : if (!(flags & RTF_UP)) {
2594 : : /* Skip routes that aren't up. */
2595 : 0 : continue;
2596 : : }
2597 : :
2598 : : /* The output of 'dest', 'mask', and 'gateway' were given in
2599 : : * network byte order, so we don't need need any endian
2600 : : * conversions here. */
2601 [ # # ]: 0 : if ((dest & mask) == (host->s_addr & mask)) {
2602 [ # # ]: 0 : if (!gateway) {
2603 : : /* The host is directly reachable. */
2604 : 0 : next_hop->s_addr = 0;
2605 : : } else {
2606 : : /* To reach the host, we must go through a gateway. */
2607 : 0 : next_hop->s_addr = gateway;
2608 : : }
2609 : 0 : *netdev_name = xstrdup(iface);
2610 : 0 : fclose(stream);
2611 : 0 : return 0;
2612 : : }
2613 : : }
2614 : : }
2615 : :
2616 : 0 : fclose(stream);
2617 : 0 : return ENXIO;
2618 : : }
2619 : :
2620 : : static int
2621 : 2998 : netdev_linux_get_status(const struct netdev *netdev_, struct smap *smap)
2622 : : {
2623 : 2998 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2624 : 2998 : int error = 0;
2625 : :
2626 : 2998 : ovs_mutex_lock(&netdev->mutex);
2627 [ + + ]: 2998 : if (!(netdev->cache_valid & VALID_DRVINFO)) {
2628 : 268 : struct ethtool_cmd *cmd = (struct ethtool_cmd *) &netdev->drvinfo;
2629 : :
2630 : 268 : COVERAGE_INC(netdev_get_ethtool);
2631 : 268 : memset(&netdev->drvinfo, 0, sizeof netdev->drvinfo);
2632 : 268 : error = netdev_linux_do_ethtool(netdev->up.name,
2633 : : cmd,
2634 : : ETHTOOL_GDRVINFO,
2635 : : "ETHTOOL_GDRVINFO");
2636 [ + - ]: 268 : if (!error) {
2637 : 268 : netdev->cache_valid |= VALID_DRVINFO;
2638 : : }
2639 : : }
2640 : :
2641 [ + - ]: 2998 : if (!error) {
2642 : 2998 : smap_add(smap, "driver_name", netdev->drvinfo.driver);
2643 : 2998 : smap_add(smap, "driver_version", netdev->drvinfo.version);
2644 : 2998 : smap_add(smap, "firmware_version", netdev->drvinfo.fw_version);
2645 : : }
2646 : 2998 : ovs_mutex_unlock(&netdev->mutex);
2647 : :
2648 : 2998 : return error;
2649 : : }
2650 : :
2651 : : static int
2652 : 1238 : netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED,
2653 : : struct smap *smap)
2654 : : {
2655 : 1238 : smap_add(smap, "driver_name", "openvswitch");
2656 : 1238 : return 0;
2657 : : }
2658 : :
2659 : : /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be
2660 : : * successfully retrieved, it stores the corresponding MAC address in 'mac' and
2661 : : * returns 0. Otherwise, it returns a positive errno value; in particular,
2662 : : * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */
2663 : : static int
2664 : 0 : netdev_linux_arp_lookup(const struct netdev *netdev,
2665 : : ovs_be32 ip, struct eth_addr *mac)
2666 : : {
2667 : : struct arpreq r;
2668 : : struct sockaddr_in sin;
2669 : : int retval;
2670 : :
2671 : 0 : memset(&r, 0, sizeof r);
2672 : 0 : memset(&sin, 0, sizeof sin);
2673 : 0 : sin.sin_family = AF_INET;
2674 : 0 : sin.sin_addr.s_addr = ip;
2675 : 0 : sin.sin_port = 0;
2676 : 0 : memcpy(&r.arp_pa, &sin, sizeof sin);
2677 : 0 : r.arp_ha.sa_family = ARPHRD_ETHER;
2678 : 0 : r.arp_flags = 0;
2679 : 0 : ovs_strzcpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev);
2680 : 0 : COVERAGE_INC(netdev_arp_lookup);
2681 : 0 : retval = af_inet_ioctl(SIOCGARP, &r);
2682 [ # # ]: 0 : if (!retval) {
2683 : 0 : memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN);
2684 [ # # ]: 0 : } else if (retval != ENXIO) {
2685 [ # # ]: 0 : VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s",
2686 : : netdev_get_name(netdev), IP_ARGS(ip),
2687 : : ovs_strerror(retval));
2688 : : }
2689 : 0 : return retval;
2690 : : }
2691 : :
2692 : : static int
2693 : 65454 : nd_to_iff_flags(enum netdev_flags nd)
2694 : : {
2695 : 65454 : int iff = 0;
2696 [ + + ]: 65454 : if (nd & NETDEV_UP) {
2697 : 35 : iff |= IFF_UP;
2698 : : }
2699 [ + + ]: 65454 : if (nd & NETDEV_PROMISC) {
2700 : 179 : iff |= IFF_PROMISC;
2701 : : }
2702 [ - + ]: 65454 : if (nd & NETDEV_LOOPBACK) {
2703 : 0 : iff |= IFF_LOOPBACK;
2704 : : }
2705 : 65454 : return iff;
2706 : : }
2707 : :
2708 : : static int
2709 : 32727 : iff_to_nd_flags(int iff)
2710 : : {
2711 : 32727 : enum netdev_flags nd = 0;
2712 [ + + ]: 32727 : if (iff & IFF_UP) {
2713 : 25674 : nd |= NETDEV_UP;
2714 : : }
2715 [ + + ]: 32727 : if (iff & IFF_PROMISC) {
2716 : 7385 : nd |= NETDEV_PROMISC;
2717 : : }
2718 [ + + ]: 32727 : if (iff & IFF_LOOPBACK) {
2719 : 10038 : nd |= NETDEV_LOOPBACK;
2720 : : }
2721 : 32727 : return nd;
2722 : : }
2723 : :
2724 : : static int
2725 : 32727 : update_flags(struct netdev_linux *netdev, enum netdev_flags off,
2726 : : enum netdev_flags on, enum netdev_flags *old_flagsp)
2727 : : OVS_REQUIRES(netdev->mutex)
2728 : : {
2729 : : int old_flags, new_flags;
2730 : 32727 : int error = 0;
2731 : :
2732 : 32727 : old_flags = netdev->ifi_flags;
2733 : 32727 : *old_flagsp = iff_to_nd_flags(old_flags);
2734 : 32727 : new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
2735 [ + + ]: 32727 : if (new_flags != old_flags) {
2736 : 179 : error = set_flags(netdev_get_name(&netdev->up), new_flags);
2737 : 179 : get_flags(&netdev->up, &netdev->ifi_flags);
2738 : : }
2739 : :
2740 : 32727 : return error;
2741 : : }
2742 : :
2743 : : static int
2744 : 32692 : netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
2745 : : enum netdev_flags on, enum netdev_flags *old_flagsp)
2746 : : {
2747 : 32692 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2748 : : int error;
2749 : :
2750 : 32692 : ovs_mutex_lock(&netdev->mutex);
2751 : 32692 : error = update_flags(netdev, off, on, old_flagsp);
2752 : 32692 : ovs_mutex_unlock(&netdev->mutex);
2753 : :
2754 : 32692 : return error;
2755 : : }
2756 : :
2757 : : #define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, \
2758 : : GET_FEATURES, GET_STATUS) \
2759 : : { \
2760 : : NAME, \
2761 : : false, /* is_pmd */ \
2762 : : \
2763 : : NULL, \
2764 : : netdev_linux_run, \
2765 : : netdev_linux_wait, \
2766 : : \
2767 : : netdev_linux_alloc, \
2768 : : CONSTRUCT, \
2769 : : netdev_linux_destruct, \
2770 : : netdev_linux_dealloc, \
2771 : : NULL, /* get_config */ \
2772 : : NULL, /* set_config */ \
2773 : : NULL, /* get_tunnel_config */ \
2774 : : NULL, /* build header */ \
2775 : : NULL, /* push header */ \
2776 : : NULL, /* pop header */ \
2777 : : NULL, /* get_numa_id */ \
2778 : : NULL, /* set_tx_multiq */ \
2779 : : \
2780 : : netdev_linux_send, \
2781 : : netdev_linux_send_wait, \
2782 : : \
2783 : : netdev_linux_set_etheraddr, \
2784 : : netdev_linux_get_etheraddr, \
2785 : : netdev_linux_get_mtu, \
2786 : : netdev_linux_set_mtu, \
2787 : : netdev_linux_get_ifindex, \
2788 : : netdev_linux_get_carrier, \
2789 : : netdev_linux_get_carrier_resets, \
2790 : : netdev_linux_set_miimon_interval, \
2791 : : GET_STATS, \
2792 : : \
2793 : : GET_FEATURES, \
2794 : : netdev_linux_set_advertisements, \
2795 : : \
2796 : : netdev_linux_set_policing, \
2797 : : netdev_linux_get_qos_types, \
2798 : : netdev_linux_get_qos_capabilities, \
2799 : : netdev_linux_get_qos, \
2800 : : netdev_linux_set_qos, \
2801 : : netdev_linux_get_queue, \
2802 : : netdev_linux_set_queue, \
2803 : : netdev_linux_delete_queue, \
2804 : : netdev_linux_get_queue_stats, \
2805 : : netdev_linux_queue_dump_start, \
2806 : : netdev_linux_queue_dump_next, \
2807 : : netdev_linux_queue_dump_done, \
2808 : : netdev_linux_dump_queue_stats, \
2809 : : \
2810 : : netdev_linux_set_in4, \
2811 : : netdev_linux_get_addr_list, \
2812 : : netdev_linux_add_router, \
2813 : : netdev_linux_get_next_hop, \
2814 : : GET_STATUS, \
2815 : : netdev_linux_arp_lookup, \
2816 : : \
2817 : : netdev_linux_update_flags, \
2818 : : NULL, /* reconfigure */ \
2819 : : \
2820 : : netdev_linux_rxq_alloc, \
2821 : : netdev_linux_rxq_construct, \
2822 : : netdev_linux_rxq_destruct, \
2823 : : netdev_linux_rxq_dealloc, \
2824 : : netdev_linux_rxq_recv, \
2825 : : netdev_linux_rxq_wait, \
2826 : : netdev_linux_rxq_drain, \
2827 : : }
2828 : :
2829 : : const struct netdev_class netdev_linux_class =
2830 : : NETDEV_LINUX_CLASS(
2831 : : "system",
2832 : : netdev_linux_construct,
2833 : : netdev_linux_get_stats,
2834 : : netdev_linux_get_features,
2835 : : netdev_linux_get_status);
2836 : :
2837 : : const struct netdev_class netdev_tap_class =
2838 : : NETDEV_LINUX_CLASS(
2839 : : "tap",
2840 : : netdev_linux_construct_tap,
2841 : : netdev_tap_get_stats,
2842 : : netdev_linux_get_features,
2843 : : netdev_linux_get_status);
2844 : :
2845 : : const struct netdev_class netdev_internal_class =
2846 : : NETDEV_LINUX_CLASS(
2847 : : "internal",
2848 : : netdev_linux_construct,
2849 : : netdev_internal_get_stats,
2850 : : NULL, /* get_features */
2851 : : netdev_internal_get_status);
2852 : :
2853 : :
2854 : : #define CODEL_N_QUEUES 0x0000
2855 : :
2856 : : /* In sufficiently new kernel headers these are defined as enums in
2857 : : * <linux/pkt_sched.h>. Define them here as macros to help out with older
2858 : : * kernels. (This overrides any enum definition in the header file but that's
2859 : : * harmless.) */
2860 : : #define TCA_CODEL_TARGET 1
2861 : : #define TCA_CODEL_LIMIT 2
2862 : : #define TCA_CODEL_INTERVAL 3
2863 : :
2864 : : struct codel {
2865 : : struct tc tc;
2866 : : uint32_t target;
2867 : : uint32_t limit;
2868 : : uint32_t interval;
2869 : : };
2870 : :
2871 : : static struct codel *
2872 : 0 : codel_get__(const struct netdev *netdev_)
2873 : : {
2874 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2875 : 0 : return CONTAINER_OF(netdev->tc, struct codel, tc);
2876 : : }
2877 : :
2878 : : static void
2879 : 0 : codel_install__(struct netdev *netdev_, uint32_t target, uint32_t limit,
2880 : : uint32_t interval)
2881 : : {
2882 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2883 : : struct codel *codel;
2884 : :
2885 : 0 : codel = xmalloc(sizeof *codel);
2886 : 0 : tc_init(&codel->tc, &tc_ops_codel);
2887 : 0 : codel->target = target;
2888 : 0 : codel->limit = limit;
2889 : 0 : codel->interval = interval;
2890 : :
2891 : 0 : netdev->tc = &codel->tc;
2892 : 0 : }
2893 : :
2894 : : static int
2895 : 0 : codel_setup_qdisc__(struct netdev *netdev, uint32_t target, uint32_t limit,
2896 : : uint32_t interval)
2897 : : {
2898 : : size_t opt_offset;
2899 : : struct ofpbuf request;
2900 : : struct tcmsg *tcmsg;
2901 : : uint32_t otarget, olimit, ointerval;
2902 : : int error;
2903 : :
2904 : 0 : tc_del_qdisc(netdev);
2905 : :
2906 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
2907 : : NLM_F_EXCL | NLM_F_CREATE, &request);
2908 [ # # ]: 0 : if (!tcmsg) {
2909 : 0 : return ENODEV;
2910 : : }
2911 : 0 : tcmsg->tcm_handle = tc_make_handle(1, 0);
2912 : 0 : tcmsg->tcm_parent = TC_H_ROOT;
2913 : :
2914 [ # # ]: 0 : otarget = target ? target : 5000;
2915 [ # # ]: 0 : olimit = limit ? limit : 10240;
2916 [ # # ]: 0 : ointerval = interval ? interval : 100000;
2917 : :
2918 : 0 : nl_msg_put_string(&request, TCA_KIND, "codel");
2919 : 0 : opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
2920 : 0 : nl_msg_put_u32(&request, TCA_CODEL_TARGET, otarget);
2921 : 0 : nl_msg_put_u32(&request, TCA_CODEL_LIMIT, olimit);
2922 : 0 : nl_msg_put_u32(&request, TCA_CODEL_INTERVAL, ointerval);
2923 : 0 : nl_msg_end_nested(&request, opt_offset);
2924 : :
2925 : 0 : error = tc_transact(&request, NULL);
2926 [ # # ]: 0 : if (error) {
2927 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to replace %s qdisc, "
2928 : : "target %u, limit %u, interval %u error %d(%s)",
2929 : : netdev_get_name(netdev),
2930 : : otarget, olimit, ointerval,
2931 : : error, ovs_strerror(error));
2932 : : }
2933 : 0 : return error;
2934 : : }
2935 : :
2936 : : static void
2937 : 0 : codel_parse_qdisc_details__(struct netdev *netdev OVS_UNUSED,
2938 : : const struct smap *details, struct codel *codel)
2939 : : {
2940 : 0 : codel->target = smap_get_ullong(details, "target", 0);
2941 : 0 : codel->limit = smap_get_ullong(details, "limit", 0);
2942 : 0 : codel->interval = smap_get_ullong(details, "interval", 0);
2943 : :
2944 [ # # ]: 0 : if (!codel->target) {
2945 : 0 : codel->target = 5000;
2946 : : }
2947 [ # # ]: 0 : if (!codel->limit) {
2948 : 0 : codel->limit = 10240;
2949 : : }
2950 [ # # ]: 0 : if (!codel->interval) {
2951 : 0 : codel->interval = 100000;
2952 : : }
2953 : 0 : }
2954 : :
2955 : : static int
2956 : 0 : codel_tc_install(struct netdev *netdev, const struct smap *details)
2957 : : {
2958 : : int error;
2959 : : struct codel codel;
2960 : :
2961 : 0 : codel_parse_qdisc_details__(netdev, details, &codel);
2962 : 0 : error = codel_setup_qdisc__(netdev, codel.target, codel.limit,
2963 : : codel.interval);
2964 [ # # ]: 0 : if (!error) {
2965 : 0 : codel_install__(netdev, codel.target, codel.limit, codel.interval);
2966 : : }
2967 : 0 : return error;
2968 : : }
2969 : :
2970 : : static int
2971 : 0 : codel_parse_tca_options__(struct nlattr *nl_options, struct codel *codel)
2972 : : {
2973 : : static const struct nl_policy tca_codel_policy[] = {
2974 : : [TCA_CODEL_TARGET] = { .type = NL_A_U32 },
2975 : : [TCA_CODEL_LIMIT] = { .type = NL_A_U32 },
2976 : : [TCA_CODEL_INTERVAL] = { .type = NL_A_U32 }
2977 : : };
2978 : :
2979 : : struct nlattr *attrs[ARRAY_SIZE(tca_codel_policy)];
2980 : :
2981 [ # # ]: 0 : if (!nl_parse_nested(nl_options, tca_codel_policy,
2982 : : attrs, ARRAY_SIZE(tca_codel_policy))) {
2983 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse CoDel class options");
2984 : 0 : return EPROTO;
2985 : : }
2986 : :
2987 : 0 : codel->target = nl_attr_get_u32(attrs[TCA_CODEL_TARGET]);
2988 : 0 : codel->limit = nl_attr_get_u32(attrs[TCA_CODEL_LIMIT]);
2989 : 0 : codel->interval = nl_attr_get_u32(attrs[TCA_CODEL_INTERVAL]);
2990 : 0 : return 0;
2991 : : }
2992 : :
2993 : : static int
2994 : 0 : codel_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg)
2995 : : {
2996 : : struct nlattr *nlattr;
2997 : : const char * kind;
2998 : : int error;
2999 : : struct codel codel;
3000 : :
3001 : 0 : error = tc_parse_qdisc(nlmsg, &kind, &nlattr);
3002 [ # # ]: 0 : if (error != 0) {
3003 : 0 : return error;
3004 : : }
3005 : :
3006 : 0 : error = codel_parse_tca_options__(nlattr, &codel);
3007 [ # # ]: 0 : if (error != 0) {
3008 : 0 : return error;
3009 : : }
3010 : :
3011 : 0 : codel_install__(netdev, codel.target, codel.limit, codel.interval);
3012 : 0 : return 0;
3013 : : }
3014 : :
3015 : :
3016 : : static void
3017 : 0 : codel_tc_destroy(struct tc *tc)
3018 : : {
3019 : 0 : struct codel *codel = CONTAINER_OF(tc, struct codel, tc);
3020 : 0 : tc_destroy(tc);
3021 : 0 : free(codel);
3022 : 0 : }
3023 : :
3024 : : static int
3025 : 0 : codel_qdisc_get(const struct netdev *netdev, struct smap *details)
3026 : : {
3027 : 0 : const struct codel *codel = codel_get__(netdev);
3028 : 0 : smap_add_format(details, "target", "%u", codel->target);
3029 : 0 : smap_add_format(details, "limit", "%u", codel->limit);
3030 : 0 : smap_add_format(details, "interval", "%u", codel->interval);
3031 : 0 : return 0;
3032 : : }
3033 : :
3034 : : static int
3035 : 0 : codel_qdisc_set(struct netdev *netdev, const struct smap *details)
3036 : : {
3037 : : struct codel codel;
3038 : :
3039 : 0 : codel_parse_qdisc_details__(netdev, details, &codel);
3040 : 0 : codel_install__(netdev, codel.target, codel.limit, codel.interval);
3041 : 0 : codel_get__(netdev)->target = codel.target;
3042 : 0 : codel_get__(netdev)->limit = codel.limit;
3043 : 0 : codel_get__(netdev)->interval = codel.interval;
3044 : 0 : return 0;
3045 : : }
3046 : :
3047 : : static const struct tc_ops tc_ops_codel = {
3048 : : "codel", /* linux_name */
3049 : : "linux-codel", /* ovs_name */
3050 : : CODEL_N_QUEUES, /* n_queues */
3051 : : codel_tc_install,
3052 : : codel_tc_load,
3053 : : codel_tc_destroy,
3054 : : codel_qdisc_get,
3055 : : codel_qdisc_set,
3056 : : NULL,
3057 : : NULL,
3058 : : NULL,
3059 : : NULL,
3060 : : NULL
3061 : : };
3062 : :
3063 : : /* FQ-CoDel traffic control class. */
3064 : :
3065 : : #define FQCODEL_N_QUEUES 0x0000
3066 : :
3067 : : /* In sufficiently new kernel headers these are defined as enums in
3068 : : * <linux/pkt_sched.h>. Define them here as macros to help out with older
3069 : : * kernels. (This overrides any enum definition in the header file but that's
3070 : : * harmless.) */
3071 : : #define TCA_FQ_CODEL_TARGET 1
3072 : : #define TCA_FQ_CODEL_LIMIT 2
3073 : : #define TCA_FQ_CODEL_INTERVAL 3
3074 : : #define TCA_FQ_CODEL_ECN 4
3075 : : #define TCA_FQ_CODEL_FLOWS 5
3076 : : #define TCA_FQ_CODEL_QUANTUM 6
3077 : :
3078 : : struct fqcodel {
3079 : : struct tc tc;
3080 : : uint32_t target;
3081 : : uint32_t limit;
3082 : : uint32_t interval;
3083 : : uint32_t flows;
3084 : : uint32_t quantum;
3085 : : };
3086 : :
3087 : : static struct fqcodel *
3088 : 0 : fqcodel_get__(const struct netdev *netdev_)
3089 : : {
3090 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3091 : 0 : return CONTAINER_OF(netdev->tc, struct fqcodel, tc);
3092 : : }
3093 : :
3094 : : static void
3095 : 233 : fqcodel_install__(struct netdev *netdev_, uint32_t target, uint32_t limit,
3096 : : uint32_t interval, uint32_t flows, uint32_t quantum)
3097 : : {
3098 : 233 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3099 : : struct fqcodel *fqcodel;
3100 : :
3101 : 233 : fqcodel = xmalloc(sizeof *fqcodel);
3102 : 233 : tc_init(&fqcodel->tc, &tc_ops_fqcodel);
3103 : 233 : fqcodel->target = target;
3104 : 233 : fqcodel->limit = limit;
3105 : 233 : fqcodel->interval = interval;
3106 : 233 : fqcodel->flows = flows;
3107 : 233 : fqcodel->quantum = quantum;
3108 : :
3109 : 233 : netdev->tc = &fqcodel->tc;
3110 : 233 : }
3111 : :
3112 : : static int
3113 : 0 : fqcodel_setup_qdisc__(struct netdev *netdev, uint32_t target, uint32_t limit,
3114 : : uint32_t interval, uint32_t flows, uint32_t quantum)
3115 : : {
3116 : : size_t opt_offset;
3117 : : struct ofpbuf request;
3118 : : struct tcmsg *tcmsg;
3119 : : uint32_t otarget, olimit, ointerval, oflows, oquantum;
3120 : : int error;
3121 : :
3122 : 0 : tc_del_qdisc(netdev);
3123 : :
3124 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
3125 : : NLM_F_EXCL | NLM_F_CREATE, &request);
3126 [ # # ]: 0 : if (!tcmsg) {
3127 : 0 : return ENODEV;
3128 : : }
3129 : 0 : tcmsg->tcm_handle = tc_make_handle(1, 0);
3130 : 0 : tcmsg->tcm_parent = TC_H_ROOT;
3131 : :
3132 [ # # ]: 0 : otarget = target ? target : 5000;
3133 [ # # ]: 0 : olimit = limit ? limit : 10240;
3134 [ # # ]: 0 : ointerval = interval ? interval : 100000;
3135 [ # # ]: 0 : oflows = flows ? flows : 1024;
3136 [ # # ]: 0 : oquantum = quantum ? quantum : 1514; /* fq_codel default quantum is 1514
3137 : : not mtu */
3138 : :
3139 : 0 : nl_msg_put_string(&request, TCA_KIND, "fq_codel");
3140 : 0 : opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
3141 : 0 : nl_msg_put_u32(&request, TCA_FQ_CODEL_TARGET, otarget);
3142 : 0 : nl_msg_put_u32(&request, TCA_FQ_CODEL_LIMIT, olimit);
3143 : 0 : nl_msg_put_u32(&request, TCA_FQ_CODEL_INTERVAL, ointerval);
3144 : 0 : nl_msg_put_u32(&request, TCA_FQ_CODEL_FLOWS, oflows);
3145 : 0 : nl_msg_put_u32(&request, TCA_FQ_CODEL_QUANTUM, oquantum);
3146 : 0 : nl_msg_end_nested(&request, opt_offset);
3147 : :
3148 : 0 : error = tc_transact(&request, NULL);
3149 [ # # ]: 0 : if (error) {
3150 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to replace %s qdisc, "
3151 : : "target %u, limit %u, interval %u, flows %u, quantum %u error %d(%s)",
3152 : : netdev_get_name(netdev),
3153 : : otarget, olimit, ointerval, oflows, oquantum,
3154 : : error, ovs_strerror(error));
3155 : : }
3156 : 0 : return error;
3157 : : }
3158 : :
3159 : : static void
3160 : 0 : fqcodel_parse_qdisc_details__(struct netdev *netdev OVS_UNUSED,
3161 : : const struct smap *details, struct fqcodel *fqcodel)
3162 : : {
3163 : 0 : fqcodel->target = smap_get_ullong(details, "target", 0);
3164 : 0 : fqcodel->limit = smap_get_ullong(details, "limit", 0);
3165 : 0 : fqcodel->interval = smap_get_ullong(details, "interval", 0);
3166 : 0 : fqcodel->flows = smap_get_ullong(details, "flows", 0);
3167 : 0 : fqcodel->quantum = smap_get_ullong(details, "quantum", 0);
3168 : :
3169 [ # # ]: 0 : if (!fqcodel->target) {
3170 : 0 : fqcodel->target = 5000;
3171 : : }
3172 [ # # ]: 0 : if (!fqcodel->limit) {
3173 : 0 : fqcodel->limit = 10240;
3174 : : }
3175 [ # # ]: 0 : if (!fqcodel->interval) {
3176 : 0 : fqcodel->interval = 1000000;
3177 : : }
3178 [ # # ]: 0 : if (!fqcodel->flows) {
3179 : 0 : fqcodel->flows = 1024;
3180 : : }
3181 [ # # ]: 0 : if (!fqcodel->quantum) {
3182 : 0 : fqcodel->quantum = 1514;
3183 : : }
3184 : 0 : }
3185 : :
3186 : : static int
3187 : 0 : fqcodel_tc_install(struct netdev *netdev, const struct smap *details)
3188 : : {
3189 : : int error;
3190 : : struct fqcodel fqcodel;
3191 : :
3192 : 0 : fqcodel_parse_qdisc_details__(netdev, details, &fqcodel);
3193 : 0 : error = fqcodel_setup_qdisc__(netdev, fqcodel.target, fqcodel.limit,
3194 : : fqcodel.interval, fqcodel.flows,
3195 : : fqcodel.quantum);
3196 [ # # ]: 0 : if (!error) {
3197 : 0 : fqcodel_install__(netdev, fqcodel.target, fqcodel.limit,
3198 : : fqcodel.interval, fqcodel.flows, fqcodel.quantum);
3199 : : }
3200 : 0 : return error;
3201 : : }
3202 : :
3203 : : static int
3204 : 233 : fqcodel_parse_tca_options__(struct nlattr *nl_options, struct fqcodel *fqcodel)
3205 : : {
3206 : : static const struct nl_policy tca_fqcodel_policy[] = {
3207 : : [TCA_FQ_CODEL_TARGET] = { .type = NL_A_U32 },
3208 : : [TCA_FQ_CODEL_LIMIT] = { .type = NL_A_U32 },
3209 : : [TCA_FQ_CODEL_INTERVAL] = { .type = NL_A_U32 },
3210 : : [TCA_FQ_CODEL_FLOWS] = { .type = NL_A_U32 },
3211 : : [TCA_FQ_CODEL_QUANTUM] = { .type = NL_A_U32 }
3212 : : };
3213 : :
3214 : : struct nlattr *attrs[ARRAY_SIZE(tca_fqcodel_policy)];
3215 : :
3216 [ - + ]: 233 : if (!nl_parse_nested(nl_options, tca_fqcodel_policy,
3217 : : attrs, ARRAY_SIZE(tca_fqcodel_policy))) {
3218 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse FQ_CoDel class options");
3219 : 0 : return EPROTO;
3220 : : }
3221 : :
3222 : 233 : fqcodel->target = nl_attr_get_u32(attrs[TCA_FQ_CODEL_TARGET]);
3223 : 233 : fqcodel->limit = nl_attr_get_u32(attrs[TCA_FQ_CODEL_LIMIT]);
3224 : 233 : fqcodel->interval =nl_attr_get_u32(attrs[TCA_FQ_CODEL_INTERVAL]);
3225 : 233 : fqcodel->flows = nl_attr_get_u32(attrs[TCA_FQ_CODEL_FLOWS]);
3226 : 233 : fqcodel->quantum = nl_attr_get_u32(attrs[TCA_FQ_CODEL_QUANTUM]);
3227 : 233 : return 0;
3228 : : }
3229 : :
3230 : : static int
3231 : 233 : fqcodel_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg)
3232 : : {
3233 : : struct nlattr *nlattr;
3234 : : const char * kind;
3235 : : int error;
3236 : : struct fqcodel fqcodel;
3237 : :
3238 : 233 : error = tc_parse_qdisc(nlmsg, &kind, &nlattr);
3239 [ - + ]: 233 : if (error != 0) {
3240 : 0 : return error;
3241 : : }
3242 : :
3243 : 233 : error = fqcodel_parse_tca_options__(nlattr, &fqcodel);
3244 [ - + ]: 233 : if (error != 0) {
3245 : 0 : return error;
3246 : : }
3247 : :
3248 : 233 : fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval,
3249 : : fqcodel.flows, fqcodel.quantum);
3250 : 233 : return 0;
3251 : : }
3252 : :
3253 : : static void
3254 : 233 : fqcodel_tc_destroy(struct tc *tc)
3255 : : {
3256 : 233 : struct fqcodel *fqcodel = CONTAINER_OF(tc, struct fqcodel, tc);
3257 : 233 : tc_destroy(tc);
3258 : 233 : free(fqcodel);
3259 : 233 : }
3260 : :
3261 : : static int
3262 : 0 : fqcodel_qdisc_get(const struct netdev *netdev, struct smap *details)
3263 : : {
3264 : 0 : const struct fqcodel *fqcodel = fqcodel_get__(netdev);
3265 : 0 : smap_add_format(details, "target", "%u", fqcodel->target);
3266 : 0 : smap_add_format(details, "limit", "%u", fqcodel->limit);
3267 : 0 : smap_add_format(details, "interval", "%u", fqcodel->interval);
3268 : 0 : smap_add_format(details, "flows", "%u", fqcodel->flows);
3269 : 0 : smap_add_format(details, "quantum", "%u", fqcodel->quantum);
3270 : 0 : return 0;
3271 : : }
3272 : :
3273 : : static int
3274 : 0 : fqcodel_qdisc_set(struct netdev *netdev, const struct smap *details)
3275 : : {
3276 : : struct fqcodel fqcodel;
3277 : :
3278 : 0 : fqcodel_parse_qdisc_details__(netdev, details, &fqcodel);
3279 : 0 : fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval,
3280 : : fqcodel.flows, fqcodel.quantum);
3281 : 0 : fqcodel_get__(netdev)->target = fqcodel.target;
3282 : 0 : fqcodel_get__(netdev)->limit = fqcodel.limit;
3283 : 0 : fqcodel_get__(netdev)->interval = fqcodel.interval;
3284 : 0 : fqcodel_get__(netdev)->flows = fqcodel.flows;
3285 : 0 : fqcodel_get__(netdev)->quantum = fqcodel.quantum;
3286 : 0 : return 0;
3287 : : }
3288 : :
3289 : : static const struct tc_ops tc_ops_fqcodel = {
3290 : : "fq_codel", /* linux_name */
3291 : : "linux-fq_codel", /* ovs_name */
3292 : : FQCODEL_N_QUEUES, /* n_queues */
3293 : : fqcodel_tc_install,
3294 : : fqcodel_tc_load,
3295 : : fqcodel_tc_destroy,
3296 : : fqcodel_qdisc_get,
3297 : : fqcodel_qdisc_set,
3298 : : NULL,
3299 : : NULL,
3300 : : NULL,
3301 : : NULL,
3302 : : NULL
3303 : : };
3304 : :
3305 : : /* SFQ traffic control class. */
3306 : :
3307 : : #define SFQ_N_QUEUES 0x0000
3308 : :
3309 : : struct sfq {
3310 : : struct tc tc;
3311 : : uint32_t quantum;
3312 : : uint32_t perturb;
3313 : : };
3314 : :
3315 : : static struct sfq *
3316 : 0 : sfq_get__(const struct netdev *netdev_)
3317 : : {
3318 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3319 : 0 : return CONTAINER_OF(netdev->tc, struct sfq, tc);
3320 : : }
3321 : :
3322 : : static void
3323 : 0 : sfq_install__(struct netdev *netdev_, uint32_t quantum, uint32_t perturb)
3324 : : {
3325 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3326 : : struct sfq *sfq;
3327 : :
3328 : 0 : sfq = xmalloc(sizeof *sfq);
3329 : 0 : tc_init(&sfq->tc, &tc_ops_sfq);
3330 : 0 : sfq->perturb = perturb;
3331 : 0 : sfq->quantum = quantum;
3332 : :
3333 : 0 : netdev->tc = &sfq->tc;
3334 : 0 : }
3335 : :
3336 : : static int
3337 : 0 : sfq_setup_qdisc__(struct netdev *netdev, uint32_t quantum, uint32_t perturb)
3338 : : {
3339 : : struct tc_sfq_qopt opt;
3340 : : struct ofpbuf request;
3341 : : struct tcmsg *tcmsg;
3342 : : int mtu;
3343 : : int mtu_error, error;
3344 : 0 : mtu_error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu);
3345 : :
3346 : 0 : tc_del_qdisc(netdev);
3347 : :
3348 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
3349 : : NLM_F_EXCL | NLM_F_CREATE, &request);
3350 [ # # ]: 0 : if (!tcmsg) {
3351 : 0 : return ENODEV;
3352 : : }
3353 : 0 : tcmsg->tcm_handle = tc_make_handle(1, 0);
3354 : 0 : tcmsg->tcm_parent = TC_H_ROOT;
3355 : :
3356 : 0 : memset(&opt, 0, sizeof opt);
3357 [ # # ]: 0 : if (!quantum) {
3358 [ # # ]: 0 : if (!mtu_error) {
3359 : 0 : opt.quantum = mtu; /* if we cannot find mtu, use default */
3360 : : }
3361 : : } else {
3362 : 0 : opt.quantum = quantum;
3363 : : }
3364 : :
3365 [ # # ]: 0 : if (!perturb) {
3366 : 0 : opt.perturb_period = 10;
3367 : : } else {
3368 : 0 : opt.perturb_period = perturb;
3369 : : }
3370 : :
3371 : 0 : nl_msg_put_string(&request, TCA_KIND, "sfq");
3372 : 0 : nl_msg_put_unspec(&request, TCA_OPTIONS, &opt, sizeof opt);
3373 : :
3374 : 0 : error = tc_transact(&request, NULL);
3375 [ # # ]: 0 : if (error) {
3376 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to replace %s qdisc, "
3377 : : "quantum %u, perturb %u error %d(%s)",
3378 : : netdev_get_name(netdev),
3379 : : opt.quantum, opt.perturb_period,
3380 : : error, ovs_strerror(error));
3381 : : }
3382 : 0 : return error;
3383 : : }
3384 : :
3385 : : static void
3386 : 0 : sfq_parse_qdisc_details__(struct netdev *netdev,
3387 : : const struct smap *details, struct sfq *sfq)
3388 : : {
3389 : 0 : sfq->perturb = smap_get_ullong(details, "perturb", 0);
3390 : 0 : sfq->quantum = smap_get_ullong(details, "quantum", 0);
3391 : :
3392 [ # # ]: 0 : if (!sfq->perturb) {
3393 : 0 : sfq->perturb = 10;
3394 : : }
3395 : :
3396 [ # # ]: 0 : if (!sfq->quantum) {
3397 : : int mtu;
3398 [ # # ]: 0 : if (!netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu)) {
3399 : 0 : sfq->quantum = mtu;
3400 : : } else {
3401 [ # # ]: 0 : VLOG_WARN_RL(&rl, "when using SFQ, you must specify quantum on a "
3402 : : "device without mtu");
3403 : : }
3404 : : }
3405 : 0 : }
3406 : :
3407 : : static int
3408 : 0 : sfq_tc_install(struct netdev *netdev, const struct smap *details)
3409 : : {
3410 : : int error;
3411 : : struct sfq sfq;
3412 : :
3413 : 0 : sfq_parse_qdisc_details__(netdev, details, &sfq);
3414 : 0 : error = sfq_setup_qdisc__(netdev, sfq.quantum, sfq.perturb);
3415 [ # # ]: 0 : if (!error) {
3416 : 0 : sfq_install__(netdev, sfq.quantum, sfq.perturb);
3417 : : }
3418 : 0 : return error;
3419 : : }
3420 : :
3421 : : static int
3422 : 0 : sfq_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg)
3423 : : {
3424 : : const struct tc_sfq_qopt *sfq;
3425 : : struct nlattr *nlattr;
3426 : : const char * kind;
3427 : : int error;
3428 : :
3429 : 0 : error = tc_parse_qdisc(nlmsg, &kind, &nlattr);
3430 [ # # ]: 0 : if (error == 0) {
3431 : 0 : sfq = nl_attr_get(nlattr);
3432 : 0 : sfq_install__(netdev, sfq->perturb_period, sfq->quantum);
3433 : 0 : return 0;
3434 : : }
3435 : :
3436 : 0 : return error;
3437 : : }
3438 : :
3439 : : static void
3440 : 0 : sfq_tc_destroy(struct tc *tc)
3441 : : {
3442 : 0 : struct sfq *sfq = CONTAINER_OF(tc, struct sfq, tc);
3443 : 0 : tc_destroy(tc);
3444 : 0 : free(sfq);
3445 : 0 : }
3446 : :
3447 : : static int
3448 : 0 : sfq_qdisc_get(const struct netdev *netdev, struct smap *details)
3449 : : {
3450 : 0 : const struct sfq *sfq = sfq_get__(netdev);
3451 : 0 : smap_add_format(details, "quantum", "%u", sfq->quantum);
3452 : 0 : smap_add_format(details, "perturb", "%u", sfq->perturb);
3453 : 0 : return 0;
3454 : : }
3455 : :
3456 : : static int
3457 : 0 : sfq_qdisc_set(struct netdev *netdev, const struct smap *details)
3458 : : {
3459 : : struct sfq sfq;
3460 : :
3461 : 0 : sfq_parse_qdisc_details__(netdev, details, &sfq);
3462 : 0 : sfq_install__(netdev, sfq.quantum, sfq.perturb);
3463 : 0 : sfq_get__(netdev)->quantum = sfq.quantum;
3464 : 0 : sfq_get__(netdev)->perturb = sfq.perturb;
3465 : 0 : return 0;
3466 : : }
3467 : :
3468 : : static const struct tc_ops tc_ops_sfq = {
3469 : : "sfq", /* linux_name */
3470 : : "linux-sfq", /* ovs_name */
3471 : : SFQ_N_QUEUES, /* n_queues */
3472 : : sfq_tc_install,
3473 : : sfq_tc_load,
3474 : : sfq_tc_destroy,
3475 : : sfq_qdisc_get,
3476 : : sfq_qdisc_set,
3477 : : NULL,
3478 : : NULL,
3479 : : NULL,
3480 : : NULL,
3481 : : NULL
3482 : : };
3483 : :
3484 : : /* HTB traffic control class. */
3485 : :
3486 : : #define HTB_N_QUEUES 0xf000
3487 : : #define HTB_RATE2QUANTUM 10
3488 : :
3489 : : struct htb {
3490 : : struct tc tc;
3491 : : unsigned int max_rate; /* In bytes/s. */
3492 : : };
3493 : :
3494 : : struct htb_class {
3495 : : struct tc_queue tc_queue;
3496 : : unsigned int min_rate; /* In bytes/s. */
3497 : : unsigned int max_rate; /* In bytes/s. */
3498 : : unsigned int burst; /* In bytes. */
3499 : : unsigned int priority; /* Lower values are higher priorities. */
3500 : : };
3501 : :
3502 : : static struct htb *
3503 : 0 : htb_get__(const struct netdev *netdev_)
3504 : : {
3505 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3506 : 0 : return CONTAINER_OF(netdev->tc, struct htb, tc);
3507 : : }
3508 : :
3509 : : static void
3510 : 0 : htb_install__(struct netdev *netdev_, uint64_t max_rate)
3511 : : {
3512 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3513 : : struct htb *htb;
3514 : :
3515 : 0 : htb = xmalloc(sizeof *htb);
3516 : 0 : tc_init(&htb->tc, &tc_ops_htb);
3517 : 0 : htb->max_rate = max_rate;
3518 : :
3519 : 0 : netdev->tc = &htb->tc;
3520 : 0 : }
3521 : :
3522 : : /* Create an HTB qdisc.
3523 : : *
3524 : : * Equivalent to "tc qdisc add dev <dev> root handle 1: htb default 1". */
3525 : : static int
3526 : 0 : htb_setup_qdisc__(struct netdev *netdev)
3527 : : {
3528 : : size_t opt_offset;
3529 : : struct tc_htb_glob opt;
3530 : : struct ofpbuf request;
3531 : : struct tcmsg *tcmsg;
3532 : :
3533 : 0 : tc_del_qdisc(netdev);
3534 : :
3535 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
3536 : : NLM_F_EXCL | NLM_F_CREATE, &request);
3537 [ # # ]: 0 : if (!tcmsg) {
3538 : 0 : return ENODEV;
3539 : : }
3540 : 0 : tcmsg->tcm_handle = tc_make_handle(1, 0);
3541 : 0 : tcmsg->tcm_parent = TC_H_ROOT;
3542 : :
3543 : 0 : nl_msg_put_string(&request, TCA_KIND, "htb");
3544 : :
3545 : 0 : memset(&opt, 0, sizeof opt);
3546 : 0 : opt.rate2quantum = HTB_RATE2QUANTUM;
3547 : 0 : opt.version = 3;
3548 : 0 : opt.defcls = 1;
3549 : :
3550 : 0 : opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
3551 : 0 : nl_msg_put_unspec(&request, TCA_HTB_INIT, &opt, sizeof opt);
3552 : 0 : nl_msg_end_nested(&request, opt_offset);
3553 : :
3554 : 0 : return tc_transact(&request, NULL);
3555 : : }
3556 : :
3557 : : /* Equivalent to "tc class replace <dev> classid <handle> parent <parent> htb
3558 : : * rate <min_rate>bps ceil <max_rate>bps burst <burst>b prio <priority>". */
3559 : : static int
3560 : 0 : htb_setup_class__(struct netdev *netdev, unsigned int handle,
3561 : : unsigned int parent, struct htb_class *class)
3562 : : {
3563 : : size_t opt_offset;
3564 : : struct tc_htb_opt opt;
3565 : : struct ofpbuf request;
3566 : : struct tcmsg *tcmsg;
3567 : : int error;
3568 : : int mtu;
3569 : :
3570 : 0 : error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu);
3571 [ # # ]: 0 : if (error) {
3572 [ # # ]: 0 : VLOG_WARN_RL(&rl, "cannot set up HTB on device %s that lacks MTU",
3573 : : netdev_get_name(netdev));
3574 : 0 : return error;
3575 : : }
3576 : :
3577 : 0 : memset(&opt, 0, sizeof opt);
3578 : 0 : tc_fill_rate(&opt.rate, class->min_rate, mtu);
3579 : 0 : tc_fill_rate(&opt.ceil, class->max_rate, mtu);
3580 : : /* Makes sure the quantum is at least MTU. Setting quantum will
3581 : : * make htb ignore the r2q for this class. */
3582 [ # # ]: 0 : if ((class->min_rate / HTB_RATE2QUANTUM) < mtu) {
3583 : 0 : opt.quantum = mtu;
3584 : : }
3585 : 0 : opt.buffer = tc_calc_buffer(opt.rate.rate, mtu, class->burst);
3586 : 0 : opt.cbuffer = tc_calc_buffer(opt.ceil.rate, mtu, class->burst);
3587 : 0 : opt.prio = class->priority;
3588 : :
3589 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWTCLASS, NLM_F_CREATE, &request);
3590 [ # # ]: 0 : if (!tcmsg) {
3591 : 0 : return ENODEV;
3592 : : }
3593 : 0 : tcmsg->tcm_handle = handle;
3594 : 0 : tcmsg->tcm_parent = parent;
3595 : :
3596 : 0 : nl_msg_put_string(&request, TCA_KIND, "htb");
3597 : 0 : opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
3598 : 0 : nl_msg_put_unspec(&request, TCA_HTB_PARMS, &opt, sizeof opt);
3599 : 0 : tc_put_rtab(&request, TCA_HTB_RTAB, &opt.rate);
3600 : 0 : tc_put_rtab(&request, TCA_HTB_CTAB, &opt.ceil);
3601 : 0 : nl_msg_end_nested(&request, opt_offset);
3602 : :
3603 : 0 : error = tc_transact(&request, NULL);
3604 [ # # ]: 0 : if (error) {
3605 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to replace %s class %u:%u, parent %u:%u, "
3606 : : "min_rate=%u max_rate=%u burst=%u prio=%u (%s)",
3607 : : netdev_get_name(netdev),
3608 : : tc_get_major(handle), tc_get_minor(handle),
3609 : : tc_get_major(parent), tc_get_minor(parent),
3610 : : class->min_rate, class->max_rate,
3611 : : class->burst, class->priority, ovs_strerror(error));
3612 : : }
3613 : 0 : return error;
3614 : : }
3615 : :
3616 : : /* Parses Netlink attributes in 'options' for HTB parameters and stores a
3617 : : * description of them into 'details'. The description complies with the
3618 : : * specification given in the vswitch database documentation for linux-htb
3619 : : * queue details. */
3620 : : static int
3621 : 0 : htb_parse_tca_options__(struct nlattr *nl_options, struct htb_class *class)
3622 : : {
3623 : : static const struct nl_policy tca_htb_policy[] = {
3624 : : [TCA_HTB_PARMS] = { .type = NL_A_UNSPEC, .optional = false,
3625 : : .min_len = sizeof(struct tc_htb_opt) },
3626 : : };
3627 : :
3628 : : struct nlattr *attrs[ARRAY_SIZE(tca_htb_policy)];
3629 : : const struct tc_htb_opt *htb;
3630 : :
3631 [ # # ]: 0 : if (!nl_parse_nested(nl_options, tca_htb_policy,
3632 : : attrs, ARRAY_SIZE(tca_htb_policy))) {
3633 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse HTB class options");
3634 : 0 : return EPROTO;
3635 : : }
3636 : :
3637 : 0 : htb = nl_attr_get(attrs[TCA_HTB_PARMS]);
3638 : 0 : class->min_rate = htb->rate.rate;
3639 : 0 : class->max_rate = htb->ceil.rate;
3640 : 0 : class->burst = tc_ticks_to_bytes(htb->rate.rate, htb->buffer);
3641 : 0 : class->priority = htb->prio;
3642 : 0 : return 0;
3643 : : }
3644 : :
3645 : : static int
3646 : 0 : htb_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id,
3647 : : struct htb_class *options,
3648 : : struct netdev_queue_stats *stats)
3649 : : {
3650 : : struct nlattr *nl_options;
3651 : : unsigned int handle;
3652 : : int error;
3653 : :
3654 : 0 : error = tc_parse_class(tcmsg, &handle, &nl_options, stats);
3655 [ # # ][ # # ]: 0 : if (!error && queue_id) {
3656 : 0 : unsigned int major = tc_get_major(handle);
3657 : 0 : unsigned int minor = tc_get_minor(handle);
3658 [ # # ][ # # ]: 0 : if (major == 1 && minor > 0 && minor <= HTB_N_QUEUES) {
[ # # ]
3659 : 0 : *queue_id = minor - 1;
3660 : : } else {
3661 : 0 : error = EPROTO;
3662 : : }
3663 : : }
3664 [ # # ][ # # ]: 0 : if (!error && options) {
3665 : 0 : error = htb_parse_tca_options__(nl_options, options);
3666 : : }
3667 : 0 : return error;
3668 : : }
3669 : :
3670 : : static void
3671 : 0 : htb_parse_qdisc_details__(struct netdev *netdev_,
3672 : : const struct smap *details, struct htb_class *hc)
3673 : : {
3674 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3675 : :
3676 : 0 : hc->max_rate = smap_get_ullong(details, "max-rate", 0) / 8;
3677 [ # # ]: 0 : if (!hc->max_rate) {
3678 : : enum netdev_features current;
3679 : :
3680 : 0 : netdev_linux_read_features(netdev);
3681 [ # # ]: 0 : current = !netdev->get_features_error ? netdev->current : 0;
3682 : 0 : hc->max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8;
3683 : : }
3684 : 0 : hc->min_rate = hc->max_rate;
3685 : 0 : hc->burst = 0;
3686 : 0 : hc->priority = 0;
3687 : 0 : }
3688 : :
3689 : : static int
3690 : 0 : htb_parse_class_details__(struct netdev *netdev,
3691 : : const struct smap *details, struct htb_class *hc)
3692 : : {
3693 : 0 : const struct htb *htb = htb_get__(netdev);
3694 : : int mtu, error;
3695 : :
3696 : 0 : error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu);
3697 [ # # ]: 0 : if (error) {
3698 [ # # ]: 0 : VLOG_WARN_RL(&rl, "cannot parse HTB class on device %s that lacks MTU",
3699 : : netdev_get_name(netdev));
3700 : 0 : return error;
3701 : : }
3702 : :
3703 : : /* HTB requires at least an mtu sized min-rate to send any traffic even
3704 : : * on uncongested links. */
3705 : 0 : hc->min_rate = smap_get_ullong(details, "min-rate", 0) / 8;
3706 : 0 : hc->min_rate = MAX(hc->min_rate, mtu);
3707 : 0 : hc->min_rate = MIN(hc->min_rate, htb->max_rate);
3708 : :
3709 : : /* max-rate */
3710 : 0 : hc->max_rate = smap_get_ullong(details, "max-rate", 0) / 8;
3711 [ # # ]: 0 : if (!hc->max_rate) {
3712 : 0 : hc->max_rate = htb->max_rate;
3713 : : }
3714 : 0 : hc->max_rate = MAX(hc->max_rate, hc->min_rate);
3715 : 0 : hc->max_rate = MIN(hc->max_rate, htb->max_rate);
3716 : :
3717 : : /* burst
3718 : : *
3719 : : * According to hints in the documentation that I've read, it is important
3720 : : * that 'burst' be at least as big as the largest frame that might be
3721 : : * transmitted. Also, making 'burst' a bit bigger than necessary is OK,
3722 : : * but having it a bit too small is a problem. Since netdev_get_mtu()
3723 : : * doesn't include the Ethernet header, we need to add at least 14 (18?) to
3724 : : * the MTU. We actually add 64, instead of 14, as a guard against
3725 : : * additional headers get tacked on somewhere that we're not aware of. */
3726 : 0 : hc->burst = smap_get_ullong(details, "burst", 0) / 8;
3727 : 0 : hc->burst = MAX(hc->burst, mtu + 64);
3728 : :
3729 : : /* priority */
3730 : 0 : hc->priority = smap_get_ullong(details, "priority", 0);
3731 : :
3732 : 0 : return 0;
3733 : : }
3734 : :
3735 : : static int
3736 : 0 : htb_query_class__(const struct netdev *netdev, unsigned int handle,
3737 : : unsigned int parent, struct htb_class *options,
3738 : : struct netdev_queue_stats *stats)
3739 : : {
3740 : : struct ofpbuf *reply;
3741 : : int error;
3742 : :
3743 : 0 : error = tc_query_class(netdev, handle, parent, &reply);
3744 [ # # ]: 0 : if (!error) {
3745 : 0 : error = htb_parse_tcmsg__(reply, NULL, options, stats);
3746 : 0 : ofpbuf_delete(reply);
3747 : : }
3748 : 0 : return error;
3749 : : }
3750 : :
3751 : : static int
3752 : 0 : htb_tc_install(struct netdev *netdev, const struct smap *details)
3753 : : {
3754 : : int error;
3755 : :
3756 : 0 : error = htb_setup_qdisc__(netdev);
3757 [ # # ]: 0 : if (!error) {
3758 : : struct htb_class hc;
3759 : :
3760 : 0 : htb_parse_qdisc_details__(netdev, details, &hc);
3761 : 0 : error = htb_setup_class__(netdev, tc_make_handle(1, 0xfffe),
3762 : : tc_make_handle(1, 0), &hc);
3763 [ # # ]: 0 : if (!error) {
3764 : 0 : htb_install__(netdev, hc.max_rate);
3765 : : }
3766 : : }
3767 : 0 : return error;
3768 : : }
3769 : :
3770 : : static struct htb_class *
3771 : 0 : htb_class_cast__(const struct tc_queue *queue)
3772 : : {
3773 : 0 : return CONTAINER_OF(queue, struct htb_class, tc_queue);
3774 : : }
3775 : :
3776 : : static void
3777 : 0 : htb_update_queue__(struct netdev *netdev, unsigned int queue_id,
3778 : : const struct htb_class *hc)
3779 : : {
3780 : 0 : struct htb *htb = htb_get__(netdev);
3781 : 0 : size_t hash = hash_int(queue_id, 0);
3782 : : struct tc_queue *queue;
3783 : : struct htb_class *hcp;
3784 : :
3785 : 0 : queue = tc_find_queue__(netdev, queue_id, hash);
3786 [ # # ]: 0 : if (queue) {
3787 : 0 : hcp = htb_class_cast__(queue);
3788 : : } else {
3789 : 0 : hcp = xmalloc(sizeof *hcp);
3790 : 0 : queue = &hcp->tc_queue;
3791 : 0 : queue->queue_id = queue_id;
3792 : 0 : queue->created = time_msec();
3793 : 0 : hmap_insert(&htb->tc.queues, &queue->hmap_node, hash);
3794 : : }
3795 : :
3796 : 0 : hcp->min_rate = hc->min_rate;
3797 : 0 : hcp->max_rate = hc->max_rate;
3798 : 0 : hcp->burst = hc->burst;
3799 : 0 : hcp->priority = hc->priority;
3800 : 0 : }
3801 : :
3802 : : static int
3803 : 0 : htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
3804 : : {
3805 : : struct ofpbuf msg;
3806 : : struct queue_dump_state state;
3807 : : struct htb_class hc;
3808 : :
3809 : : /* Get qdisc options. */
3810 : 0 : hc.max_rate = 0;
3811 : 0 : htb_query_class__(netdev, tc_make_handle(1, 0xfffe), 0, &hc, NULL);
3812 : 0 : htb_install__(netdev, hc.max_rate);
3813 : :
3814 : : /* Get queues. */
3815 [ # # ]: 0 : if (!start_queue_dump(netdev, &state)) {
3816 : 0 : return ENODEV;
3817 : : }
3818 [ # # ]: 0 : while (nl_dump_next(&state.dump, &msg, &state.buf)) {
3819 : : unsigned int queue_id;
3820 : :
3821 [ # # ]: 0 : if (!htb_parse_tcmsg__(&msg, &queue_id, &hc, NULL)) {
3822 : 0 : htb_update_queue__(netdev, queue_id, &hc);
3823 : : }
3824 : : }
3825 : 0 : finish_queue_dump(&state);
3826 : :
3827 : 0 : return 0;
3828 : : }
3829 : :
3830 : : static void
3831 : 0 : htb_tc_destroy(struct tc *tc)
3832 : : {
3833 : 0 : struct htb *htb = CONTAINER_OF(tc, struct htb, tc);
3834 : : struct htb_class *hc;
3835 : :
3836 [ # # ][ # # ]: 0 : HMAP_FOR_EACH_POP (hc, tc_queue.hmap_node, &htb->tc.queues) {
[ # # ]
3837 : 0 : free(hc);
3838 : : }
3839 : 0 : tc_destroy(tc);
3840 : 0 : free(htb);
3841 : 0 : }
3842 : :
3843 : : static int
3844 : 0 : htb_qdisc_get(const struct netdev *netdev, struct smap *details)
3845 : : {
3846 : 0 : const struct htb *htb = htb_get__(netdev);
3847 : 0 : smap_add_format(details, "max-rate", "%llu", 8ULL * htb->max_rate);
3848 : 0 : return 0;
3849 : : }
3850 : :
3851 : : static int
3852 : 0 : htb_qdisc_set(struct netdev *netdev, const struct smap *details)
3853 : : {
3854 : : struct htb_class hc;
3855 : : int error;
3856 : :
3857 : 0 : htb_parse_qdisc_details__(netdev, details, &hc);
3858 : 0 : error = htb_setup_class__(netdev, tc_make_handle(1, 0xfffe),
3859 : : tc_make_handle(1, 0), &hc);
3860 [ # # ]: 0 : if (!error) {
3861 : 0 : htb_get__(netdev)->max_rate = hc.max_rate;
3862 : : }
3863 : 0 : return error;
3864 : : }
3865 : :
3866 : : static int
3867 : 0 : htb_class_get(const struct netdev *netdev OVS_UNUSED,
3868 : : const struct tc_queue *queue, struct smap *details)
3869 : : {
3870 : 0 : const struct htb_class *hc = htb_class_cast__(queue);
3871 : :
3872 : 0 : smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate);
3873 [ # # ]: 0 : if (hc->min_rate != hc->max_rate) {
3874 : 0 : smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate);
3875 : : }
3876 : 0 : smap_add_format(details, "burst", "%llu", 8ULL * hc->burst);
3877 [ # # ]: 0 : if (hc->priority) {
3878 : 0 : smap_add_format(details, "priority", "%u", hc->priority);
3879 : : }
3880 : 0 : return 0;
3881 : : }
3882 : :
3883 : : static int
3884 : 0 : htb_class_set(struct netdev *netdev, unsigned int queue_id,
3885 : : const struct smap *details)
3886 : : {
3887 : : struct htb_class hc;
3888 : : int error;
3889 : :
3890 : 0 : error = htb_parse_class_details__(netdev, details, &hc);
3891 [ # # ]: 0 : if (error) {
3892 : 0 : return error;
3893 : : }
3894 : :
3895 : 0 : error = htb_setup_class__(netdev, tc_make_handle(1, queue_id + 1),
3896 : : tc_make_handle(1, 0xfffe), &hc);
3897 [ # # ]: 0 : if (error) {
3898 : 0 : return error;
3899 : : }
3900 : :
3901 : 0 : htb_update_queue__(netdev, queue_id, &hc);
3902 : 0 : return 0;
3903 : : }
3904 : :
3905 : : static int
3906 : 0 : htb_class_delete(struct netdev *netdev, struct tc_queue *queue)
3907 : : {
3908 : 0 : struct htb_class *hc = htb_class_cast__(queue);
3909 : 0 : struct htb *htb = htb_get__(netdev);
3910 : : int error;
3911 : :
3912 : 0 : error = tc_delete_class(netdev, tc_make_handle(1, queue->queue_id + 1));
3913 [ # # ]: 0 : if (!error) {
3914 : 0 : hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node);
3915 : 0 : free(hc);
3916 : : }
3917 : 0 : return error;
3918 : : }
3919 : :
3920 : : static int
3921 : 0 : htb_class_get_stats(const struct netdev *netdev, const struct tc_queue *queue,
3922 : : struct netdev_queue_stats *stats)
3923 : : {
3924 : 0 : return htb_query_class__(netdev, tc_make_handle(1, queue->queue_id + 1),
3925 : : tc_make_handle(1, 0xfffe), NULL, stats);
3926 : : }
3927 : :
3928 : : static int
3929 : 0 : htb_class_dump_stats(const struct netdev *netdev OVS_UNUSED,
3930 : : const struct ofpbuf *nlmsg,
3931 : : netdev_dump_queue_stats_cb *cb, void *aux)
3932 : : {
3933 : : struct netdev_queue_stats stats;
3934 : : unsigned int handle, major, minor;
3935 : : int error;
3936 : :
3937 : 0 : error = tc_parse_class(nlmsg, &handle, NULL, &stats);
3938 [ # # ]: 0 : if (error) {
3939 : 0 : return error;
3940 : : }
3941 : :
3942 : 0 : major = tc_get_major(handle);
3943 : 0 : minor = tc_get_minor(handle);
3944 [ # # ][ # # ]: 0 : if (major == 1 && minor > 0 && minor <= HTB_N_QUEUES) {
[ # # ]
3945 : 0 : (*cb)(minor - 1, &stats, aux);
3946 : : }
3947 : 0 : return 0;
3948 : : }
3949 : :
3950 : : static const struct tc_ops tc_ops_htb = {
3951 : : "htb", /* linux_name */
3952 : : "linux-htb", /* ovs_name */
3953 : : HTB_N_QUEUES, /* n_queues */
3954 : : htb_tc_install,
3955 : : htb_tc_load,
3956 : : htb_tc_destroy,
3957 : : htb_qdisc_get,
3958 : : htb_qdisc_set,
3959 : : htb_class_get,
3960 : : htb_class_set,
3961 : : htb_class_delete,
3962 : : htb_class_get_stats,
3963 : : htb_class_dump_stats
3964 : : };
3965 : :
3966 : : /* "linux-hfsc" traffic control class. */
3967 : :
3968 : : #define HFSC_N_QUEUES 0xf000
3969 : :
3970 : : struct hfsc {
3971 : : struct tc tc;
3972 : : uint32_t max_rate;
3973 : : };
3974 : :
3975 : : struct hfsc_class {
3976 : : struct tc_queue tc_queue;
3977 : : uint32_t min_rate;
3978 : : uint32_t max_rate;
3979 : : };
3980 : :
3981 : : static struct hfsc *
3982 : 0 : hfsc_get__(const struct netdev *netdev_)
3983 : : {
3984 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3985 : 0 : return CONTAINER_OF(netdev->tc, struct hfsc, tc);
3986 : : }
3987 : :
3988 : : static struct hfsc_class *
3989 : 0 : hfsc_class_cast__(const struct tc_queue *queue)
3990 : : {
3991 : 0 : return CONTAINER_OF(queue, struct hfsc_class, tc_queue);
3992 : : }
3993 : :
3994 : : static void
3995 : 0 : hfsc_install__(struct netdev *netdev_, uint32_t max_rate)
3996 : : {
3997 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
3998 : : struct hfsc *hfsc;
3999 : :
4000 : 0 : hfsc = xmalloc(sizeof *hfsc);
4001 : 0 : tc_init(&hfsc->tc, &tc_ops_hfsc);
4002 : 0 : hfsc->max_rate = max_rate;
4003 : 0 : netdev->tc = &hfsc->tc;
4004 : 0 : }
4005 : :
4006 : : static void
4007 : 0 : hfsc_update_queue__(struct netdev *netdev, unsigned int queue_id,
4008 : : const struct hfsc_class *hc)
4009 : : {
4010 : : size_t hash;
4011 : : struct hfsc *hfsc;
4012 : : struct hfsc_class *hcp;
4013 : : struct tc_queue *queue;
4014 : :
4015 : 0 : hfsc = hfsc_get__(netdev);
4016 : 0 : hash = hash_int(queue_id, 0);
4017 : :
4018 : 0 : queue = tc_find_queue__(netdev, queue_id, hash);
4019 [ # # ]: 0 : if (queue) {
4020 : 0 : hcp = hfsc_class_cast__(queue);
4021 : : } else {
4022 : 0 : hcp = xmalloc(sizeof *hcp);
4023 : 0 : queue = &hcp->tc_queue;
4024 : 0 : queue->queue_id = queue_id;
4025 : 0 : queue->created = time_msec();
4026 : 0 : hmap_insert(&hfsc->tc.queues, &queue->hmap_node, hash);
4027 : : }
4028 : :
4029 : 0 : hcp->min_rate = hc->min_rate;
4030 : 0 : hcp->max_rate = hc->max_rate;
4031 : 0 : }
4032 : :
4033 : : static int
4034 : 0 : hfsc_parse_tca_options__(struct nlattr *nl_options, struct hfsc_class *class)
4035 : : {
4036 : : const struct tc_service_curve *rsc, *fsc, *usc;
4037 : : static const struct nl_policy tca_hfsc_policy[] = {
4038 : : [TCA_HFSC_RSC] = {
4039 : : .type = NL_A_UNSPEC,
4040 : : .optional = false,
4041 : : .min_len = sizeof(struct tc_service_curve),
4042 : : },
4043 : : [TCA_HFSC_FSC] = {
4044 : : .type = NL_A_UNSPEC,
4045 : : .optional = false,
4046 : : .min_len = sizeof(struct tc_service_curve),
4047 : : },
4048 : : [TCA_HFSC_USC] = {
4049 : : .type = NL_A_UNSPEC,
4050 : : .optional = false,
4051 : : .min_len = sizeof(struct tc_service_curve),
4052 : : },
4053 : : };
4054 : : struct nlattr *attrs[ARRAY_SIZE(tca_hfsc_policy)];
4055 : :
4056 [ # # ]: 0 : if (!nl_parse_nested(nl_options, tca_hfsc_policy,
4057 : : attrs, ARRAY_SIZE(tca_hfsc_policy))) {
4058 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse HFSC class options");
4059 : 0 : return EPROTO;
4060 : : }
4061 : :
4062 : 0 : rsc = nl_attr_get(attrs[TCA_HFSC_RSC]);
4063 : 0 : fsc = nl_attr_get(attrs[TCA_HFSC_FSC]);
4064 : 0 : usc = nl_attr_get(attrs[TCA_HFSC_USC]);
4065 : :
4066 [ # # ][ # # ]: 0 : if (rsc->m1 != 0 || rsc->d != 0 ||
[ # # ]
4067 [ # # ][ # # ]: 0 : fsc->m1 != 0 || fsc->d != 0 ||
4068 [ # # ]: 0 : usc->m1 != 0 || usc->d != 0) {
4069 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse HFSC class options. "
4070 : : "Non-linear service curves are not supported.");
4071 : 0 : return EPROTO;
4072 : : }
4073 : :
4074 [ # # ]: 0 : if (rsc->m2 != fsc->m2) {
4075 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse HFSC class options. "
4076 : : "Real-time service curves are not supported ");
4077 : 0 : return EPROTO;
4078 : : }
4079 : :
4080 [ # # ]: 0 : if (rsc->m2 > usc->m2) {
4081 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse HFSC class options. "
4082 : : "Min-rate service curve is greater than "
4083 : : "the max-rate service curve.");
4084 : 0 : return EPROTO;
4085 : : }
4086 : :
4087 : 0 : class->min_rate = fsc->m2;
4088 : 0 : class->max_rate = usc->m2;
4089 : 0 : return 0;
4090 : : }
4091 : :
4092 : : static int
4093 : 0 : hfsc_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id,
4094 : : struct hfsc_class *options,
4095 : : struct netdev_queue_stats *stats)
4096 : : {
4097 : : int error;
4098 : : unsigned int handle;
4099 : : struct nlattr *nl_options;
4100 : :
4101 : 0 : error = tc_parse_class(tcmsg, &handle, &nl_options, stats);
4102 [ # # ]: 0 : if (error) {
4103 : 0 : return error;
4104 : : }
4105 : :
4106 [ # # ]: 0 : if (queue_id) {
4107 : : unsigned int major, minor;
4108 : :
4109 : 0 : major = tc_get_major(handle);
4110 : 0 : minor = tc_get_minor(handle);
4111 [ # # ][ # # ]: 0 : if (major == 1 && minor > 0 && minor <= HFSC_N_QUEUES) {
[ # # ]
4112 : 0 : *queue_id = minor - 1;
4113 : : } else {
4114 : 0 : return EPROTO;
4115 : : }
4116 : : }
4117 : :
4118 [ # # ]: 0 : if (options) {
4119 : 0 : error = hfsc_parse_tca_options__(nl_options, options);
4120 : : }
4121 : :
4122 : 0 : return error;
4123 : : }
4124 : :
4125 : : static int
4126 : 0 : hfsc_query_class__(const struct netdev *netdev, unsigned int handle,
4127 : : unsigned int parent, struct hfsc_class *options,
4128 : : struct netdev_queue_stats *stats)
4129 : : {
4130 : : int error;
4131 : : struct ofpbuf *reply;
4132 : :
4133 : 0 : error = tc_query_class(netdev, handle, parent, &reply);
4134 [ # # ]: 0 : if (error) {
4135 : 0 : return error;
4136 : : }
4137 : :
4138 : 0 : error = hfsc_parse_tcmsg__(reply, NULL, options, stats);
4139 : 0 : ofpbuf_delete(reply);
4140 : 0 : return error;
4141 : : }
4142 : :
4143 : : static void
4144 : 0 : hfsc_parse_qdisc_details__(struct netdev *netdev_, const struct smap *details,
4145 : : struct hfsc_class *class)
4146 : : {
4147 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
4148 : :
4149 : 0 : uint32_t max_rate = smap_get_ullong(details, "max-rate", 0) / 8;
4150 [ # # ]: 0 : if (!max_rate) {
4151 : : enum netdev_features current;
4152 : :
4153 : 0 : netdev_linux_read_features(netdev);
4154 [ # # ]: 0 : current = !netdev->get_features_error ? netdev->current : 0;
4155 : 0 : max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8;
4156 : : }
4157 : :
4158 : 0 : class->min_rate = max_rate;
4159 : 0 : class->max_rate = max_rate;
4160 : 0 : }
4161 : :
4162 : : static int
4163 : 0 : hfsc_parse_class_details__(struct netdev *netdev,
4164 : : const struct smap *details,
4165 : : struct hfsc_class * class)
4166 : : {
4167 : : const struct hfsc *hfsc;
4168 : : uint32_t min_rate, max_rate;
4169 : :
4170 : 0 : hfsc = hfsc_get__(netdev);
4171 : :
4172 : 0 : min_rate = smap_get_ullong(details, "min-rate", 0) / 8;
4173 : 0 : min_rate = MAX(min_rate, 1);
4174 : 0 : min_rate = MIN(min_rate, hfsc->max_rate);
4175 : :
4176 : 0 : max_rate = smap_get_ullong(details, "max-rate", hfsc->max_rate * 8) / 8;
4177 : 0 : max_rate = MAX(max_rate, min_rate);
4178 : 0 : max_rate = MIN(max_rate, hfsc->max_rate);
4179 : :
4180 : 0 : class->min_rate = min_rate;
4181 : 0 : class->max_rate = max_rate;
4182 : :
4183 : 0 : return 0;
4184 : : }
4185 : :
4186 : : /* Create an HFSC qdisc.
4187 : : *
4188 : : * Equivalent to "tc qdisc add dev <dev> root handle 1: hfsc default 1". */
4189 : : static int
4190 : 0 : hfsc_setup_qdisc__(struct netdev * netdev)
4191 : : {
4192 : : struct tcmsg *tcmsg;
4193 : : struct ofpbuf request;
4194 : : struct tc_hfsc_qopt opt;
4195 : :
4196 : 0 : tc_del_qdisc(netdev);
4197 : :
4198 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
4199 : : NLM_F_EXCL | NLM_F_CREATE, &request);
4200 : :
4201 [ # # ]: 0 : if (!tcmsg) {
4202 : 0 : return ENODEV;
4203 : : }
4204 : :
4205 : 0 : tcmsg->tcm_handle = tc_make_handle(1, 0);
4206 : 0 : tcmsg->tcm_parent = TC_H_ROOT;
4207 : :
4208 : 0 : memset(&opt, 0, sizeof opt);
4209 : 0 : opt.defcls = 1;
4210 : :
4211 : 0 : nl_msg_put_string(&request, TCA_KIND, "hfsc");
4212 : 0 : nl_msg_put_unspec(&request, TCA_OPTIONS, &opt, sizeof opt);
4213 : :
4214 : 0 : return tc_transact(&request, NULL);
4215 : : }
4216 : :
4217 : : /* Create an HFSC class.
4218 : : *
4219 : : * Equivalent to "tc class add <dev> parent <parent> classid <handle> hfsc
4220 : : * sc rate <min_rate> ul rate <max_rate>" */
4221 : : static int
4222 : 0 : hfsc_setup_class__(struct netdev *netdev, unsigned int handle,
4223 : : unsigned int parent, struct hfsc_class *class)
4224 : : {
4225 : : int error;
4226 : : size_t opt_offset;
4227 : : struct tcmsg *tcmsg;
4228 : : struct ofpbuf request;
4229 : : struct tc_service_curve min, max;
4230 : :
4231 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWTCLASS, NLM_F_CREATE, &request);
4232 : :
4233 [ # # ]: 0 : if (!tcmsg) {
4234 : 0 : return ENODEV;
4235 : : }
4236 : :
4237 : 0 : tcmsg->tcm_handle = handle;
4238 : 0 : tcmsg->tcm_parent = parent;
4239 : :
4240 : 0 : min.m1 = 0;
4241 : 0 : min.d = 0;
4242 : 0 : min.m2 = class->min_rate;
4243 : :
4244 : 0 : max.m1 = 0;
4245 : 0 : max.d = 0;
4246 : 0 : max.m2 = class->max_rate;
4247 : :
4248 : 0 : nl_msg_put_string(&request, TCA_KIND, "hfsc");
4249 : 0 : opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
4250 : 0 : nl_msg_put_unspec(&request, TCA_HFSC_RSC, &min, sizeof min);
4251 : 0 : nl_msg_put_unspec(&request, TCA_HFSC_FSC, &min, sizeof min);
4252 : 0 : nl_msg_put_unspec(&request, TCA_HFSC_USC, &max, sizeof max);
4253 : 0 : nl_msg_end_nested(&request, opt_offset);
4254 : :
4255 : 0 : error = tc_transact(&request, NULL);
4256 [ # # ]: 0 : if (error) {
4257 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to replace %s class %u:%u, parent %u:%u, "
4258 : : "min-rate %ubps, max-rate %ubps (%s)",
4259 : : netdev_get_name(netdev),
4260 : : tc_get_major(handle), tc_get_minor(handle),
4261 : : tc_get_major(parent), tc_get_minor(parent),
4262 : : class->min_rate, class->max_rate, ovs_strerror(error));
4263 : : }
4264 : :
4265 : 0 : return error;
4266 : : }
4267 : :
4268 : : static int
4269 : 0 : hfsc_tc_install(struct netdev *netdev, const struct smap *details)
4270 : : {
4271 : : int error;
4272 : : struct hfsc_class class;
4273 : :
4274 : 0 : error = hfsc_setup_qdisc__(netdev);
4275 : :
4276 [ # # ]: 0 : if (error) {
4277 : 0 : return error;
4278 : : }
4279 : :
4280 : 0 : hfsc_parse_qdisc_details__(netdev, details, &class);
4281 : 0 : error = hfsc_setup_class__(netdev, tc_make_handle(1, 0xfffe),
4282 : : tc_make_handle(1, 0), &class);
4283 : :
4284 [ # # ]: 0 : if (error) {
4285 : 0 : return error;
4286 : : }
4287 : :
4288 : 0 : hfsc_install__(netdev, class.max_rate);
4289 : 0 : return 0;
4290 : : }
4291 : :
4292 : : static int
4293 : 0 : hfsc_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
4294 : : {
4295 : : struct ofpbuf msg;
4296 : : struct queue_dump_state state;
4297 : : struct hfsc_class hc;
4298 : :
4299 : 0 : hc.max_rate = 0;
4300 : 0 : hfsc_query_class__(netdev, tc_make_handle(1, 0xfffe), 0, &hc, NULL);
4301 : 0 : hfsc_install__(netdev, hc.max_rate);
4302 : :
4303 [ # # ]: 0 : if (!start_queue_dump(netdev, &state)) {
4304 : 0 : return ENODEV;
4305 : : }
4306 : :
4307 [ # # ]: 0 : while (nl_dump_next(&state.dump, &msg, &state.buf)) {
4308 : : unsigned int queue_id;
4309 : :
4310 [ # # ]: 0 : if (!hfsc_parse_tcmsg__(&msg, &queue_id, &hc, NULL)) {
4311 : 0 : hfsc_update_queue__(netdev, queue_id, &hc);
4312 : : }
4313 : : }
4314 : :
4315 : 0 : finish_queue_dump(&state);
4316 : 0 : return 0;
4317 : : }
4318 : :
4319 : : static void
4320 : 0 : hfsc_tc_destroy(struct tc *tc)
4321 : : {
4322 : : struct hfsc *hfsc;
4323 : : struct hfsc_class *hc, *next;
4324 : :
4325 : 0 : hfsc = CONTAINER_OF(tc, struct hfsc, tc);
4326 : :
4327 [ # # ][ # # ]: 0 : HMAP_FOR_EACH_SAFE (hc, next, tc_queue.hmap_node, &hfsc->tc.queues) {
[ # # ]
4328 : 0 : hmap_remove(&hfsc->tc.queues, &hc->tc_queue.hmap_node);
4329 : 0 : free(hc);
4330 : : }
4331 : :
4332 : 0 : tc_destroy(tc);
4333 : 0 : free(hfsc);
4334 : 0 : }
4335 : :
4336 : : static int
4337 : 0 : hfsc_qdisc_get(const struct netdev *netdev, struct smap *details)
4338 : : {
4339 : : const struct hfsc *hfsc;
4340 : 0 : hfsc = hfsc_get__(netdev);
4341 : 0 : smap_add_format(details, "max-rate", "%llu", 8ULL * hfsc->max_rate);
4342 : 0 : return 0;
4343 : : }
4344 : :
4345 : : static int
4346 : 0 : hfsc_qdisc_set(struct netdev *netdev, const struct smap *details)
4347 : : {
4348 : : int error;
4349 : : struct hfsc_class class;
4350 : :
4351 : 0 : hfsc_parse_qdisc_details__(netdev, details, &class);
4352 : 0 : error = hfsc_setup_class__(netdev, tc_make_handle(1, 0xfffe),
4353 : : tc_make_handle(1, 0), &class);
4354 : :
4355 [ # # ]: 0 : if (!error) {
4356 : 0 : hfsc_get__(netdev)->max_rate = class.max_rate;
4357 : : }
4358 : :
4359 : 0 : return error;
4360 : : }
4361 : :
4362 : : static int
4363 : 0 : hfsc_class_get(const struct netdev *netdev OVS_UNUSED,
4364 : : const struct tc_queue *queue, struct smap *details)
4365 : : {
4366 : : const struct hfsc_class *hc;
4367 : :
4368 : 0 : hc = hfsc_class_cast__(queue);
4369 : 0 : smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate);
4370 [ # # ]: 0 : if (hc->min_rate != hc->max_rate) {
4371 : 0 : smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate);
4372 : : }
4373 : 0 : return 0;
4374 : : }
4375 : :
4376 : : static int
4377 : 0 : hfsc_class_set(struct netdev *netdev, unsigned int queue_id,
4378 : : const struct smap *details)
4379 : : {
4380 : : int error;
4381 : : struct hfsc_class class;
4382 : :
4383 : 0 : error = hfsc_parse_class_details__(netdev, details, &class);
4384 [ # # ]: 0 : if (error) {
4385 : 0 : return error;
4386 : : }
4387 : :
4388 : 0 : error = hfsc_setup_class__(netdev, tc_make_handle(1, queue_id + 1),
4389 : : tc_make_handle(1, 0xfffe), &class);
4390 [ # # ]: 0 : if (error) {
4391 : 0 : return error;
4392 : : }
4393 : :
4394 : 0 : hfsc_update_queue__(netdev, queue_id, &class);
4395 : 0 : return 0;
4396 : : }
4397 : :
4398 : : static int
4399 : 0 : hfsc_class_delete(struct netdev *netdev, struct tc_queue *queue)
4400 : : {
4401 : : int error;
4402 : : struct hfsc *hfsc;
4403 : : struct hfsc_class *hc;
4404 : :
4405 : 0 : hc = hfsc_class_cast__(queue);
4406 : 0 : hfsc = hfsc_get__(netdev);
4407 : :
4408 : 0 : error = tc_delete_class(netdev, tc_make_handle(1, queue->queue_id + 1));
4409 [ # # ]: 0 : if (!error) {
4410 : 0 : hmap_remove(&hfsc->tc.queues, &hc->tc_queue.hmap_node);
4411 : 0 : free(hc);
4412 : : }
4413 : 0 : return error;
4414 : : }
4415 : :
4416 : : static int
4417 : 0 : hfsc_class_get_stats(const struct netdev *netdev, const struct tc_queue *queue,
4418 : : struct netdev_queue_stats *stats)
4419 : : {
4420 : 0 : return hfsc_query_class__(netdev, tc_make_handle(1, queue->queue_id + 1),
4421 : : tc_make_handle(1, 0xfffe), NULL, stats);
4422 : : }
4423 : :
4424 : : static int
4425 : 0 : hfsc_class_dump_stats(const struct netdev *netdev OVS_UNUSED,
4426 : : const struct ofpbuf *nlmsg,
4427 : : netdev_dump_queue_stats_cb *cb, void *aux)
4428 : : {
4429 : : struct netdev_queue_stats stats;
4430 : : unsigned int handle, major, minor;
4431 : : int error;
4432 : :
4433 : 0 : error = tc_parse_class(nlmsg, &handle, NULL, &stats);
4434 [ # # ]: 0 : if (error) {
4435 : 0 : return error;
4436 : : }
4437 : :
4438 : 0 : major = tc_get_major(handle);
4439 : 0 : minor = tc_get_minor(handle);
4440 [ # # ][ # # ]: 0 : if (major == 1 && minor > 0 && minor <= HFSC_N_QUEUES) {
[ # # ]
4441 : 0 : (*cb)(minor - 1, &stats, aux);
4442 : : }
4443 : 0 : return 0;
4444 : : }
4445 : :
4446 : : static const struct tc_ops tc_ops_hfsc = {
4447 : : "hfsc", /* linux_name */
4448 : : "linux-hfsc", /* ovs_name */
4449 : : HFSC_N_QUEUES, /* n_queues */
4450 : : hfsc_tc_install, /* tc_install */
4451 : : hfsc_tc_load, /* tc_load */
4452 : : hfsc_tc_destroy, /* tc_destroy */
4453 : : hfsc_qdisc_get, /* qdisc_get */
4454 : : hfsc_qdisc_set, /* qdisc_set */
4455 : : hfsc_class_get, /* class_get */
4456 : : hfsc_class_set, /* class_set */
4457 : : hfsc_class_delete, /* class_delete */
4458 : : hfsc_class_get_stats, /* class_get_stats */
4459 : : hfsc_class_dump_stats /* class_dump_stats */
4460 : : };
4461 : :
4462 : : /* "linux-noop" traffic control class. */
4463 : :
4464 : : static void
4465 : 0 : noop_install__(struct netdev *netdev_)
4466 : : {
4467 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
4468 : : static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_default);
4469 : :
4470 : 0 : netdev->tc = CONST_CAST(struct tc *, &tc);
4471 : 0 : }
4472 : :
4473 : : static int
4474 : 0 : noop_tc_install(struct netdev *netdev,
4475 : : const struct smap *details OVS_UNUSED)
4476 : : {
4477 : 0 : noop_install__(netdev);
4478 : 0 : return 0;
4479 : : }
4480 : :
4481 : : static int
4482 : 0 : noop_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
4483 : : {
4484 : 0 : noop_install__(netdev);
4485 : 0 : return 0;
4486 : : }
4487 : :
4488 : : static const struct tc_ops tc_ops_noop = {
4489 : : NULL, /* linux_name */
4490 : : "linux-noop", /* ovs_name */
4491 : : 0, /* n_queues */
4492 : : noop_tc_install,
4493 : : noop_tc_load,
4494 : : NULL, /* tc_destroy */
4495 : : NULL, /* qdisc_get */
4496 : : NULL, /* qdisc_set */
4497 : : NULL, /* class_get */
4498 : : NULL, /* class_set */
4499 : : NULL, /* class_delete */
4500 : : NULL, /* class_get_stats */
4501 : : NULL /* class_dump_stats */
4502 : : };
4503 : :
4504 : : /* "linux-default" traffic control class.
4505 : : *
4506 : : * This class represents the default, unnamed Linux qdisc. It corresponds to
4507 : : * the "" (empty string) QoS type in the OVS database. */
4508 : :
4509 : : static void
4510 : 344 : default_install__(struct netdev *netdev_)
4511 : : {
4512 : 344 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
4513 : : static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_default);
4514 : :
4515 : : /* Nothing but a tc class implementation is allowed to write to a tc. This
4516 : : * class never does that, so we can legitimately use a const tc object. */
4517 : 344 : netdev->tc = CONST_CAST(struct tc *, &tc);
4518 : 344 : }
4519 : :
4520 : : static int
4521 : 233 : default_tc_install(struct netdev *netdev,
4522 : : const struct smap *details OVS_UNUSED)
4523 : : {
4524 : 233 : default_install__(netdev);
4525 : 233 : return 0;
4526 : : }
4527 : :
4528 : : static int
4529 : 111 : default_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
4530 : : {
4531 : 111 : default_install__(netdev);
4532 : 111 : return 0;
4533 : : }
4534 : :
4535 : : static const struct tc_ops tc_ops_default = {
4536 : : NULL, /* linux_name */
4537 : : "", /* ovs_name */
4538 : : 0, /* n_queues */
4539 : : default_tc_install,
4540 : : default_tc_load,
4541 : : NULL, /* tc_destroy */
4542 : : NULL, /* qdisc_get */
4543 : : NULL, /* qdisc_set */
4544 : : NULL, /* class_get */
4545 : : NULL, /* class_set */
4546 : : NULL, /* class_delete */
4547 : : NULL, /* class_get_stats */
4548 : : NULL /* class_dump_stats */
4549 : : };
4550 : :
4551 : : /* "linux-other" traffic control class.
4552 : : *
4553 : : * */
4554 : :
4555 : : static int
4556 : 0 : other_tc_load(struct netdev *netdev_, struct ofpbuf *nlmsg OVS_UNUSED)
4557 : : {
4558 : 0 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
4559 : : static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_other);
4560 : :
4561 : : /* Nothing but a tc class implementation is allowed to write to a tc. This
4562 : : * class never does that, so we can legitimately use a const tc object. */
4563 : 0 : netdev->tc = CONST_CAST(struct tc *, &tc);
4564 : 0 : return 0;
4565 : : }
4566 : :
4567 : : static const struct tc_ops tc_ops_other = {
4568 : : NULL, /* linux_name */
4569 : : "linux-other", /* ovs_name */
4570 : : 0, /* n_queues */
4571 : : NULL, /* tc_install */
4572 : : other_tc_load,
4573 : : NULL, /* tc_destroy */
4574 : : NULL, /* qdisc_get */
4575 : : NULL, /* qdisc_set */
4576 : : NULL, /* class_get */
4577 : : NULL, /* class_set */
4578 : : NULL, /* class_delete */
4579 : : NULL, /* class_get_stats */
4580 : : NULL /* class_dump_stats */
4581 : : };
4582 : :
4583 : : /* Traffic control. */
4584 : :
4585 : : /* Number of kernel "tc" ticks per second. */
4586 : : static double ticks_per_s;
4587 : :
4588 : : /* Number of kernel "jiffies" per second. This is used for the purpose of
4589 : : * computing buffer sizes. Generally kernel qdiscs need to be able to buffer
4590 : : * one jiffy's worth of data.
4591 : : *
4592 : : * There are two possibilities here:
4593 : : *
4594 : : * - 'buffer_hz' is the kernel's real timer tick rate, a small number in the
4595 : : * approximate range of 100 to 1024. That means that we really need to
4596 : : * make sure that the qdisc can buffer that much data.
4597 : : *
4598 : : * - 'buffer_hz' is an absurdly large number. That means that the kernel
4599 : : * has finely granular timers and there's no need to fudge additional room
4600 : : * for buffers. (There's no extra effort needed to implement that: the
4601 : : * large 'buffer_hz' is used as a divisor, so practically any number will
4602 : : * come out as 0 in the division. Small integer results in the case of
4603 : : * really high dividends won't have any real effect anyhow.)
4604 : : */
4605 : : static unsigned int buffer_hz;
4606 : :
4607 : : /* Returns tc handle 'major':'minor'. */
4608 : : static unsigned int
4609 : 1499 : tc_make_handle(unsigned int major, unsigned int minor)
4610 : : {
4611 : 1499 : return TC_H_MAKE(major << 16, minor);
4612 : : }
4613 : :
4614 : : /* Returns the major number from 'handle'. */
4615 : : static unsigned int
4616 : 0 : tc_get_major(unsigned int handle)
4617 : : {
4618 : 0 : return TC_H_MAJ(handle) >> 16;
4619 : : }
4620 : :
4621 : : /* Returns the minor number from 'handle'. */
4622 : : static unsigned int
4623 : 0 : tc_get_minor(unsigned int handle)
4624 : : {
4625 : 0 : return TC_H_MIN(handle);
4626 : : }
4627 : :
4628 : : static struct tcmsg *
4629 : 1501 : tc_make_request(const struct netdev *netdev, int type, unsigned int flags,
4630 : : struct ofpbuf *request)
4631 : : {
4632 : : struct tcmsg *tcmsg;
4633 : : int ifindex;
4634 : : int error;
4635 : :
4636 : 1501 : error = get_ifindex(netdev, &ifindex);
4637 [ + + ]: 1501 : if (error) {
4638 : 2 : return NULL;
4639 : : }
4640 : :
4641 : 1499 : ofpbuf_init(request, 512);
4642 : 1499 : nl_msg_put_nlmsghdr(request, sizeof *tcmsg, type, NLM_F_REQUEST | flags);
4643 : 1499 : tcmsg = ofpbuf_put_zeros(request, sizeof *tcmsg);
4644 : 1499 : tcmsg->tcm_family = AF_UNSPEC;
4645 : 1499 : tcmsg->tcm_ifindex = ifindex;
4646 : : /* Caller should fill in tcmsg->tcm_handle. */
4647 : : /* Caller should fill in tcmsg->tcm_parent. */
4648 : :
4649 : 1501 : return tcmsg;
4650 : : }
4651 : :
4652 : : static int
4653 : 1499 : tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
4654 : : {
4655 : 1499 : int error = nl_transact(NETLINK_ROUTE, request, replyp);
4656 : 1499 : ofpbuf_uninit(request);
4657 : 1499 : return error;
4658 : : }
4659 : :
4660 : : /* Adds or deletes a root ingress qdisc on 'netdev'. We use this for
4661 : : * policing configuration.
4662 : : *
4663 : : * This function is equivalent to running the following when 'add' is true:
4664 : : * /sbin/tc qdisc add dev <devname> handle ffff: ingress
4665 : : *
4666 : : * This function is equivalent to running the following when 'add' is false:
4667 : : * /sbin/tc qdisc del dev <devname> handle ffff: ingress
4668 : : *
4669 : : * The configuration and stats may be seen with the following command:
4670 : : * /sbin/tc -s qdisc show dev <devname>
4671 : : *
4672 : : * Returns 0 if successful, otherwise a positive errno value.
4673 : : */
4674 : : static int
4675 : 924 : tc_add_del_ingress_qdisc(struct netdev *netdev, bool add)
4676 : : {
4677 : : struct ofpbuf request;
4678 : : struct tcmsg *tcmsg;
4679 : : int error;
4680 [ - + ]: 924 : int type = add ? RTM_NEWQDISC : RTM_DELQDISC;
4681 [ - + ]: 924 : int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
4682 : :
4683 : 924 : tcmsg = tc_make_request(netdev, type, flags, &request);
4684 [ + + ]: 924 : if (!tcmsg) {
4685 : 2 : return ENODEV;
4686 : : }
4687 : 922 : tcmsg->tcm_handle = tc_make_handle(0xffff, 0);
4688 : 922 : tcmsg->tcm_parent = TC_H_INGRESS;
4689 : 922 : nl_msg_put_string(&request, TCA_KIND, "ingress");
4690 : 922 : nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
4691 : :
4692 : 922 : error = tc_transact(&request, NULL);
4693 [ + - ]: 922 : if (error) {
4694 : : /* If we're deleting the qdisc, don't worry about some of the
4695 : : * error conditions. */
4696 [ + - ][ - + ]: 922 : if (!add && (error == ENOENT || error == EINVAL)) {
[ # # ]
4697 : 922 : return 0;
4698 : : }
4699 : 0 : return error;
4700 : : }
4701 : :
4702 : 924 : return 0;
4703 : : }
4704 : :
4705 : : /* Adds a policer to 'netdev' with a rate of 'kbits_rate' and a burst size
4706 : : * of 'kbits_burst'.
4707 : : *
4708 : : * This function is equivalent to running:
4709 : : * /sbin/tc filter add dev <devname> parent ffff: protocol all prio 49
4710 : : * basic police rate <kbits_rate>kbit burst <kbits_burst>k
4711 : : * mtu 65535 drop
4712 : : *
4713 : : * The configuration and stats may be seen with the following command:
4714 : : * /sbin/tc -s filter show dev <devname> parent ffff:
4715 : : *
4716 : : * Returns 0 if successful, otherwise a positive errno value.
4717 : : */
4718 : : static int
4719 : 0 : tc_add_policer(struct netdev *netdev,
4720 : : uint32_t kbits_rate, uint32_t kbits_burst)
4721 : : {
4722 : : struct tc_police tc_police;
4723 : : struct ofpbuf request;
4724 : : struct tcmsg *tcmsg;
4725 : : size_t basic_offset;
4726 : : size_t police_offset;
4727 : : int error;
4728 : 0 : int mtu = 65535;
4729 : :
4730 : 0 : memset(&tc_police, 0, sizeof tc_police);
4731 : 0 : tc_police.action = TC_POLICE_SHOT;
4732 : 0 : tc_police.mtu = mtu;
4733 : 0 : tc_fill_rate(&tc_police.rate, ((uint64_t) kbits_rate * 1000)/8, mtu);
4734 : :
4735 : : /* The following appears wrong in one way: In networking a kilobit is
4736 : : * usually 1000 bits but this uses 1024 bits.
4737 : : *
4738 : : * However if you "fix" those problems then "tc filter show ..." shows
4739 : : * "125000b", meaning 125,000 bits, when OVS configures it for 1000 kbit ==
4740 : : * 1,000,000 bits, whereas this actually ends up doing the right thing from
4741 : : * tc's point of view. Whatever. */
4742 : 0 : tc_police.burst = tc_bytes_to_ticks(
4743 : 0 : tc_police.rate.rate, MIN(UINT32_MAX / 1024, kbits_burst) * 1024 / 8);
4744 : :
4745 : 0 : tcmsg = tc_make_request(netdev, RTM_NEWTFILTER,
4746 : : NLM_F_EXCL | NLM_F_CREATE, &request);
4747 [ # # ]: 0 : if (!tcmsg) {
4748 : 0 : return ENODEV;
4749 : : }
4750 : 0 : tcmsg->tcm_parent = tc_make_handle(0xffff, 0);
4751 : 0 : tcmsg->tcm_info = tc_make_handle(49,
4752 : 0 : (OVS_FORCE uint16_t) htons(ETH_P_ALL));
4753 : :
4754 : 0 : nl_msg_put_string(&request, TCA_KIND, "basic");
4755 : 0 : basic_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
4756 : 0 : police_offset = nl_msg_start_nested(&request, TCA_BASIC_POLICE);
4757 : 0 : nl_msg_put_unspec(&request, TCA_POLICE_TBF, &tc_police, sizeof tc_police);
4758 : 0 : tc_put_rtab(&request, TCA_POLICE_RATE, &tc_police.rate);
4759 : 0 : nl_msg_end_nested(&request, police_offset);
4760 : 0 : nl_msg_end_nested(&request, basic_offset);
4761 : :
4762 : 0 : error = tc_transact(&request, NULL);
4763 [ # # ]: 0 : if (error) {
4764 : 0 : return error;
4765 : : }
4766 : :
4767 : 0 : return 0;
4768 : : }
4769 : :
4770 : : static void
4771 : 0 : read_psched(void)
4772 : : {
4773 : : /* The values in psched are not individually very meaningful, but they are
4774 : : * important. The tables below show some values seen in the wild.
4775 : : *
4776 : : * Some notes:
4777 : : *
4778 : : * - "c" has always been a constant 1000000 since at least Linux 2.4.14.
4779 : : * (Before that, there are hints that it was 1000000000.)
4780 : : *
4781 : : * - "d" can be unrealistically large, see the comment on 'buffer_hz'
4782 : : * above.
4783 : : *
4784 : : * /proc/net/psched
4785 : : * -----------------------------------
4786 : : * [1] 000c8000 000f4240 000f4240 00000064
4787 : : * [2] 000003e8 00000400 000f4240 3b9aca00
4788 : : * [3] 000003e8 00000400 000f4240 3b9aca00
4789 : : * [4] 000003e8 00000400 000f4240 00000064
4790 : : * [5] 000003e8 00000040 000f4240 3b9aca00
4791 : : * [6] 000003e8 00000040 000f4240 000000f9
4792 : : *
4793 : : * a b c d ticks_per_s buffer_hz
4794 : : * ------- --------- ---------- ------------- ----------- -------------
4795 : : * [1] 819,200 1,000,000 1,000,000 100 819,200 100
4796 : : * [2] 1,000 1,024 1,000,000 1,000,000,000 976,562 1,000,000,000
4797 : : * [3] 1,000 1,024 1,000,000 1,000,000,000 976,562 1,000,000,000
4798 : : * [4] 1,000 1,024 1,000,000 100 976,562 100
4799 : : * [5] 1,000 64 1,000,000 1,000,000,000 15,625,000 1,000,000,000
4800 : : * [6] 1,000 64 1,000,000 249 15,625,000 249
4801 : : *
4802 : : * [1] 2.6.18-128.1.6.el5.xs5.5.0.505.1024xen from XenServer 5.5.0-24648p
4803 : : * [2] 2.6.26-1-686-bigmem from Debian lenny
4804 : : * [3] 2.6.26-2-sparc64 from Debian lenny
4805 : : * [4] 2.6.27.42-0.1.1.xs5.6.810.44.111163xen from XenServer 5.6.810-31078p
4806 : : * [5] 2.6.32.21.22 (approx.) from Ubuntu 10.04 on VMware Fusion
4807 : : * [6] 2.6.34 from kernel.org on KVM
4808 : : */
4809 : : static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
4810 : : static const char fn[] = "/proc/net/psched";
4811 : : unsigned int a, b, c, d;
4812 : : FILE *stream;
4813 : :
4814 [ # # ]: 0 : if (!ovsthread_once_start(&once)) {
4815 : 0 : return;
4816 : : }
4817 : :
4818 : 0 : ticks_per_s = 1.0;
4819 : 0 : buffer_hz = 100;
4820 : :
4821 : 0 : stream = fopen(fn, "r");
4822 [ # # ]: 0 : if (!stream) {
4823 [ # # ]: 0 : VLOG_WARN("%s: open failed: %s", fn, ovs_strerror(errno));
4824 : 0 : goto exit;
4825 : : }
4826 : :
4827 [ # # ]: 0 : if (fscanf(stream, "%x %x %x %x", &a, &b, &c, &d) != 4) {
4828 [ # # ]: 0 : VLOG_WARN("%s: read failed", fn);
4829 : 0 : fclose(stream);
4830 : 0 : goto exit;
4831 : : }
4832 [ # # ]: 0 : VLOG_DBG("%s: psched parameters are: %u %u %u %u", fn, a, b, c, d);
4833 : 0 : fclose(stream);
4834 : :
4835 [ # # ][ # # ]: 0 : if (!a || !c) {
4836 [ # # ]: 0 : VLOG_WARN("%s: invalid scheduler parameters", fn);
4837 : 0 : goto exit;
4838 : : }
4839 : :
4840 : 0 : ticks_per_s = (double) a * c / b;
4841 [ # # ]: 0 : if (c == 1000000) {
4842 : 0 : buffer_hz = d;
4843 : : } else {
4844 [ # # ]: 0 : VLOG_WARN("%s: unexpected psched parameters: %u %u %u %u",
4845 : : fn, a, b, c, d);
4846 : : }
4847 [ # # ]: 0 : VLOG_DBG("%s: ticks_per_s=%f buffer_hz=%u", fn, ticks_per_s, buffer_hz);
4848 : :
4849 : : exit:
4850 : 0 : ovsthread_once_done(&once);
4851 : : }
4852 : :
4853 : : /* Returns the number of bytes that can be transmitted in 'ticks' ticks at a
4854 : : * rate of 'rate' bytes per second. */
4855 : : static unsigned int
4856 : 0 : tc_ticks_to_bytes(unsigned int rate, unsigned int ticks)
4857 : : {
4858 : 0 : read_psched();
4859 : 0 : return (rate * ticks) / ticks_per_s;
4860 : : }
4861 : :
4862 : : /* Returns the number of ticks that it would take to transmit 'size' bytes at a
4863 : : * rate of 'rate' bytes per second. */
4864 : : static unsigned int
4865 : 0 : tc_bytes_to_ticks(unsigned int rate, unsigned int size)
4866 : : {
4867 : 0 : read_psched();
4868 [ # # ]: 0 : return rate ? ((unsigned long long int) ticks_per_s * size) / rate : 0;
4869 : : }
4870 : :
4871 : : /* Returns the number of bytes that need to be reserved for qdisc buffering at
4872 : : * a transmission rate of 'rate' bytes per second. */
4873 : : static unsigned int
4874 : 0 : tc_buffer_per_jiffy(unsigned int rate)
4875 : : {
4876 : 0 : read_psched();
4877 : 0 : return rate / buffer_hz;
4878 : : }
4879 : :
4880 : : /* Given Netlink 'msg' that describes a qdisc, extracts the name of the qdisc,
4881 : : * e.g. "htb", into '*kind' (if it is nonnull). If 'options' is nonnull,
4882 : : * extracts 'msg''s TCA_OPTIONS attributes into '*options' if it is present or
4883 : : * stores NULL into it if it is absent.
4884 : : *
4885 : : * '*kind' and '*options' point into 'msg', so they are owned by whoever owns
4886 : : * 'msg'.
4887 : : *
4888 : : * Returns 0 if successful, otherwise a positive errno value. */
4889 : : static int
4890 : 466 : tc_parse_qdisc(const struct ofpbuf *msg, const char **kind,
4891 : : struct nlattr **options)
4892 : : {
4893 : : static const struct nl_policy tca_policy[] = {
4894 : : [TCA_KIND] = { .type = NL_A_STRING, .optional = false },
4895 : : [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
4896 : : };
4897 : : struct nlattr *ta[ARRAY_SIZE(tca_policy)];
4898 : :
4899 [ - + ]: 466 : if (!nl_policy_parse(msg, NLMSG_HDRLEN + sizeof(struct tcmsg),
4900 : : tca_policy, ta, ARRAY_SIZE(ta))) {
4901 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse qdisc message");
4902 : 0 : goto error;
4903 : : }
4904 : :
4905 [ + - ]: 466 : if (kind) {
4906 : 466 : *kind = nl_attr_get_string(ta[TCA_KIND]);
4907 : : }
4908 : :
4909 [ + + ]: 466 : if (options) {
4910 : 233 : *options = ta[TCA_OPTIONS];
4911 : : }
4912 : :
4913 : 466 : return 0;
4914 : :
4915 : : error:
4916 [ # # ]: 0 : if (kind) {
4917 : 0 : *kind = NULL;
4918 : : }
4919 [ # # ]: 0 : if (options) {
4920 : 0 : *options = NULL;
4921 : : }
4922 : 466 : return EPROTO;
4923 : : }
4924 : :
4925 : : /* Given Netlink 'msg' that describes a class, extracts the queue ID (e.g. the
4926 : : * minor number of its class ID) into '*queue_id', its TCA_OPTIONS attribute
4927 : : * into '*options', and its queue statistics into '*stats'. Any of the output
4928 : : * arguments may be null.
4929 : : *
4930 : : * Returns 0 if successful, otherwise a positive errno value. */
4931 : : static int
4932 : 0 : tc_parse_class(const struct ofpbuf *msg, unsigned int *handlep,
4933 : : struct nlattr **options, struct netdev_queue_stats *stats)
4934 : : {
4935 : : static const struct nl_policy tca_policy[] = {
4936 : : [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false },
4937 : : [TCA_STATS2] = { .type = NL_A_NESTED, .optional = false },
4938 : : };
4939 : : struct nlattr *ta[ARRAY_SIZE(tca_policy)];
4940 : :
4941 [ # # ]: 0 : if (!nl_policy_parse(msg, NLMSG_HDRLEN + sizeof(struct tcmsg),
4942 : : tca_policy, ta, ARRAY_SIZE(ta))) {
4943 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse class message");
4944 : 0 : goto error;
4945 : : }
4946 : :
4947 [ # # ]: 0 : if (handlep) {
4948 : 0 : struct tcmsg *tc = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tc);
4949 : 0 : *handlep = tc->tcm_handle;
4950 : : }
4951 : :
4952 [ # # ]: 0 : if (options) {
4953 : 0 : *options = ta[TCA_OPTIONS];
4954 : : }
4955 : :
4956 [ # # ]: 0 : if (stats) {
4957 : : const struct gnet_stats_queue *gsq;
4958 : : struct gnet_stats_basic gsb;
4959 : :
4960 : : static const struct nl_policy stats_policy[] = {
4961 : : [TCA_STATS_BASIC] = { .type = NL_A_UNSPEC, .optional = false,
4962 : : .min_len = sizeof gsb },
4963 : : [TCA_STATS_QUEUE] = { .type = NL_A_UNSPEC, .optional = false,
4964 : : .min_len = sizeof *gsq },
4965 : : };
4966 : : struct nlattr *sa[ARRAY_SIZE(stats_policy)];
4967 : :
4968 [ # # ]: 0 : if (!nl_parse_nested(ta[TCA_STATS2], stats_policy,
4969 : : sa, ARRAY_SIZE(sa))) {
4970 [ # # ]: 0 : VLOG_WARN_RL(&rl, "failed to parse class stats");
4971 : 0 : goto error;
4972 : : }
4973 : :
4974 : : /* Alignment issues screw up the length of struct gnet_stats_basic on
4975 : : * some arch/bitsize combinations. Newer versions of Linux have a
4976 : : * struct gnet_stats_basic_packed, but we can't depend on that. The
4977 : : * easiest thing to do is just to make a copy. */
4978 : 0 : memset(&gsb, 0, sizeof gsb);
4979 [ # # ]: 0 : memcpy(&gsb, nl_attr_get(sa[TCA_STATS_BASIC]),
4980 : 0 : MIN(nl_attr_get_size(sa[TCA_STATS_BASIC]), sizeof gsb));
4981 : 0 : stats->tx_bytes = gsb.bytes;
4982 : 0 : stats->tx_packets = gsb.packets;
4983 : :
4984 : 0 : gsq = nl_attr_get(sa[TCA_STATS_QUEUE]);
4985 : 0 : stats->tx_errors = gsq->drops;
4986 : : }
4987 : :
4988 : 0 : return 0;
4989 : :
4990 : : error:
4991 [ # # ]: 0 : if (options) {
4992 : 0 : *options = NULL;
4993 : : }
4994 [ # # ]: 0 : if (stats) {
4995 : 0 : memset(stats, 0, sizeof *stats);
4996 : : }
4997 : 0 : return EPROTO;
4998 : : }
4999 : :
5000 : : /* Queries the kernel for class with identifier 'handle' and parent 'parent'
5001 : : * on 'netdev'. */
5002 : : static int
5003 : 0 : tc_query_class(const struct netdev *netdev,
5004 : : unsigned int handle, unsigned int parent,
5005 : : struct ofpbuf **replyp)
5006 : : {
5007 : : struct ofpbuf request;
5008 : : struct tcmsg *tcmsg;
5009 : : int error;
5010 : :
5011 : 0 : tcmsg = tc_make_request(netdev, RTM_GETTCLASS, NLM_F_ECHO, &request);
5012 [ # # ]: 0 : if (!tcmsg) {
5013 : 0 : return ENODEV;
5014 : : }
5015 : 0 : tcmsg->tcm_handle = handle;
5016 : 0 : tcmsg->tcm_parent = parent;
5017 : :
5018 : 0 : error = tc_transact(&request, replyp);
5019 [ # # ]: 0 : if (error) {
5020 [ # # ]: 0 : VLOG_WARN_RL(&rl, "query %s class %u:%u (parent %u:%u) failed (%s)",
5021 : : netdev_get_name(netdev),
5022 : : tc_get_major(handle), tc_get_minor(handle),
5023 : : tc_get_major(parent), tc_get_minor(parent),
5024 : : ovs_strerror(error));
5025 : : }
5026 : 0 : return error;
5027 : : }
5028 : :
5029 : : /* Equivalent to "tc class del dev <name> handle <handle>". */
5030 : : static int
5031 : 0 : tc_delete_class(const struct netdev *netdev, unsigned int handle)
5032 : : {
5033 : : struct ofpbuf request;
5034 : : struct tcmsg *tcmsg;
5035 : : int error;
5036 : :
5037 : 0 : tcmsg = tc_make_request(netdev, RTM_DELTCLASS, 0, &request);
5038 [ # # ]: 0 : if (!tcmsg) {
5039 : 0 : return ENODEV;
5040 : : }
5041 : 0 : tcmsg->tcm_handle = handle;
5042 : 0 : tcmsg->tcm_parent = 0;
5043 : :
5044 : 0 : error = tc_transact(&request, NULL);
5045 [ # # ]: 0 : if (error) {
5046 [ # # ]: 0 : VLOG_WARN_RL(&rl, "delete %s class %u:%u failed (%s)",
5047 : : netdev_get_name(netdev),
5048 : : tc_get_major(handle), tc_get_minor(handle),
5049 : : ovs_strerror(error));
5050 : : }
5051 : 0 : return error;
5052 : : }
5053 : :
5054 : : /* Equivalent to "tc qdisc del dev <name> root". */
5055 : : static int
5056 : 233 : tc_del_qdisc(struct netdev *netdev_)
5057 : : {
5058 : 233 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
5059 : : struct ofpbuf request;
5060 : : struct tcmsg *tcmsg;
5061 : : int error;
5062 : :
5063 : 233 : tcmsg = tc_make_request(netdev_, RTM_DELQDISC, 0, &request);
5064 [ - + ]: 233 : if (!tcmsg) {
5065 : 0 : return ENODEV;
5066 : : }
5067 : 233 : tcmsg->tcm_handle = tc_make_handle(1, 0);
5068 : 233 : tcmsg->tcm_parent = TC_H_ROOT;
5069 : :
5070 : 233 : error = tc_transact(&request, NULL);
5071 [ + - ]: 233 : if (error == EINVAL) {
5072 : : /* EINVAL probably means that the default qdisc was in use, in which
5073 : : * case we've accomplished our purpose. */
5074 : 233 : error = 0;
5075 : : }
5076 [ + - ][ + - ]: 233 : if (!error && netdev->tc) {
5077 [ + - ]: 233 : if (netdev->tc->ops->tc_destroy) {
5078 : 233 : netdev->tc->ops->tc_destroy(netdev->tc);
5079 : : }
5080 : 233 : netdev->tc = NULL;
5081 : : }
5082 : 233 : return error;
5083 : : }
5084 : :
5085 : : static bool
5086 : 688 : getqdisc_is_safe(void)
5087 : : {
5088 : : static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
5089 : : static bool safe = false;
5090 : :
5091 [ + + ]: 688 : if (ovsthread_once_start(&once)) {
5092 : : struct utsname utsname;
5093 : : int major, minor;
5094 : :
5095 [ - + ]: 94 : if (uname(&utsname) == -1) {
5096 [ # # ]: 0 : VLOG_WARN("uname failed (%s)", ovs_strerror(errno));
5097 [ - + ]: 94 : } else if (!ovs_scan(utsname.release, "%d.%d", &major, &minor)) {
5098 [ # # ]: 0 : VLOG_WARN("uname reported bad OS release (%s)", utsname.release);
5099 [ + - ][ - + ]: 94 : } else if (major < 2 || (major == 2 && minor < 35)) {
[ # # ]
5100 [ # # ]: 0 : VLOG_INFO("disabling unsafe RTM_GETQDISC in Linux kernel %s",
5101 : : utsname.release);
5102 : : } else {
5103 : 94 : safe = true;
5104 : : }
5105 : 94 : ovsthread_once_done(&once);
5106 : : }
5107 : 688 : return safe;
5108 : : }
5109 : :
5110 : : /* If 'netdev''s qdisc type and parameters are not yet known, queries the
5111 : : * kernel to determine what they are. Returns 0 if successful, otherwise a
5112 : : * positive errno value. */
5113 : : static int
5114 : 4175 : tc_query_qdisc(const struct netdev *netdev_)
5115 : : {
5116 : 4175 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
5117 : : struct ofpbuf request, *qdisc;
5118 : : const struct tc_ops *ops;
5119 : : struct tcmsg *tcmsg;
5120 : : int load_error;
5121 : : int error;
5122 : :
5123 [ + + ]: 4175 : if (netdev->tc) {
5124 : 3831 : return 0;
5125 : : }
5126 : :
5127 : : /* This RTM_GETQDISC is crafted to avoid OOPSing kernels that do not have
5128 : : * commit 53b0f08 "net_sched: Fix qdisc_notify()", which is anything before
5129 : : * 2.6.35 without that fix backported to it.
5130 : : *
5131 : : * To avoid the OOPS, we must not make a request that would attempt to dump
5132 : : * a "built-in" qdisc, that is, the default pfifo_fast qdisc or one of a
5133 : : * few others. There are a few ways that I can see to do this, but most of
5134 : : * them seem to be racy (and if you lose the race the kernel OOPSes). The
5135 : : * technique chosen here is to assume that any non-default qdisc that we
5136 : : * create will have a class with handle 1:0. The built-in qdiscs only have
5137 : : * a class with handle 0:0.
5138 : : *
5139 : : * On Linux 2.6.35+ we use the straightforward method because it allows us
5140 : : * to handle non-builtin qdiscs without handle 1:0 (e.g. codel). However,
5141 : : * in such a case we get no response at all from the kernel (!) if a
5142 : : * builtin qdisc is in use (which is later caught by "!error &&
5143 : : * !qdisc->size"). */
5144 : 344 : tcmsg = tc_make_request(netdev_, RTM_GETQDISC, NLM_F_ECHO, &request);
5145 [ - + ]: 344 : if (!tcmsg) {
5146 : 0 : return ENODEV;
5147 : : }
5148 [ + - ]: 344 : tcmsg->tcm_handle = tc_make_handle(getqdisc_is_safe() ? 0 : 1, 0);
5149 [ + - ]: 344 : tcmsg->tcm_parent = getqdisc_is_safe() ? TC_H_ROOT : 0;
5150 : :
5151 : : /* Figure out what tc class to instantiate. */
5152 : 344 : error = tc_transact(&request, &qdisc);
5153 [ + - ][ + + ]: 577 : if (!error && qdisc->size) {
5154 : : const char *kind;
5155 : :
5156 : 233 : error = tc_parse_qdisc(qdisc, &kind, NULL);
5157 [ - + ]: 233 : if (error) {
5158 : 0 : ops = &tc_ops_other;
5159 : : } else {
5160 : 233 : ops = tc_lookup_linux_name(kind);
5161 [ - + ]: 233 : if (!ops) {
5162 : : static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 1);
5163 [ # # ]: 0 : VLOG_DBG_RL(&rl2, "unknown qdisc \"%s\"", kind);
5164 : :
5165 : 0 : ops = &tc_ops_other;
5166 : : }
5167 : : }
5168 [ + - ][ - + ]: 111 : } else if ((!error && !qdisc->size) || error == ENOENT) {
[ # # ]
5169 : : /* Either it's a built-in qdisc, or (on Linux pre-2.6.35) it's a qdisc
5170 : : * set up by some other entity that doesn't have a handle 1:0. We will
5171 : : * assume that it's the system default qdisc. */
5172 : 111 : ops = &tc_ops_default;
5173 : 111 : error = 0;
5174 : : } else {
5175 : : /* Who knows? Maybe the device got deleted. */
5176 [ # # ]: 0 : VLOG_WARN_RL(&rl, "query %s qdisc failed (%s)",
5177 : : netdev_get_name(netdev_), ovs_strerror(error));
5178 : 0 : ops = &tc_ops_other;
5179 : : }
5180 : :
5181 : : /* Instantiate it. */
5182 : 344 : load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev_), qdisc);
5183 [ - + ]: 344 : ovs_assert((load_error == 0) == (netdev->tc != NULL));
5184 : 344 : ofpbuf_delete(qdisc);
5185 : :
5186 [ - + ]: 4175 : return error ? error : load_error;
5187 : : }
5188 : :
5189 : : /* Linux traffic control uses tables with 256 entries ("rtab" tables) to
5190 : : approximate the time to transmit packets of various lengths. For an MTU of
5191 : : 256 or less, each entry is exact; for an MTU of 257 through 512, each entry
5192 : : represents two possible packet lengths; for a MTU of 513 through 1024, four
5193 : : possible lengths; and so on.
5194 : :
5195 : : Returns, for the specified 'mtu', the number of bits that packet lengths
5196 : : need to be shifted right to fit within such a 256-entry table. */
5197 : : static int
5198 : 0 : tc_calc_cell_log(unsigned int mtu)
5199 : : {
5200 : : int cell_log;
5201 : :
5202 [ # # ]: 0 : if (!mtu) {
5203 : 0 : mtu = ETH_PAYLOAD_MAX;
5204 : : }
5205 : 0 : mtu += ETH_HEADER_LEN + VLAN_HEADER_LEN;
5206 : :
5207 [ # # ]: 0 : for (cell_log = 0; mtu >= 256; cell_log++) {
5208 : 0 : mtu >>= 1;
5209 : : }
5210 : :
5211 : 0 : return cell_log;
5212 : : }
5213 : :
5214 : : /* Initializes 'rate' properly for a rate of 'Bps' bytes per second with an MTU
5215 : : * of 'mtu'. */
5216 : : static void
5217 : 0 : tc_fill_rate(struct tc_ratespec *rate, uint64_t Bps, int mtu)
5218 : : {
5219 : 0 : memset(rate, 0, sizeof *rate);
5220 : 0 : rate->cell_log = tc_calc_cell_log(mtu);
5221 : : /* rate->overhead = 0; */ /* New in 2.6.24, not yet in some */
5222 : : /* rate->cell_align = 0; */ /* distro headers. */
5223 : 0 : rate->mpu = ETH_TOTAL_MIN;
5224 : 0 : rate->rate = Bps;
5225 : 0 : }
5226 : :
5227 : : /* Appends to 'msg' an "rtab" table for the specified 'rate' as a Netlink
5228 : : * attribute of the specified "type".
5229 : : *
5230 : : * See tc_calc_cell_log() above for a description of "rtab"s. */
5231 : : static void
5232 : 0 : tc_put_rtab(struct ofpbuf *msg, uint16_t type, const struct tc_ratespec *rate)
5233 : : {
5234 : : uint32_t *rtab;
5235 : : unsigned int i;
5236 : :
5237 : 0 : rtab = nl_msg_put_unspec_uninit(msg, type, TC_RTAB_SIZE);
5238 [ # # ]: 0 : for (i = 0; i < TC_RTAB_SIZE / sizeof *rtab; i++) {
5239 : 0 : unsigned packet_size = (i + 1) << rate->cell_log;
5240 [ # # ]: 0 : if (packet_size < rate->mpu) {
5241 : 0 : packet_size = rate->mpu;
5242 : : }
5243 : 0 : rtab[i] = tc_bytes_to_ticks(rate->rate, packet_size);
5244 : : }
5245 : 0 : }
5246 : :
5247 : : /* Calculates the proper value of 'buffer' or 'cbuffer' in HTB options given a
5248 : : * rate of 'Bps' bytes per second, the specified 'mtu', and a user-requested
5249 : : * burst size of 'burst_bytes'. (If no value was requested, a 'burst_bytes' of
5250 : : * 0 is fine.) */
5251 : : static int
5252 : 0 : tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes)
5253 : : {
5254 : 0 : unsigned int min_burst = tc_buffer_per_jiffy(Bps) + mtu;
5255 : 0 : return tc_bytes_to_ticks(Bps, MAX(burst_bytes, min_burst));
5256 : : }
5257 : :
5258 : : /* Linux-only functions declared in netdev-linux.h */
5259 : :
5260 : : /* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If
5261 : : * 'enable' is true, the bit is set. Otherwise, it is cleared. */
5262 : : int
5263 : 151 : netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
5264 : : const char *flag_name, bool enable)
5265 : : {
5266 : 151 : const char *netdev_name = netdev_get_name(netdev);
5267 : : struct ethtool_value evalue;
5268 : : uint32_t new_flags;
5269 : : int error;
5270 : :
5271 : 151 : COVERAGE_INC(netdev_get_ethtool);
5272 : 151 : memset(&evalue, 0, sizeof evalue);
5273 : 151 : error = netdev_linux_do_ethtool(netdev_name,
5274 : : (struct ethtool_cmd *)&evalue,
5275 : : ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
5276 [ - + ]: 151 : if (error) {
5277 : 0 : return error;
5278 : : }
5279 : :
5280 : 151 : COVERAGE_INC(netdev_set_ethtool);
5281 [ - + ]: 151 : new_flags = (evalue.data & ~flag) | (enable ? flag : 0);
5282 [ + - ]: 151 : if (new_flags == evalue.data) {
5283 : 151 : return 0;
5284 : : }
5285 : 0 : evalue.data = new_flags;
5286 : 0 : error = netdev_linux_do_ethtool(netdev_name,
5287 : : (struct ethtool_cmd *)&evalue,
5288 : : ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS");
5289 [ # # ]: 0 : if (error) {
5290 : 0 : return error;
5291 : : }
5292 : :
5293 : 0 : COVERAGE_INC(netdev_get_ethtool);
5294 : 0 : memset(&evalue, 0, sizeof evalue);
5295 : 0 : error = netdev_linux_do_ethtool(netdev_name,
5296 : : (struct ethtool_cmd *)&evalue,
5297 : : ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
5298 [ # # ]: 0 : if (error) {
5299 : 0 : return error;
5300 : : }
5301 : :
5302 [ # # ]: 0 : if (new_flags != evalue.data) {
5303 [ # # ][ # # ]: 0 : VLOG_WARN_RL(&rl, "attempt to %s ethtool %s flag on network "
5304 : : "device %s failed", enable ? "enable" : "disable",
5305 : : flag_name, netdev_name);
5306 : 0 : return EOPNOTSUPP;
5307 : : }
5308 : :
5309 : 151 : return 0;
5310 : : }
5311 : :
5312 : : /* Utility functions. */
5313 : :
5314 : : /* Copies 'src' into 'dst', performing format conversion in the process. */
5315 : : static void
5316 : 0 : netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst,
5317 : : const struct rtnl_link_stats *src)
5318 : : {
5319 : 0 : dst->rx_packets = src->rx_packets;
5320 : 0 : dst->tx_packets = src->tx_packets;
5321 : 0 : dst->rx_bytes = src->rx_bytes;
5322 : 0 : dst->tx_bytes = src->tx_bytes;
5323 : 0 : dst->rx_errors = src->rx_errors;
5324 : 0 : dst->tx_errors = src->tx_errors;
5325 : 0 : dst->rx_dropped = src->rx_dropped;
5326 : 0 : dst->tx_dropped = src->tx_dropped;
5327 : 0 : dst->multicast = src->multicast;
5328 : 0 : dst->collisions = src->collisions;
5329 : 0 : dst->rx_length_errors = src->rx_length_errors;
5330 : 0 : dst->rx_over_errors = src->rx_over_errors;
5331 : 0 : dst->rx_crc_errors = src->rx_crc_errors;
5332 : 0 : dst->rx_frame_errors = src->rx_frame_errors;
5333 : 0 : dst->rx_fifo_errors = src->rx_fifo_errors;
5334 : 0 : dst->rx_missed_errors = src->rx_missed_errors;
5335 : 0 : dst->tx_aborted_errors = src->tx_aborted_errors;
5336 : 0 : dst->tx_carrier_errors = src->tx_carrier_errors;
5337 : 0 : dst->tx_fifo_errors = src->tx_fifo_errors;
5338 : 0 : dst->tx_heartbeat_errors = src->tx_heartbeat_errors;
5339 : 0 : dst->tx_window_errors = src->tx_window_errors;
5340 : 0 : }
5341 : :
5342 : : /* Copies 'src' into 'dst', performing format conversion in the process. */
5343 : : static void
5344 : 425 : netdev_stats_from_rtnl_link_stats64(struct netdev_stats *dst,
5345 : : const struct rtnl_link_stats64 *src)
5346 : : {
5347 : 425 : dst->rx_packets = src->rx_packets;
5348 : 425 : dst->tx_packets = src->tx_packets;
5349 : 425 : dst->rx_bytes = src->rx_bytes;
5350 : 425 : dst->tx_bytes = src->tx_bytes;
5351 : 425 : dst->rx_errors = src->rx_errors;
5352 : 425 : dst->tx_errors = src->tx_errors;
5353 : 425 : dst->rx_dropped = src->rx_dropped;
5354 : 425 : dst->tx_dropped = src->tx_dropped;
5355 : 425 : dst->multicast = src->multicast;
5356 : 425 : dst->collisions = src->collisions;
5357 : 425 : dst->rx_length_errors = src->rx_length_errors;
5358 : 425 : dst->rx_over_errors = src->rx_over_errors;
5359 : 425 : dst->rx_crc_errors = src->rx_crc_errors;
5360 : 425 : dst->rx_frame_errors = src->rx_frame_errors;
5361 : 425 : dst->rx_fifo_errors = src->rx_fifo_errors;
5362 : 425 : dst->rx_missed_errors = src->rx_missed_errors;
5363 : 425 : dst->tx_aborted_errors = src->tx_aborted_errors;
5364 : 425 : dst->tx_carrier_errors = src->tx_carrier_errors;
5365 : 425 : dst->tx_fifo_errors = src->tx_fifo_errors;
5366 : 425 : dst->tx_heartbeat_errors = src->tx_heartbeat_errors;
5367 : 425 : dst->tx_window_errors = src->tx_window_errors;
5368 : 425 : }
5369 : :
5370 : : static int
5371 : 425 : get_stats_via_netlink(const struct netdev *netdev_, struct netdev_stats *stats)
5372 : : {
5373 : : struct ofpbuf request;
5374 : : struct ofpbuf *reply;
5375 : : int error;
5376 : :
5377 : : /* Filtering all counters by default */
5378 : 425 : memset(stats, 0xFF, sizeof(struct netdev_stats));
5379 : :
5380 : 425 : ofpbuf_init(&request, 0);
5381 : 425 : nl_msg_put_nlmsghdr(&request,
5382 : : sizeof(struct ifinfomsg) + NL_ATTR_SIZE(IFNAMSIZ),
5383 : : RTM_GETLINK, NLM_F_REQUEST);
5384 : 425 : ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
5385 : 425 : nl_msg_put_string(&request, IFLA_IFNAME, netdev_get_name(netdev_));
5386 : 425 : error = nl_transact(NETLINK_ROUTE, &request, &reply);
5387 : 425 : ofpbuf_uninit(&request);
5388 [ - + ]: 425 : if (error) {
5389 : 0 : return error;
5390 : : }
5391 : :
5392 [ + - ]: 425 : if (ofpbuf_try_pull(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg))) {
5393 : 425 : const struct nlattr *a = nl_attr_find(reply, 0, IFLA_STATS64);
5394 [ + - ][ + - ]: 425 : if (a && nl_attr_get_size(a) >= sizeof(struct rtnl_link_stats64)) {
5395 : 425 : netdev_stats_from_rtnl_link_stats64(stats, nl_attr_get(a));
5396 : 425 : error = 0;
5397 : : } else {
5398 : 0 : const struct nlattr *a = nl_attr_find(reply, 0, IFLA_STATS);
5399 [ # # ][ # # ]: 0 : if (a && nl_attr_get_size(a) >= sizeof(struct rtnl_link_stats)) {
5400 : 0 : netdev_stats_from_rtnl_link_stats(stats, nl_attr_get(a));
5401 : 0 : error = 0;
5402 : : } else {
5403 [ # # ]: 0 : VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats");
5404 : 425 : error = EPROTO;
5405 : : }
5406 : : }
5407 : : } else {
5408 [ # # ]: 0 : VLOG_WARN_RL(&rl, "short RTM_GETLINK reply");
5409 : 0 : error = EPROTO;
5410 : : }
5411 : :
5412 : :
5413 : 425 : ofpbuf_delete(reply);
5414 : 425 : return error;
5415 : : }
5416 : :
5417 : : static int
5418 : 25509 : get_flags(const struct netdev *dev, unsigned int *flags)
5419 : : {
5420 : : struct ifreq ifr;
5421 : : int error;
5422 : :
5423 : 25509 : *flags = 0;
5424 : 25509 : error = af_inet_ifreq_ioctl(dev->name, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS");
5425 [ + + ]: 25509 : if (!error) {
5426 : 25433 : *flags = ifr.ifr_flags;
5427 : : }
5428 : 25509 : return error;
5429 : : }
5430 : :
5431 : : static int
5432 : 179 : set_flags(const char *name, unsigned int flags)
5433 : : {
5434 : : struct ifreq ifr;
5435 : :
5436 : 179 : ifr.ifr_flags = flags;
5437 : 179 : return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
5438 : : }
5439 : :
5440 : : static int
5441 : 346 : do_get_ifindex(const char *netdev_name)
5442 : : {
5443 : : struct ifreq ifr;
5444 : : int error;
5445 : :
5446 : 346 : ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
5447 : 346 : COVERAGE_INC(netdev_get_ifindex);
5448 : :
5449 : 346 : error = af_inet_ioctl(SIOCGIFINDEX, &ifr);
5450 [ + + ]: 346 : if (error) {
5451 [ + - ]: 2 : VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s",
5452 : : netdev_name, ovs_strerror(error));
5453 : 2 : return -error;
5454 : : }
5455 : 346 : return ifr.ifr_ifindex;
5456 : : }
5457 : :
5458 : : static int
5459 : 6749 : get_ifindex(const struct netdev *netdev_, int *ifindexp)
5460 : : {
5461 : 6749 : struct netdev_linux *netdev = netdev_linux_cast(netdev_);
5462 : :
5463 [ + + ]: 6749 : if (!(netdev->cache_valid & VALID_IFINDEX)) {
5464 : 346 : int ifindex = do_get_ifindex(netdev_get_name(netdev_));
5465 : :
5466 [ + + ]: 346 : if (ifindex < 0) {
5467 : 2 : netdev->get_ifindex_error = -ifindex;
5468 : 2 : netdev->ifindex = 0;
5469 : : } else {
5470 : 344 : netdev->get_ifindex_error = 0;
5471 : 344 : netdev->ifindex = ifindex;
5472 : : }
5473 : 346 : netdev->cache_valid |= VALID_IFINDEX;
5474 : : }
5475 : :
5476 : 6749 : *ifindexp = netdev->ifindex;
5477 : 6749 : return netdev->get_ifindex_error;
5478 : : }
5479 : :
5480 : : static int
5481 : 2817 : get_etheraddr(const char *netdev_name, struct eth_addr *ea)
5482 : : {
5483 : : struct ifreq ifr;
5484 : : int hwaddr_family;
5485 : : int error;
5486 : :
5487 : 2817 : memset(&ifr, 0, sizeof ifr);
5488 : 2817 : ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
5489 : 2817 : COVERAGE_INC(netdev_get_hwaddr);
5490 : 2817 : error = af_inet_ioctl(SIOCGIFHWADDR, &ifr);
5491 [ + + ]: 2817 : if (error) {
5492 : : /* ENODEV probably means that a vif disappeared asynchronously and
5493 : : * hasn't been removed from the database yet, so reduce the log level
5494 : : * to INFO for that case. */
5495 [ + - ][ + - ]: 2 : VLOG(error == ENODEV ? VLL_INFO : VLL_ERR,
5496 : : "ioctl(SIOCGIFHWADDR) on %s device failed: %s",
5497 : : netdev_name, ovs_strerror(error));
5498 : 2 : return error;
5499 : : }
5500 : 2815 : hwaddr_family = ifr.ifr_hwaddr.sa_family;
5501 [ + - ][ - + ]: 2815 : if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
5502 [ # # ]: 0 : VLOG_INFO("%s device has unknown hardware address family %d",
5503 : : netdev_name, hwaddr_family);
5504 : 0 : return EINVAL;
5505 : : }
5506 : 2815 : memcpy(ea, ifr.ifr_hwaddr.sa_data, ETH_ADDR_LEN);
5507 : 2817 : return 0;
5508 : : }
5509 : :
5510 : : static int
5511 : 109 : set_etheraddr(const char *netdev_name, const struct eth_addr mac)
5512 : : {
5513 : : struct ifreq ifr;
5514 : : int error;
5515 : :
5516 : 109 : memset(&ifr, 0, sizeof ifr);
5517 : 109 : ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
5518 : 109 : ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
5519 : 109 : memcpy(ifr.ifr_hwaddr.sa_data, &mac, ETH_ADDR_LEN);
5520 : 109 : COVERAGE_INC(netdev_set_hwaddr);
5521 : 109 : error = af_inet_ioctl(SIOCSIFHWADDR, &ifr);
5522 [ - + ]: 109 : if (error) {
5523 [ # # ]: 0 : VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s",
5524 : : netdev_name, ovs_strerror(error));
5525 : : }
5526 : 109 : return error;
5527 : : }
5528 : :
5529 : : static int
5530 : 1195 : netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd,
5531 : : int cmd, const char *cmd_name)
5532 : : {
5533 : : struct ifreq ifr;
5534 : : int error;
5535 : :
5536 : 1195 : memset(&ifr, 0, sizeof ifr);
5537 : 1195 : ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
5538 : 1195 : ifr.ifr_data = (caddr_t) ecmd;
5539 : :
5540 : 1195 : ecmd->cmd = cmd;
5541 : 1195 : error = af_inet_ioctl(SIOCETHTOOL, &ifr);
5542 [ - + ]: 1195 : if (error) {
5543 [ # # ]: 0 : if (error != EOPNOTSUPP) {
5544 [ # # ]: 0 : VLOG_WARN_RL(&rl, "ethtool command %s on network device %s "
5545 : : "failed: %s", cmd_name, name, ovs_strerror(error));
5546 : : } else {
5547 : : /* The device doesn't support this operation. That's pretty
5548 : : * common, so there's no point in logging anything. */
5549 : : }
5550 : : }
5551 : 1195 : return error;
5552 : : }
5553 : :
5554 : : /* Returns an AF_PACKET raw socket or a negative errno value. */
5555 : : static int
5556 : 930 : af_packet_sock(void)
5557 : : {
5558 : : static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
5559 : : static int sock;
5560 : :
5561 [ + + ]: 930 : if (ovsthread_once_start(&once)) {
5562 : 29 : sock = socket(AF_PACKET, SOCK_RAW, 0);
5563 [ + - ]: 29 : if (sock >= 0) {
5564 : 29 : int error = set_nonblocking(sock);
5565 [ - + ]: 29 : if (error) {
5566 : 0 : close(sock);
5567 : 29 : sock = -error;
5568 : : }
5569 : : } else {
5570 : 0 : sock = -errno;
5571 [ # # ]: 0 : VLOG_ERR("failed to create packet socket: %s",
5572 : : ovs_strerror(errno));
5573 : : }
5574 : 29 : ovsthread_once_done(&once);
5575 : : }
5576 : :
5577 : 930 : return sock;
5578 : : }
|