]>
Commit | Line | Data |
---|---|---|
72865317 | 1 | /* |
ff073a71 | 2 | * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. |
72865317 BP |
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> | |
db73f716 | 18 | #include "dpif-netdev.h" |
72865317 | 19 | |
72865317 BP |
20 | #include <ctype.h> |
21 | #include <errno.h> | |
22 | #include <fcntl.h> | |
23 | #include <inttypes.h> | |
72865317 | 24 | #include <netinet/in.h> |
9d82ec47 | 25 | #include <sys/socket.h> |
7f3adc00 | 26 | #include <net/if.h> |
cdee00fd | 27 | #include <stdint.h> |
72865317 BP |
28 | #include <stdlib.h> |
29 | #include <string.h> | |
30 | #include <sys/ioctl.h> | |
31 | #include <sys/stat.h> | |
72865317 BP |
32 | #include <unistd.h> |
33 | ||
2c0ea78f | 34 | #include "classifier.h" |
59e6d833 | 35 | #include "cmap.h" |
72865317 | 36 | #include "csum.h" |
614c4892 | 37 | #include "dpif.h" |
72865317 | 38 | #include "dpif-provider.h" |
614c4892 | 39 | #include "dummy.h" |
36956a7d | 40 | #include "dynamic-string.h" |
afae68b1 | 41 | #include "fat-rwlock.h" |
72865317 | 42 | #include "flow.h" |
9f361d6b | 43 | #include "cmap.h" |
6c3eee82 | 44 | #include "latch.h" |
72865317 | 45 | #include "list.h" |
8c301900 | 46 | #include "meta-flow.h" |
72865317 | 47 | #include "netdev.h" |
8617afff | 48 | #include "netdev-dpdk.h" |
de281153 | 49 | #include "netdev-vport.h" |
cdee00fd | 50 | #include "netlink.h" |
f094af7b | 51 | #include "odp-execute.h" |
72865317 BP |
52 | #include "odp-util.h" |
53 | #include "ofp-print.h" | |
54 | #include "ofpbuf.h" | |
61e7deb1 | 55 | #include "ovs-rcu.h" |
91088554 | 56 | #include "packet-dpif.h" |
72865317 BP |
57 | #include "packets.h" |
58 | #include "poll-loop.h" | |
26c6b6cd | 59 | #include "random.h" |
d33ed218 | 60 | #include "seq.h" |
462278db | 61 | #include "shash.h" |
0cbfe35d | 62 | #include "sset.h" |
72865317 | 63 | #include "timeval.h" |
74cc3969 | 64 | #include "unixctl.h" |
72865317 | 65 | #include "util.h" |
72865317 | 66 | #include "vlog.h" |
5136ce49 | 67 | |
d98e6007 | 68 | VLOG_DEFINE_THIS_MODULE(dpif_netdev); |
72865317 | 69 | |
2c0ea78f GS |
70 | /* By default, choose a priority in the middle. */ |
71 | #define NETDEV_RULE_PRIORITY 0x8000 | |
72 | ||
8bb113da | 73 | #define FLOW_DUMP_MAX_BATCH 50 |
adcf00ba AZ |
74 | /* Use per thread recirc_depth to prevent recirculation loop. */ |
75 | #define MAX_RECIRC_DEPTH 5 | |
76 | DEFINE_STATIC_PER_THREAD_DATA(uint32_t, recirc_depth, 0) | |
e4cfed38 | 77 | |
72865317 | 78 | /* Configuration parameters. */ |
72865317 BP |
79 | enum { MAX_FLOWS = 65536 }; /* Maximum number of flows in flow table. */ |
80 | ||
8a4e3a85 BP |
81 | /* Protects against changes to 'dp_netdevs'. */ |
82 | static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER; | |
83 | ||
84 | /* Contains all 'struct dp_netdev's. */ | |
85 | static struct shash dp_netdevs OVS_GUARDED_BY(dp_netdev_mutex) | |
86 | = SHASH_INITIALIZER(&dp_netdevs); | |
87 | ||
623540e4 | 88 | static struct vlog_rate_limit upcall_rl = VLOG_RATE_LIMIT_INIT(600, 600); |
6b31e073 | 89 | |
8a4e3a85 BP |
90 | /* Datapath based on the network device interface from netdev.h. |
91 | * | |
92 | * | |
93 | * Thread-safety | |
94 | * ============= | |
95 | * | |
96 | * Some members, marked 'const', are immutable. Accessing other members | |
97 | * requires synchronization, as noted in more detail below. | |
98 | * | |
99 | * Acquisition order is, from outermost to innermost: | |
100 | * | |
101 | * dp_netdev_mutex (global) | |
59e6d833 | 102 | * port_mutex |
8a4e3a85 | 103 | * flow_mutex |
8a4e3a85 | 104 | */ |
72865317 | 105 | struct dp_netdev { |
8a4e3a85 BP |
106 | const struct dpif_class *const class; |
107 | const char *const name; | |
6b31e073 | 108 | struct dpif *dpif; |
6a8267c5 BP |
109 | struct ovs_refcount ref_cnt; |
110 | atomic_flag destroyed; | |
72865317 | 111 | |
8a4e3a85 BP |
112 | /* Flows. |
113 | * | |
afae68b1 JR |
114 | * Writers of 'flow_table' must take the 'flow_mutex'. Corresponding |
115 | * changes to 'cls' must be made while still holding the 'flow_mutex'. | |
8a4e3a85 BP |
116 | */ |
117 | struct ovs_mutex flow_mutex; | |
afae68b1 | 118 | struct classifier cls; |
9f361d6b | 119 | struct cmap flow_table OVS_GUARDED; /* Flow table. */ |
8a4e3a85 | 120 | |
8a4e3a85 BP |
121 | /* Statistics. |
122 | * | |
51852a57 BP |
123 | * ovsthread_stats is internally synchronized. */ |
124 | struct ovsthread_stats stats; /* Contains 'struct dp_netdev_stats *'. */ | |
72865317 | 125 | |
8a4e3a85 BP |
126 | /* Ports. |
127 | * | |
59e6d833 BP |
128 | * Protected by RCU. Take the mutex to add or remove ports. */ |
129 | struct ovs_mutex port_mutex; | |
130 | struct cmap ports; | |
d33ed218 | 131 | struct seq *port_seq; /* Incremented whenever a port changes. */ |
6c3eee82 | 132 | |
6b31e073 RW |
133 | /* Protects access to ofproto-dpif-upcall interface during revalidator |
134 | * thread synchronization. */ | |
135 | struct fat_rwlock upcall_rwlock; | |
623540e4 EJ |
136 | upcall_callback *upcall_cb; /* Callback function for executing upcalls. */ |
137 | void *upcall_aux; | |
6b31e073 | 138 | |
6c3eee82 BP |
139 | /* Forwarding threads. */ |
140 | struct latch exit_latch; | |
e4cfed38 PS |
141 | struct pmd_thread *pmd_threads; |
142 | size_t n_pmd_threads; | |
143 | int pmd_count; | |
72865317 BP |
144 | }; |
145 | ||
8a4e3a85 | 146 | static struct dp_netdev_port *dp_netdev_lookup_port(const struct dp_netdev *dp, |
59e6d833 | 147 | odp_port_t); |
ff073a71 | 148 | |
51852a57 BP |
149 | enum dp_stat_type { |
150 | DP_STAT_HIT, /* Packets that matched in the flow table. */ | |
151 | DP_STAT_MISS, /* Packets that did not match. */ | |
152 | DP_STAT_LOST, /* Packets not passed up to the client. */ | |
153 | DP_N_STATS | |
154 | }; | |
155 | ||
156 | /* Contained by struct dp_netdev's 'stats' member. */ | |
157 | struct dp_netdev_stats { | |
158 | struct ovs_mutex mutex; /* Protects 'n'. */ | |
159 | ||
160 | /* Indexed by DP_STAT_*, protected by 'mutex'. */ | |
161 | unsigned long long int n[DP_N_STATS] OVS_GUARDED; | |
162 | }; | |
163 | ||
164 | ||
72865317 BP |
165 | /* A port in a netdev-based datapath. */ |
166 | struct dp_netdev_port { | |
59e6d833 | 167 | struct cmap_node node; /* Node in dp_netdev's 'ports'. */ |
ff073a71 | 168 | odp_port_t port_no; |
72865317 | 169 | struct netdev *netdev; |
4b609110 | 170 | struct netdev_saved_flags *sf; |
55c955bd | 171 | struct netdev_rxq **rxq; |
b284085e | 172 | struct ovs_refcount ref_cnt; |
0cbfe35d | 173 | char *type; /* Port type as requested by user. */ |
72865317 BP |
174 | }; |
175 | ||
8cbf4f47 DDP |
176 | |
177 | /* Stores a miniflow */ | |
178 | ||
179 | /* There are fields in the flow structure that we never use. Therefore we can | |
180 | * save a few words of memory */ | |
e9fe2381 | 181 | #define NETDEV_KEY_BUF_SIZE_U32 (FLOW_U32S - MINI_N_INLINE \ |
8cbf4f47 DDP |
182 | - FLOW_U32_SIZE(regs) \ |
183 | - FLOW_U32_SIZE(metadata) \ | |
184 | ) | |
185 | struct netdev_flow_key { | |
186 | struct miniflow flow; | |
187 | uint32_t buf[NETDEV_KEY_BUF_SIZE_U32]; | |
711df373 | 188 | }; |
8cbf4f47 | 189 | |
8a4e3a85 BP |
190 | /* A flow in dp_netdev's 'flow_table'. |
191 | * | |
192 | * | |
193 | * Thread-safety | |
194 | * ============= | |
195 | * | |
196 | * Except near the beginning or ending of its lifespan, rule 'rule' belongs to | |
197 | * its dp_netdev's classifier. The text below calls this classifier 'cls'. | |
198 | * | |
199 | * Motivation | |
200 | * ---------- | |
201 | * | |
202 | * The thread safety rules described here for "struct dp_netdev_flow" are | |
203 | * motivated by two goals: | |
204 | * | |
205 | * - Prevent threads that read members of "struct dp_netdev_flow" from | |
206 | * reading bad data due to changes by some thread concurrently modifying | |
207 | * those members. | |
208 | * | |
209 | * - Prevent two threads making changes to members of a given "struct | |
210 | * dp_netdev_flow" from interfering with each other. | |
211 | * | |
212 | * | |
213 | * Rules | |
214 | * ----- | |
215 | * | |
ed79f89a DDP |
216 | * A flow 'flow' may be accessed without a risk of being freed during an RCU |
217 | * grace period. Code that needs to hold onto a flow for a while | |
218 | * should try incrementing 'flow->ref_cnt' with dp_netdev_flow_ref(). | |
8a4e3a85 BP |
219 | * |
220 | * 'flow->ref_cnt' protects 'flow' from being freed. It doesn't protect the | |
ed79f89a DDP |
221 | * flow from being deleted from 'cls' and it doesn't protect members of 'flow' |
222 | * from modification. | |
8a4e3a85 BP |
223 | * |
224 | * Some members, marked 'const', are immutable. Accessing other members | |
225 | * requires synchronization, as noted in more detail below. | |
226 | */ | |
72865317 | 227 | struct dp_netdev_flow { |
2c0ea78f | 228 | /* Packet classification. */ |
8a4e3a85 | 229 | const struct cls_rule cr; /* In owning dp_netdev's 'cls'. */ |
2c0ea78f | 230 | |
8a4e3a85 | 231 | /* Hash table index by unmasked flow. */ |
9f361d6b | 232 | const struct cmap_node node; /* In owning dp_netdev's 'flow_table'. */ |
8a4e3a85 | 233 | const struct flow flow; /* The flow that created this entry. */ |
72865317 | 234 | |
ed79f89a DDP |
235 | /* Number of references. |
236 | * The classifier owns one reference. | |
237 | * Any thread trying to keep a rule from being freed should hold its own | |
238 | * reference. */ | |
239 | struct ovs_refcount ref_cnt; | |
240 | ||
8a4e3a85 BP |
241 | /* Statistics. |
242 | * | |
243 | * Reading or writing these members requires 'mutex'. */ | |
679ba04c | 244 | struct ovsthread_stats stats; /* Contains "struct dp_netdev_flow_stats". */ |
8a4e3a85 | 245 | |
45c626a3 | 246 | /* Actions. */ |
61e7deb1 | 247 | OVSRCU_TYPE(struct dp_netdev_actions *) actions; |
72865317 BP |
248 | }; |
249 | ||
ed79f89a | 250 | static void dp_netdev_flow_unref(struct dp_netdev_flow *); |
8a4e3a85 | 251 | |
679ba04c BP |
252 | /* Contained by struct dp_netdev_flow's 'stats' member. */ |
253 | struct dp_netdev_flow_stats { | |
254 | struct ovs_mutex mutex; /* Guards all the other members. */ | |
255 | ||
256 | long long int used OVS_GUARDED; /* Last used time, in monotonic msecs. */ | |
257 | long long int packet_count OVS_GUARDED; /* Number of packets matched. */ | |
258 | long long int byte_count OVS_GUARDED; /* Number of bytes matched. */ | |
259 | uint16_t tcp_flags OVS_GUARDED; /* Bitwise-OR of seen tcp_flags values. */ | |
260 | }; | |
261 | ||
a84cb64a BP |
262 | /* A set of datapath actions within a "struct dp_netdev_flow". |
263 | * | |
264 | * | |
265 | * Thread-safety | |
266 | * ============= | |
267 | * | |
45c626a3 | 268 | * A struct dp_netdev_actions 'actions' is protected with RCU. */ |
a84cb64a | 269 | struct dp_netdev_actions { |
a84cb64a BP |
270 | /* These members are immutable: they do not change during the struct's |
271 | * lifetime. */ | |
272 | struct nlattr *actions; /* Sequence of OVS_ACTION_ATTR_* attributes. */ | |
273 | unsigned int size; /* Size of 'actions', in bytes. */ | |
274 | }; | |
275 | ||
276 | struct dp_netdev_actions *dp_netdev_actions_create(const struct nlattr *, | |
277 | size_t); | |
61e7deb1 BP |
278 | struct dp_netdev_actions *dp_netdev_flow_get_actions( |
279 | const struct dp_netdev_flow *); | |
280 | static void dp_netdev_actions_free(struct dp_netdev_actions *); | |
a84cb64a | 281 | |
e4cfed38 PS |
282 | /* PMD: Poll modes drivers. PMD accesses devices via polling to eliminate |
283 | * the performance overhead of interrupt processing. Therefore netdev can | |
284 | * not implement rx-wait for these devices. dpif-netdev needs to poll | |
285 | * these device to check for recv buffer. pmd-thread does polling for | |
286 | * devices assigned to itself thread. | |
287 | * | |
288 | * DPDK used PMD for accessing NIC. | |
289 | * | |
290 | * A thread that receives packets from PMD ports, looks them up in the flow | |
291 | * table, and executes the actions it finds. | |
292 | **/ | |
293 | struct pmd_thread { | |
6c3eee82 BP |
294 | struct dp_netdev *dp; |
295 | pthread_t thread; | |
e4cfed38 PS |
296 | int id; |
297 | atomic_uint change_seq; | |
6c3eee82 BP |
298 | }; |
299 | ||
84067a4c JR |
300 | #define PMD_INITIAL_SEQ 1 |
301 | ||
72865317 BP |
302 | /* Interface to netdev-based datapath. */ |
303 | struct dpif_netdev { | |
304 | struct dpif dpif; | |
305 | struct dp_netdev *dp; | |
d33ed218 | 306 | uint64_t last_port_seq; |
72865317 BP |
307 | }; |
308 | ||
8a4e3a85 | 309 | static int get_port_by_number(struct dp_netdev *dp, odp_port_t port_no, |
59e6d833 | 310 | struct dp_netdev_port **portp); |
8a4e3a85 | 311 | static int get_port_by_name(struct dp_netdev *dp, const char *devname, |
59e6d833 | 312 | struct dp_netdev_port **portp); |
8a4e3a85 BP |
313 | static void dp_netdev_free(struct dp_netdev *) |
314 | OVS_REQUIRES(dp_netdev_mutex); | |
72865317 | 315 | static void dp_netdev_flow_flush(struct dp_netdev *); |
8a4e3a85 BP |
316 | static int do_add_port(struct dp_netdev *dp, const char *devname, |
317 | const char *type, odp_port_t port_no) | |
59e6d833 | 318 | OVS_REQUIRES(dp->port_mutex); |
c40b890f | 319 | static void do_del_port(struct dp_netdev *dp, struct dp_netdev_port *) |
59e6d833 | 320 | OVS_REQUIRES(dp->port_mutex); |
614c4892 BP |
321 | static int dpif_netdev_open(const struct dpif_class *, const char *name, |
322 | bool create, struct dpif **); | |
8a4e3a85 | 323 | static void dp_netdev_execute_actions(struct dp_netdev *dp, |
8cbf4f47 DDP |
324 | struct dpif_packet **, int c, |
325 | bool may_steal, struct pkt_metadata *, | |
4edb9ae9 | 326 | const struct nlattr *actions, |
e4cfed38 | 327 | size_t actions_len); |
91088554 | 328 | static void dp_netdev_port_input(struct dp_netdev *dp, |
8cbf4f47 DDP |
329 | struct dpif_packet **packets, int cnt, |
330 | odp_port_t port_no); | |
e4cfed38 PS |
331 | |
332 | static void dp_netdev_set_pmd_threads(struct dp_netdev *, int n); | |
6b31e073 | 333 | static void dp_netdev_disable_upcall(struct dp_netdev *); |
72865317 BP |
334 | |
335 | static struct dpif_netdev * | |
336 | dpif_netdev_cast(const struct dpif *dpif) | |
337 | { | |
cb22974d | 338 | ovs_assert(dpif->dpif_class->open == dpif_netdev_open); |
72865317 BP |
339 | return CONTAINER_OF(dpif, struct dpif_netdev, dpif); |
340 | } | |
341 | ||
342 | static struct dp_netdev * | |
343 | get_dp_netdev(const struct dpif *dpif) | |
344 | { | |
345 | return dpif_netdev_cast(dpif)->dp; | |
346 | } | |
347 | ||
2197d7ab | 348 | static int |
2240af25 DDP |
349 | dpif_netdev_enumerate(struct sset *all_dps, |
350 | const struct dpif_class *dpif_class) | |
2197d7ab GL |
351 | { |
352 | struct shash_node *node; | |
353 | ||
97be1538 | 354 | ovs_mutex_lock(&dp_netdev_mutex); |
2197d7ab | 355 | SHASH_FOR_EACH(node, &dp_netdevs) { |
2240af25 DDP |
356 | struct dp_netdev *dp = node->data; |
357 | if (dpif_class != dp->class) { | |
358 | /* 'dp_netdevs' contains both "netdev" and "dummy" dpifs. | |
359 | * If the class doesn't match, skip this dpif. */ | |
360 | continue; | |
361 | } | |
2197d7ab GL |
362 | sset_add(all_dps, node->name); |
363 | } | |
97be1538 | 364 | ovs_mutex_unlock(&dp_netdev_mutex); |
5279f8fd | 365 | |
2197d7ab GL |
366 | return 0; |
367 | } | |
368 | ||
add90f6f EJ |
369 | static bool |
370 | dpif_netdev_class_is_dummy(const struct dpif_class *class) | |
371 | { | |
372 | return class != &dpif_netdev_class; | |
373 | } | |
374 | ||
0aeaabc8 JP |
375 | static const char * |
376 | dpif_netdev_port_open_type(const struct dpif_class *class, const char *type) | |
377 | { | |
378 | return strcmp(type, "internal") ? type | |
add90f6f | 379 | : dpif_netdev_class_is_dummy(class) ? "dummy" |
0aeaabc8 JP |
380 | : "tap"; |
381 | } | |
382 | ||
72865317 BP |
383 | static struct dpif * |
384 | create_dpif_netdev(struct dp_netdev *dp) | |
385 | { | |
462278db | 386 | uint16_t netflow_id = hash_string(dp->name, 0); |
72865317 | 387 | struct dpif_netdev *dpif; |
72865317 | 388 | |
6a8267c5 | 389 | ovs_refcount_ref(&dp->ref_cnt); |
72865317 | 390 | |
72865317 | 391 | dpif = xmalloc(sizeof *dpif); |
614c4892 | 392 | dpif_init(&dpif->dpif, dp->class, dp->name, netflow_id >> 8, netflow_id); |
72865317 | 393 | dpif->dp = dp; |
d33ed218 | 394 | dpif->last_port_seq = seq_read(dp->port_seq); |
72865317 BP |
395 | |
396 | return &dpif->dpif; | |
397 | } | |
398 | ||
4e022ec0 AW |
399 | /* Choose an unused, non-zero port number and return it on success. |
400 | * Return ODPP_NONE on failure. */ | |
401 | static odp_port_t | |
e44768b7 | 402 | choose_port(struct dp_netdev *dp, const char *name) |
59e6d833 | 403 | OVS_REQUIRES(dp->port_mutex) |
e44768b7 | 404 | { |
4e022ec0 | 405 | uint32_t port_no; |
e44768b7 JP |
406 | |
407 | if (dp->class != &dpif_netdev_class) { | |
408 | const char *p; | |
409 | int start_no = 0; | |
410 | ||
411 | /* If the port name begins with "br", start the number search at | |
412 | * 100 to make writing tests easier. */ | |
413 | if (!strncmp(name, "br", 2)) { | |
414 | start_no = 100; | |
415 | } | |
416 | ||
417 | /* If the port name contains a number, try to assign that port number. | |
418 | * This can make writing unit tests easier because port numbers are | |
419 | * predictable. */ | |
420 | for (p = name; *p != '\0'; p++) { | |
421 | if (isdigit((unsigned char) *p)) { | |
422 | port_no = start_no + strtol(p, NULL, 10); | |
ff073a71 BP |
423 | if (port_no > 0 && port_no != odp_to_u32(ODPP_NONE) |
424 | && !dp_netdev_lookup_port(dp, u32_to_odp(port_no))) { | |
4e022ec0 | 425 | return u32_to_odp(port_no); |
e44768b7 JP |
426 | } |
427 | break; | |
428 | } | |
429 | } | |
430 | } | |
431 | ||
ff073a71 BP |
432 | for (port_no = 1; port_no <= UINT16_MAX; port_no++) { |
433 | if (!dp_netdev_lookup_port(dp, u32_to_odp(port_no))) { | |
4e022ec0 | 434 | return u32_to_odp(port_no); |
e44768b7 JP |
435 | } |
436 | } | |
437 | ||
4e022ec0 | 438 | return ODPP_NONE; |
e44768b7 JP |
439 | } |
440 | ||
72865317 | 441 | static int |
614c4892 BP |
442 | create_dp_netdev(const char *name, const struct dpif_class *class, |
443 | struct dp_netdev **dpp) | |
8a4e3a85 | 444 | OVS_REQUIRES(dp_netdev_mutex) |
72865317 BP |
445 | { |
446 | struct dp_netdev *dp; | |
447 | int error; | |
72865317 | 448 | |
462278db | 449 | dp = xzalloc(sizeof *dp); |
8a4e3a85 BP |
450 | shash_add(&dp_netdevs, name, dp); |
451 | ||
452 | *CONST_CAST(const struct dpif_class **, &dp->class) = class; | |
453 | *CONST_CAST(const char **, &dp->name) = xstrdup(name); | |
6a8267c5 | 454 | ovs_refcount_init(&dp->ref_cnt); |
1a65ba85 | 455 | atomic_flag_clear(&dp->destroyed); |
8a4e3a85 BP |
456 | |
457 | ovs_mutex_init(&dp->flow_mutex); | |
458 | classifier_init(&dp->cls, NULL); | |
9f361d6b | 459 | cmap_init(&dp->flow_table); |
8a4e3a85 | 460 | |
51852a57 | 461 | ovsthread_stats_init(&dp->stats); |
ed27e010 | 462 | |
59e6d833 BP |
463 | ovs_mutex_init(&dp->port_mutex); |
464 | cmap_init(&dp->ports); | |
d33ed218 | 465 | dp->port_seq = seq_create(); |
6c3eee82 | 466 | latch_init(&dp->exit_latch); |
6b31e073 RW |
467 | fat_rwlock_init(&dp->upcall_rwlock); |
468 | ||
469 | /* Disable upcalls by default. */ | |
470 | dp_netdev_disable_upcall(dp); | |
623540e4 | 471 | dp->upcall_aux = NULL; |
6b31e073 | 472 | dp->upcall_cb = NULL; |
e44768b7 | 473 | |
59e6d833 | 474 | ovs_mutex_lock(&dp->port_mutex); |
4e022ec0 | 475 | error = do_add_port(dp, name, "internal", ODPP_LOCAL); |
59e6d833 | 476 | ovs_mutex_unlock(&dp->port_mutex); |
72865317 BP |
477 | if (error) { |
478 | dp_netdev_free(dp); | |
462278db | 479 | return error; |
72865317 BP |
480 | } |
481 | ||
462278db | 482 | *dpp = dp; |
72865317 BP |
483 | return 0; |
484 | } | |
485 | ||
486 | static int | |
614c4892 | 487 | dpif_netdev_open(const struct dpif_class *class, const char *name, |
4a387741 | 488 | bool create, struct dpif **dpifp) |
72865317 | 489 | { |
462278db | 490 | struct dp_netdev *dp; |
5279f8fd | 491 | int error; |
462278db | 492 | |
97be1538 | 493 | ovs_mutex_lock(&dp_netdev_mutex); |
462278db BP |
494 | dp = shash_find_data(&dp_netdevs, name); |
495 | if (!dp) { | |
5279f8fd | 496 | error = create ? create_dp_netdev(name, class, &dp) : ENODEV; |
72865317 | 497 | } else { |
5279f8fd BP |
498 | error = (dp->class != class ? EINVAL |
499 | : create ? EEXIST | |
500 | : 0); | |
501 | } | |
502 | if (!error) { | |
503 | *dpifp = create_dpif_netdev(dp); | |
6b31e073 | 504 | dp->dpif = *dpifp; |
72865317 | 505 | } |
97be1538 | 506 | ovs_mutex_unlock(&dp_netdev_mutex); |
462278db | 507 | |
5279f8fd | 508 | return error; |
72865317 BP |
509 | } |
510 | ||
8a4e3a85 BP |
511 | /* Requires dp_netdev_mutex so that we can't get a new reference to 'dp' |
512 | * through the 'dp_netdevs' shash while freeing 'dp'. */ | |
1ba530f4 BP |
513 | static void |
514 | dp_netdev_free(struct dp_netdev *dp) | |
8a4e3a85 | 515 | OVS_REQUIRES(dp_netdev_mutex) |
1ba530f4 | 516 | { |
59e6d833 | 517 | struct dp_netdev_port *port; |
51852a57 BP |
518 | struct dp_netdev_stats *bucket; |
519 | int i; | |
4ad28026 | 520 | |
8a4e3a85 BP |
521 | shash_find_and_delete(&dp_netdevs, dp->name); |
522 | ||
e4cfed38 PS |
523 | dp_netdev_set_pmd_threads(dp, 0); |
524 | free(dp->pmd_threads); | |
6c3eee82 | 525 | |
1ba530f4 | 526 | dp_netdev_flow_flush(dp); |
59e6d833 | 527 | ovs_mutex_lock(&dp->port_mutex); |
a532e683 | 528 | CMAP_FOR_EACH (port, node, &dp->ports) { |
c40b890f | 529 | do_del_port(dp, port); |
1ba530f4 | 530 | } |
59e6d833 | 531 | ovs_mutex_unlock(&dp->port_mutex); |
51852a57 BP |
532 | |
533 | OVSTHREAD_STATS_FOR_EACH_BUCKET (bucket, i, &dp->stats) { | |
534 | ovs_mutex_destroy(&bucket->mutex); | |
535 | free_cacheline(bucket); | |
536 | } | |
537 | ovsthread_stats_destroy(&dp->stats); | |
f5126b57 | 538 | |
2c0ea78f | 539 | classifier_destroy(&dp->cls); |
9f361d6b | 540 | cmap_destroy(&dp->flow_table); |
8a4e3a85 | 541 | ovs_mutex_destroy(&dp->flow_mutex); |
d33ed218 | 542 | seq_destroy(dp->port_seq); |
59e6d833 | 543 | cmap_destroy(&dp->ports); |
6b31e073 | 544 | fat_rwlock_destroy(&dp->upcall_rwlock); |
6c3eee82 | 545 | latch_destroy(&dp->exit_latch); |
8a4e3a85 | 546 | free(CONST_CAST(char *, dp->name)); |
72865317 BP |
547 | free(dp); |
548 | } | |
549 | ||
8a4e3a85 BP |
550 | static void |
551 | dp_netdev_unref(struct dp_netdev *dp) | |
552 | { | |
553 | if (dp) { | |
554 | /* Take dp_netdev_mutex so that, if dp->ref_cnt falls to zero, we can't | |
555 | * get a new reference to 'dp' through the 'dp_netdevs' shash. */ | |
556 | ovs_mutex_lock(&dp_netdev_mutex); | |
24f83812 | 557 | if (ovs_refcount_unref_relaxed(&dp->ref_cnt) == 1) { |
8a4e3a85 BP |
558 | dp_netdev_free(dp); |
559 | } | |
560 | ovs_mutex_unlock(&dp_netdev_mutex); | |
561 | } | |
562 | } | |
563 | ||
72865317 BP |
564 | static void |
565 | dpif_netdev_close(struct dpif *dpif) | |
566 | { | |
567 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
5279f8fd | 568 | |
8a4e3a85 | 569 | dp_netdev_unref(dp); |
72865317 BP |
570 | free(dpif); |
571 | } | |
572 | ||
573 | static int | |
7dab847a | 574 | dpif_netdev_destroy(struct dpif *dpif) |
72865317 BP |
575 | { |
576 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
5279f8fd | 577 | |
6a8267c5 | 578 | if (!atomic_flag_test_and_set(&dp->destroyed)) { |
24f83812 | 579 | if (ovs_refcount_unref_relaxed(&dp->ref_cnt) == 1) { |
6a8267c5 BP |
580 | /* Can't happen: 'dpif' still owns a reference to 'dp'. */ |
581 | OVS_NOT_REACHED(); | |
582 | } | |
583 | } | |
5279f8fd | 584 | |
72865317 BP |
585 | return 0; |
586 | } | |
587 | ||
588 | static int | |
a8d9304d | 589 | dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats) |
72865317 BP |
590 | { |
591 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
51852a57 BP |
592 | struct dp_netdev_stats *bucket; |
593 | size_t i; | |
5279f8fd | 594 | |
9f361d6b | 595 | stats->n_flows = cmap_count(&dp->flow_table); |
8a4e3a85 | 596 | |
51852a57 BP |
597 | stats->n_hit = stats->n_missed = stats->n_lost = 0; |
598 | OVSTHREAD_STATS_FOR_EACH_BUCKET (bucket, i, &dp->stats) { | |
599 | ovs_mutex_lock(&bucket->mutex); | |
600 | stats->n_hit += bucket->n[DP_STAT_HIT]; | |
601 | stats->n_missed += bucket->n[DP_STAT_MISS]; | |
602 | stats->n_lost += bucket->n[DP_STAT_LOST]; | |
603 | ovs_mutex_unlock(&bucket->mutex); | |
604 | } | |
1ce3fa06 | 605 | stats->n_masks = UINT32_MAX; |
847108dc | 606 | stats->n_mask_hit = UINT64_MAX; |
5279f8fd | 607 | |
72865317 BP |
608 | return 0; |
609 | } | |
610 | ||
e4cfed38 PS |
611 | static void |
612 | dp_netdev_reload_pmd_threads(struct dp_netdev *dp) | |
613 | { | |
614 | int i; | |
615 | ||
616 | for (i = 0; i < dp->n_pmd_threads; i++) { | |
617 | struct pmd_thread *f = &dp->pmd_threads[i]; | |
84067a4c | 618 | int old_seq; |
e4cfed38 | 619 | |
91a96379 | 620 | atomic_add_relaxed(&f->change_seq, 1, &old_seq); |
84067a4c | 621 | } |
e4cfed38 PS |
622 | } |
623 | ||
59e6d833 BP |
624 | static uint32_t |
625 | hash_port_no(odp_port_t port_no) | |
626 | { | |
627 | return hash_int(odp_to_u32(port_no), 0); | |
628 | } | |
629 | ||
72865317 | 630 | static int |
c3827f61 | 631 | do_add_port(struct dp_netdev *dp, const char *devname, const char *type, |
4e022ec0 | 632 | odp_port_t port_no) |
59e6d833 | 633 | OVS_REQUIRES(dp->port_mutex) |
72865317 | 634 | { |
4b609110 | 635 | struct netdev_saved_flags *sf; |
72865317 BP |
636 | struct dp_netdev_port *port; |
637 | struct netdev *netdev; | |
2499a8ce | 638 | enum netdev_flags flags; |
0cbfe35d | 639 | const char *open_type; |
72865317 | 640 | int error; |
55c955bd | 641 | int i; |
72865317 BP |
642 | |
643 | /* XXX reject devices already in some dp_netdev. */ | |
644 | ||
645 | /* Open and validate network device. */ | |
0aeaabc8 | 646 | open_type = dpif_netdev_port_open_type(dp->class, type); |
0cbfe35d | 647 | error = netdev_open(devname, open_type, &netdev); |
72865317 BP |
648 | if (error) { |
649 | return error; | |
650 | } | |
72865317 BP |
651 | /* XXX reject non-Ethernet devices */ |
652 | ||
2499a8ce AC |
653 | netdev_get_flags(netdev, &flags); |
654 | if (flags & NETDEV_LOOPBACK) { | |
655 | VLOG_ERR("%s: cannot add a loopback device", devname); | |
656 | netdev_close(netdev); | |
657 | return EINVAL; | |
658 | } | |
659 | ||
e4cfed38 PS |
660 | port = xzalloc(sizeof *port); |
661 | port->port_no = port_no; | |
662 | port->netdev = netdev; | |
55c955bd | 663 | port->rxq = xmalloc(sizeof *port->rxq * netdev_n_rxq(netdev)); |
e4cfed38 | 664 | port->type = xstrdup(type); |
55c955bd PS |
665 | for (i = 0; i < netdev_n_rxq(netdev); i++) { |
666 | error = netdev_rxq_open(netdev, &port->rxq[i], i); | |
667 | if (error | |
668 | && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) { | |
669 | VLOG_ERR("%s: cannot receive packets on this network device (%s)", | |
670 | devname, ovs_strerror(errno)); | |
671 | netdev_close(netdev); | |
16bea12c TG |
672 | free(port->type); |
673 | free(port->rxq); | |
674 | free(port); | |
55c955bd PS |
675 | return error; |
676 | } | |
7b6b0ef4 BP |
677 | } |
678 | ||
4b609110 | 679 | error = netdev_turn_flags_on(netdev, NETDEV_PROMISC, &sf); |
72865317 | 680 | if (error) { |
55c955bd PS |
681 | for (i = 0; i < netdev_n_rxq(netdev); i++) { |
682 | netdev_rxq_close(port->rxq[i]); | |
683 | } | |
72865317 | 684 | netdev_close(netdev); |
16bea12c | 685 | free(port->type); |
f7791740 | 686 | free(port->rxq); |
e4cfed38 | 687 | free(port); |
72865317 BP |
688 | return error; |
689 | } | |
4b609110 | 690 | port->sf = sf; |
e4cfed38 PS |
691 | |
692 | if (netdev_is_pmd(netdev)) { | |
693 | dp->pmd_count++; | |
db73f716 | 694 | dp_netdev_set_pmd_threads(dp, NR_PMD_THREADS); |
e4cfed38 PS |
695 | dp_netdev_reload_pmd_threads(dp); |
696 | } | |
697 | ovs_refcount_init(&port->ref_cnt); | |
72865317 | 698 | |
59e6d833 | 699 | cmap_insert(&dp->ports, &port->node, hash_port_no(port_no)); |
d33ed218 | 700 | seq_change(dp->port_seq); |
72865317 BP |
701 | |
702 | return 0; | |
703 | } | |
704 | ||
247527db BP |
705 | static int |
706 | dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev, | |
4e022ec0 | 707 | odp_port_t *port_nop) |
247527db BP |
708 | { |
709 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
3aa30359 BP |
710 | char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; |
711 | const char *dpif_port; | |
4e022ec0 | 712 | odp_port_t port_no; |
5279f8fd | 713 | int error; |
247527db | 714 | |
59e6d833 | 715 | ovs_mutex_lock(&dp->port_mutex); |
3aa30359 | 716 | dpif_port = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); |
4e022ec0 | 717 | if (*port_nop != ODPP_NONE) { |
ff073a71 BP |
718 | port_no = *port_nop; |
719 | error = dp_netdev_lookup_port(dp, *port_nop) ? EBUSY : 0; | |
232dfa4a | 720 | } else { |
3aa30359 | 721 | port_no = choose_port(dp, dpif_port); |
5279f8fd | 722 | error = port_no == ODPP_NONE ? EFBIG : 0; |
232dfa4a | 723 | } |
5279f8fd | 724 | if (!error) { |
247527db | 725 | *port_nop = port_no; |
5279f8fd | 726 | error = do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no); |
247527db | 727 | } |
59e6d833 | 728 | ovs_mutex_unlock(&dp->port_mutex); |
5279f8fd BP |
729 | |
730 | return error; | |
72865317 BP |
731 | } |
732 | ||
733 | static int | |
4e022ec0 | 734 | dpif_netdev_port_del(struct dpif *dpif, odp_port_t port_no) |
72865317 BP |
735 | { |
736 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
5279f8fd BP |
737 | int error; |
738 | ||
59e6d833 | 739 | ovs_mutex_lock(&dp->port_mutex); |
c40b890f BP |
740 | if (port_no == ODPP_LOCAL) { |
741 | error = EINVAL; | |
742 | } else { | |
743 | struct dp_netdev_port *port; | |
744 | ||
745 | error = get_port_by_number(dp, port_no, &port); | |
746 | if (!error) { | |
747 | do_del_port(dp, port); | |
748 | } | |
749 | } | |
59e6d833 | 750 | ovs_mutex_unlock(&dp->port_mutex); |
5279f8fd BP |
751 | |
752 | return error; | |
72865317 BP |
753 | } |
754 | ||
755 | static bool | |
4e022ec0 | 756 | is_valid_port_number(odp_port_t port_no) |
72865317 | 757 | { |
ff073a71 BP |
758 | return port_no != ODPP_NONE; |
759 | } | |
760 | ||
761 | static struct dp_netdev_port * | |
762 | dp_netdev_lookup_port(const struct dp_netdev *dp, odp_port_t port_no) | |
763 | { | |
764 | struct dp_netdev_port *port; | |
765 | ||
59e6d833 | 766 | CMAP_FOR_EACH_WITH_HASH (port, node, hash_port_no(port_no), &dp->ports) { |
ff073a71 BP |
767 | if (port->port_no == port_no) { |
768 | return port; | |
769 | } | |
770 | } | |
771 | return NULL; | |
72865317 BP |
772 | } |
773 | ||
774 | static int | |
775 | get_port_by_number(struct dp_netdev *dp, | |
4e022ec0 | 776 | odp_port_t port_no, struct dp_netdev_port **portp) |
72865317 BP |
777 | { |
778 | if (!is_valid_port_number(port_no)) { | |
779 | *portp = NULL; | |
780 | return EINVAL; | |
781 | } else { | |
ff073a71 | 782 | *portp = dp_netdev_lookup_port(dp, port_no); |
72865317 BP |
783 | return *portp ? 0 : ENOENT; |
784 | } | |
785 | } | |
786 | ||
b284085e PS |
787 | static void |
788 | port_ref(struct dp_netdev_port *port) | |
789 | { | |
790 | if (port) { | |
791 | ovs_refcount_ref(&port->ref_cnt); | |
792 | } | |
793 | } | |
794 | ||
795 | static void | |
59e6d833 | 796 | port_destroy__(struct dp_netdev_port *port) |
b284085e | 797 | { |
98de6beb | 798 | int n_rxq = netdev_n_rxq(port->netdev); |
59e6d833 | 799 | int i; |
55c955bd | 800 | |
59e6d833 BP |
801 | netdev_close(port->netdev); |
802 | netdev_restore_flags(port->sf); | |
55c955bd | 803 | |
59e6d833 BP |
804 | for (i = 0; i < n_rxq; i++) { |
805 | netdev_rxq_close(port->rxq[i]); | |
806 | } | |
807 | free(port->rxq); | |
808 | free(port->type); | |
809 | free(port); | |
810 | } | |
811 | ||
812 | static void | |
813 | port_unref(struct dp_netdev_port *port) | |
814 | { | |
24f83812 | 815 | if (port && ovs_refcount_unref_relaxed(&port->ref_cnt) == 1) { |
59e6d833 | 816 | ovsrcu_postpone(port_destroy__, port); |
b284085e PS |
817 | } |
818 | } | |
819 | ||
72865317 BP |
820 | static int |
821 | get_port_by_name(struct dp_netdev *dp, | |
822 | const char *devname, struct dp_netdev_port **portp) | |
59e6d833 | 823 | OVS_REQUIRES(dp->port_mutex) |
72865317 BP |
824 | { |
825 | struct dp_netdev_port *port; | |
826 | ||
a532e683 | 827 | CMAP_FOR_EACH (port, node, &dp->ports) { |
3efb6063 | 828 | if (!strcmp(netdev_get_name(port->netdev), devname)) { |
72865317 BP |
829 | *portp = port; |
830 | return 0; | |
831 | } | |
832 | } | |
833 | return ENOENT; | |
834 | } | |
835 | ||
c40b890f BP |
836 | static void |
837 | do_del_port(struct dp_netdev *dp, struct dp_netdev_port *port) | |
59e6d833 | 838 | OVS_REQUIRES(dp->port_mutex) |
72865317 | 839 | { |
c40b890f | 840 | cmap_remove(&dp->ports, &port->node, hash_odp_port(port->port_no)); |
d33ed218 | 841 | seq_change(dp->port_seq); |
e4cfed38 PS |
842 | if (netdev_is_pmd(port->netdev)) { |
843 | dp_netdev_reload_pmd_threads(dp); | |
844 | } | |
72865317 | 845 | |
b284085e | 846 | port_unref(port); |
72865317 BP |
847 | } |
848 | ||
849 | static void | |
4c738a8d BP |
850 | answer_port_query(const struct dp_netdev_port *port, |
851 | struct dpif_port *dpif_port) | |
72865317 | 852 | { |
3efb6063 | 853 | dpif_port->name = xstrdup(netdev_get_name(port->netdev)); |
0cbfe35d | 854 | dpif_port->type = xstrdup(port->type); |
4c738a8d | 855 | dpif_port->port_no = port->port_no; |
72865317 BP |
856 | } |
857 | ||
858 | static int | |
4e022ec0 | 859 | dpif_netdev_port_query_by_number(const struct dpif *dpif, odp_port_t port_no, |
4c738a8d | 860 | struct dpif_port *dpif_port) |
72865317 BP |
861 | { |
862 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
863 | struct dp_netdev_port *port; | |
864 | int error; | |
865 | ||
866 | error = get_port_by_number(dp, port_no, &port); | |
4afba28d | 867 | if (!error && dpif_port) { |
4c738a8d | 868 | answer_port_query(port, dpif_port); |
72865317 | 869 | } |
5279f8fd | 870 | |
72865317 BP |
871 | return error; |
872 | } | |
873 | ||
874 | static int | |
875 | dpif_netdev_port_query_by_name(const struct dpif *dpif, const char *devname, | |
4c738a8d | 876 | struct dpif_port *dpif_port) |
72865317 BP |
877 | { |
878 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
879 | struct dp_netdev_port *port; | |
880 | int error; | |
881 | ||
59e6d833 | 882 | ovs_mutex_lock(&dp->port_mutex); |
72865317 | 883 | error = get_port_by_name(dp, devname, &port); |
4afba28d | 884 | if (!error && dpif_port) { |
4c738a8d | 885 | answer_port_query(port, dpif_port); |
72865317 | 886 | } |
59e6d833 | 887 | ovs_mutex_unlock(&dp->port_mutex); |
5279f8fd | 888 | |
72865317 BP |
889 | return error; |
890 | } | |
891 | ||
61e7deb1 BP |
892 | static void |
893 | dp_netdev_flow_free(struct dp_netdev_flow *flow) | |
894 | { | |
895 | struct dp_netdev_flow_stats *bucket; | |
896 | size_t i; | |
897 | ||
898 | OVSTHREAD_STATS_FOR_EACH_BUCKET (bucket, i, &flow->stats) { | |
899 | ovs_mutex_destroy(&bucket->mutex); | |
900 | free_cacheline(bucket); | |
901 | } | |
902 | ovsthread_stats_destroy(&flow->stats); | |
903 | ||
904 | cls_rule_destroy(CONST_CAST(struct cls_rule *, &flow->cr)); | |
905 | dp_netdev_actions_free(dp_netdev_flow_get_actions(flow)); | |
61e7deb1 BP |
906 | free(flow); |
907 | } | |
908 | ||
ed79f89a DDP |
909 | static void dp_netdev_flow_unref(struct dp_netdev_flow *flow) |
910 | { | |
911 | if (ovs_refcount_unref_relaxed(&flow->ref_cnt) == 1) { | |
912 | ovsrcu_postpone(dp_netdev_flow_free, flow); | |
913 | } | |
914 | } | |
915 | ||
72865317 | 916 | static void |
8a4e3a85 | 917 | dp_netdev_remove_flow(struct dp_netdev *dp, struct dp_netdev_flow *flow) |
8a4e3a85 | 918 | OVS_REQUIRES(dp->flow_mutex) |
72865317 | 919 | { |
8a4e3a85 | 920 | struct cls_rule *cr = CONST_CAST(struct cls_rule *, &flow->cr); |
9f361d6b | 921 | struct cmap_node *node = CONST_CAST(struct cmap_node *, &flow->node); |
2c0ea78f | 922 | |
8a4e3a85 | 923 | classifier_remove(&dp->cls, cr); |
9f361d6b | 924 | cmap_remove(&dp->flow_table, node, flow_hash(&flow->flow, 0)); |
ed79f89a DDP |
925 | |
926 | dp_netdev_flow_unref(flow); | |
72865317 BP |
927 | } |
928 | ||
929 | static void | |
930 | dp_netdev_flow_flush(struct dp_netdev *dp) | |
931 | { | |
78c8df12 | 932 | struct dp_netdev_flow *netdev_flow; |
72865317 | 933 | |
8a4e3a85 | 934 | ovs_mutex_lock(&dp->flow_mutex); |
6bc3bb82 | 935 | CMAP_FOR_EACH (netdev_flow, node, &dp->flow_table) { |
8a4e3a85 | 936 | dp_netdev_remove_flow(dp, netdev_flow); |
72865317 | 937 | } |
8a4e3a85 | 938 | ovs_mutex_unlock(&dp->flow_mutex); |
72865317 BP |
939 | } |
940 | ||
941 | static int | |
942 | dpif_netdev_flow_flush(struct dpif *dpif) | |
943 | { | |
944 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
5279f8fd | 945 | |
72865317 BP |
946 | dp_netdev_flow_flush(dp); |
947 | return 0; | |
948 | } | |
949 | ||
b0ec0f27 | 950 | struct dp_netdev_port_state { |
59e6d833 | 951 | struct cmap_position position; |
4c738a8d | 952 | char *name; |
b0ec0f27 BP |
953 | }; |
954 | ||
955 | static int | |
956 | dpif_netdev_port_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep) | |
957 | { | |
958 | *statep = xzalloc(sizeof(struct dp_netdev_port_state)); | |
959 | return 0; | |
960 | } | |
961 | ||
72865317 | 962 | static int |
b0ec0f27 | 963 | dpif_netdev_port_dump_next(const struct dpif *dpif, void *state_, |
4c738a8d | 964 | struct dpif_port *dpif_port) |
72865317 | 965 | { |
b0ec0f27 | 966 | struct dp_netdev_port_state *state = state_; |
72865317 | 967 | struct dp_netdev *dp = get_dp_netdev(dpif); |
59e6d833 | 968 | struct cmap_node *node; |
ff073a71 | 969 | int retval; |
72865317 | 970 | |
59e6d833 | 971 | node = cmap_next_position(&dp->ports, &state->position); |
ff073a71 BP |
972 | if (node) { |
973 | struct dp_netdev_port *port; | |
5279f8fd | 974 | |
ff073a71 BP |
975 | port = CONTAINER_OF(node, struct dp_netdev_port, node); |
976 | ||
977 | free(state->name); | |
978 | state->name = xstrdup(netdev_get_name(port->netdev)); | |
979 | dpif_port->name = state->name; | |
980 | dpif_port->type = port->type; | |
981 | dpif_port->port_no = port->port_no; | |
982 | ||
983 | retval = 0; | |
984 | } else { | |
985 | retval = EOF; | |
72865317 | 986 | } |
5279f8fd | 987 | |
ff073a71 | 988 | return retval; |
b0ec0f27 BP |
989 | } |
990 | ||
991 | static int | |
4c738a8d | 992 | dpif_netdev_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_) |
b0ec0f27 | 993 | { |
4c738a8d BP |
994 | struct dp_netdev_port_state *state = state_; |
995 | free(state->name); | |
b0ec0f27 BP |
996 | free(state); |
997 | return 0; | |
72865317 BP |
998 | } |
999 | ||
1000 | static int | |
67a4917b | 1001 | dpif_netdev_port_poll(const struct dpif *dpif_, char **devnamep OVS_UNUSED) |
72865317 BP |
1002 | { |
1003 | struct dpif_netdev *dpif = dpif_netdev_cast(dpif_); | |
d33ed218 | 1004 | uint64_t new_port_seq; |
5279f8fd BP |
1005 | int error; |
1006 | ||
d33ed218 BP |
1007 | new_port_seq = seq_read(dpif->dp->port_seq); |
1008 | if (dpif->last_port_seq != new_port_seq) { | |
1009 | dpif->last_port_seq = new_port_seq; | |
5279f8fd | 1010 | error = ENOBUFS; |
72865317 | 1011 | } else { |
5279f8fd | 1012 | error = EAGAIN; |
72865317 | 1013 | } |
5279f8fd BP |
1014 | |
1015 | return error; | |
72865317 BP |
1016 | } |
1017 | ||
1018 | static void | |
1019 | dpif_netdev_port_poll_wait(const struct dpif *dpif_) | |
1020 | { | |
1021 | struct dpif_netdev *dpif = dpif_netdev_cast(dpif_); | |
5279f8fd | 1022 | |
d33ed218 | 1023 | seq_wait(dpif->dp->port_seq, dpif->last_port_seq); |
8a4e3a85 BP |
1024 | } |
1025 | ||
1026 | static struct dp_netdev_flow * | |
1027 | dp_netdev_flow_cast(const struct cls_rule *cr) | |
1028 | { | |
1029 | return cr ? CONTAINER_OF(cr, struct dp_netdev_flow, cr) : NULL; | |
72865317 BP |
1030 | } |
1031 | ||
72865317 | 1032 | static struct dp_netdev_flow * |
4f150744 | 1033 | dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct miniflow *key) |
2c0ea78f | 1034 | { |
8a4e3a85 | 1035 | struct dp_netdev_flow *netdev_flow; |
4f150744 | 1036 | struct cls_rule *rule; |
2c0ea78f | 1037 | |
b7648634 | 1038 | classifier_lookup_miniflow_batch(&dp->cls, &key, &rule, 1); |
4f150744 | 1039 | netdev_flow = dp_netdev_flow_cast(rule); |
2c0ea78f | 1040 | |
8a4e3a85 | 1041 | return netdev_flow; |
2c0ea78f GS |
1042 | } |
1043 | ||
1044 | static struct dp_netdev_flow * | |
1045 | dp_netdev_find_flow(const struct dp_netdev *dp, const struct flow *flow) | |
72865317 | 1046 | { |
1763b4b8 | 1047 | struct dp_netdev_flow *netdev_flow; |
72865317 | 1048 | |
9f361d6b | 1049 | CMAP_FOR_EACH_WITH_HASH (netdev_flow, node, flow_hash(flow, 0), |
1763b4b8 | 1050 | &dp->flow_table) { |
2c0ea78f | 1051 | if (flow_equal(&netdev_flow->flow, flow)) { |
61e7deb1 | 1052 | return netdev_flow; |
72865317 BP |
1053 | } |
1054 | } | |
8a4e3a85 | 1055 | |
72865317 BP |
1056 | return NULL; |
1057 | } | |
1058 | ||
1059 | static void | |
6fe09f8c | 1060 | get_dpif_flow_stats(const struct dp_netdev_flow *netdev_flow, |
1763b4b8 | 1061 | struct dpif_flow_stats *stats) |
feebdea2 | 1062 | { |
679ba04c BP |
1063 | struct dp_netdev_flow_stats *bucket; |
1064 | size_t i; | |
1065 | ||
1066 | memset(stats, 0, sizeof *stats); | |
1067 | OVSTHREAD_STATS_FOR_EACH_BUCKET (bucket, i, &netdev_flow->stats) { | |
1068 | ovs_mutex_lock(&bucket->mutex); | |
1069 | stats->n_packets += bucket->packet_count; | |
1070 | stats->n_bytes += bucket->byte_count; | |
1071 | stats->used = MAX(stats->used, bucket->used); | |
1072 | stats->tcp_flags |= bucket->tcp_flags; | |
1073 | ovs_mutex_unlock(&bucket->mutex); | |
1074 | } | |
72865317 BP |
1075 | } |
1076 | ||
6fe09f8c JS |
1077 | static void |
1078 | dp_netdev_flow_to_dpif_flow(const struct dp_netdev_flow *netdev_flow, | |
1079 | struct ofpbuf *buffer, struct dpif_flow *flow) | |
1080 | { | |
1081 | struct flow_wildcards wc; | |
1082 | struct dp_netdev_actions *actions; | |
1083 | ||
1084 | minimask_expand(&netdev_flow->cr.match.mask, &wc); | |
1085 | odp_flow_key_from_mask(buffer, &wc.masks, &netdev_flow->flow, | |
1086 | odp_to_u32(wc.masks.in_port.odp_port), | |
1087 | SIZE_MAX, true); | |
1088 | flow->mask = ofpbuf_data(buffer); | |
1089 | flow->mask_len = ofpbuf_size(buffer); | |
1090 | ||
1091 | actions = dp_netdev_flow_get_actions(netdev_flow); | |
1092 | flow->actions = actions->actions; | |
1093 | flow->actions_len = actions->size; | |
1094 | ||
1095 | get_dpif_flow_stats(netdev_flow, &flow->stats); | |
1096 | } | |
1097 | ||
36956a7d | 1098 | static int |
8c301900 JR |
1099 | dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len, |
1100 | const struct nlattr *mask_key, | |
1101 | uint32_t mask_key_len, const struct flow *flow, | |
1102 | struct flow *mask) | |
1103 | { | |
1104 | if (mask_key_len) { | |
80e44883 BP |
1105 | enum odp_key_fitness fitness; |
1106 | ||
1107 | fitness = odp_flow_key_to_mask(mask_key, mask_key_len, mask, flow); | |
1108 | if (fitness) { | |
8c301900 JR |
1109 | /* This should not happen: it indicates that |
1110 | * odp_flow_key_from_mask() and odp_flow_key_to_mask() | |
1111 | * disagree on the acceptable form of a mask. Log the problem | |
1112 | * as an error, with enough details to enable debugging. */ | |
1113 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
1114 | ||
1115 | if (!VLOG_DROP_ERR(&rl)) { | |
1116 | struct ds s; | |
1117 | ||
1118 | ds_init(&s); | |
1119 | odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s, | |
1120 | true); | |
80e44883 BP |
1121 | VLOG_ERR("internal error parsing flow mask %s (%s)", |
1122 | ds_cstr(&s), odp_key_fitness_to_string(fitness)); | |
8c301900 JR |
1123 | ds_destroy(&s); |
1124 | } | |
1125 | ||
1126 | return EINVAL; | |
1127 | } | |
8c301900 JR |
1128 | } else { |
1129 | enum mf_field_id id; | |
1130 | /* No mask key, unwildcard everything except fields whose | |
1131 | * prerequisities are not met. */ | |
1132 | memset(mask, 0x0, sizeof *mask); | |
1133 | ||
1134 | for (id = 0; id < MFF_N_IDS; ++id) { | |
1135 | /* Skip registers and metadata. */ | |
1136 | if (!(id >= MFF_REG0 && id < MFF_REG0 + FLOW_N_REGS) | |
1137 | && id != MFF_METADATA) { | |
1138 | const struct mf_field *mf = mf_from_id(id); | |
1139 | if (mf_are_prereqs_ok(mf, flow)) { | |
1140 | mf_mask_field(mf, mask); | |
1141 | } | |
1142 | } | |
1143 | } | |
1144 | } | |
1145 | ||
f3f750e5 BP |
1146 | /* Force unwildcard the in_port. |
1147 | * | |
1148 | * We need to do this even in the case where we unwildcard "everything" | |
1149 | * above because "everything" only includes the 16-bit OpenFlow port number | |
1150 | * mask->in_port.ofp_port, which only covers half of the 32-bit datapath | |
1151 | * port number mask->in_port.odp_port. */ | |
1152 | mask->in_port.odp_port = u32_to_odp(UINT32_MAX); | |
1153 | ||
8c301900 JR |
1154 | return 0; |
1155 | } | |
1156 | ||
1157 | static int | |
1158 | dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len, | |
1159 | struct flow *flow) | |
36956a7d | 1160 | { |
586ddea5 BP |
1161 | odp_port_t in_port; |
1162 | ||
8c301900 | 1163 | if (odp_flow_key_to_flow(key, key_len, flow)) { |
36956a7d | 1164 | /* This should not happen: it indicates that odp_flow_key_from_flow() |
8c301900 JR |
1165 | * and odp_flow_key_to_flow() disagree on the acceptable form of a |
1166 | * flow. Log the problem as an error, with enough details to enable | |
1167 | * debugging. */ | |
36956a7d BP |
1168 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
1169 | ||
1170 | if (!VLOG_DROP_ERR(&rl)) { | |
1171 | struct ds s; | |
1172 | ||
1173 | ds_init(&s); | |
8c301900 | 1174 | odp_flow_format(key, key_len, NULL, 0, NULL, &s, true); |
36956a7d BP |
1175 | VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s)); |
1176 | ds_destroy(&s); | |
1177 | } | |
1178 | ||
1179 | return EINVAL; | |
1180 | } | |
1181 | ||
586ddea5 BP |
1182 | in_port = flow->in_port.odp_port; |
1183 | if (!is_valid_port_number(in_port) && in_port != ODPP_NONE) { | |
18886b60 BP |
1184 | return EINVAL; |
1185 | } | |
1186 | ||
36956a7d BP |
1187 | return 0; |
1188 | } | |
1189 | ||
72865317 | 1190 | static int |
6fe09f8c | 1191 | dpif_netdev_flow_get(const struct dpif *dpif, const struct dpif_flow_get *get) |
72865317 BP |
1192 | { |
1193 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
1763b4b8 | 1194 | struct dp_netdev_flow *netdev_flow; |
bc4a05c6 BP |
1195 | struct flow key; |
1196 | int error; | |
36956a7d | 1197 | |
6fe09f8c | 1198 | error = dpif_netdev_flow_from_nlattrs(get->key, get->key_len, &key); |
bc4a05c6 BP |
1199 | if (error) { |
1200 | return error; | |
1201 | } | |
14608a15 | 1202 | |
2c0ea78f | 1203 | netdev_flow = dp_netdev_find_flow(dp, &key); |
8a4e3a85 | 1204 | |
1763b4b8 | 1205 | if (netdev_flow) { |
6fe09f8c | 1206 | dp_netdev_flow_to_dpif_flow(netdev_flow, get->buffer, get->flow); |
61e7deb1 | 1207 | } else { |
5279f8fd | 1208 | error = ENOENT; |
72865317 | 1209 | } |
bc4a05c6 | 1210 | |
5279f8fd | 1211 | return error; |
72865317 BP |
1212 | } |
1213 | ||
72865317 | 1214 | static int |
ae2ceebd EJ |
1215 | dp_netdev_flow_add(struct dp_netdev *dp, struct match *match, |
1216 | const struct nlattr *actions, size_t actions_len) | |
8a4e3a85 | 1217 | OVS_REQUIRES(dp->flow_mutex) |
72865317 | 1218 | { |
1763b4b8 | 1219 | struct dp_netdev_flow *netdev_flow; |
72865317 | 1220 | |
1763b4b8 | 1221 | netdev_flow = xzalloc(sizeof *netdev_flow); |
ae2ceebd | 1222 | *CONST_CAST(struct flow *, &netdev_flow->flow) = match->flow; |
8a4e3a85 | 1223 | |
ed79f89a DDP |
1224 | ovs_refcount_init(&netdev_flow->ref_cnt); |
1225 | ||
679ba04c BP |
1226 | ovsthread_stats_init(&netdev_flow->stats); |
1227 | ||
61e7deb1 BP |
1228 | ovsrcu_set(&netdev_flow->actions, |
1229 | dp_netdev_actions_create(actions, actions_len)); | |
2c0ea78f | 1230 | |
8a4e3a85 | 1231 | cls_rule_init(CONST_CAST(struct cls_rule *, &netdev_flow->cr), |
ae2ceebd | 1232 | match, NETDEV_RULE_PRIORITY); |
9f361d6b JR |
1233 | cmap_insert(&dp->flow_table, |
1234 | CONST_CAST(struct cmap_node *, &netdev_flow->node), | |
ae2ceebd | 1235 | flow_hash(&match->flow, 0)); |
8a4e3a85 BP |
1236 | classifier_insert(&dp->cls, |
1237 | CONST_CAST(struct cls_rule *, &netdev_flow->cr)); | |
72865317 | 1238 | |
623540e4 EJ |
1239 | if (OVS_UNLIKELY(VLOG_IS_DBG_ENABLED())) { |
1240 | struct ds ds = DS_EMPTY_INITIALIZER; | |
1241 | ||
1242 | ds_put_cstr(&ds, "flow_add: "); | |
1243 | match_format(match, &ds, OFP_DEFAULT_PRIORITY); | |
1244 | ds_put_cstr(&ds, ", actions:"); | |
1245 | format_odp_actions(&ds, actions, actions_len); | |
1246 | ||
1247 | VLOG_DBG_RL(&upcall_rl, "%s", ds_cstr(&ds)); | |
1248 | ||
1249 | ds_destroy(&ds); | |
1250 | } | |
1251 | ||
72865317 BP |
1252 | return 0; |
1253 | } | |
1254 | ||
1255 | static void | |
1763b4b8 | 1256 | clear_stats(struct dp_netdev_flow *netdev_flow) |
72865317 | 1257 | { |
679ba04c BP |
1258 | struct dp_netdev_flow_stats *bucket; |
1259 | size_t i; | |
1260 | ||
1261 | OVSTHREAD_STATS_FOR_EACH_BUCKET (bucket, i, &netdev_flow->stats) { | |
1262 | ovs_mutex_lock(&bucket->mutex); | |
1263 | bucket->used = 0; | |
1264 | bucket->packet_count = 0; | |
1265 | bucket->byte_count = 0; | |
1266 | bucket->tcp_flags = 0; | |
1267 | ovs_mutex_unlock(&bucket->mutex); | |
1268 | } | |
72865317 BP |
1269 | } |
1270 | ||
1271 | static int | |
89625d1e | 1272 | dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put) |
72865317 BP |
1273 | { |
1274 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
1763b4b8 | 1275 | struct dp_netdev_flow *netdev_flow; |
4f150744 | 1276 | struct miniflow miniflow; |
ae2ceebd | 1277 | struct match match; |
36956a7d BP |
1278 | int error; |
1279 | ||
ae2ceebd | 1280 | error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &match.flow); |
8c301900 JR |
1281 | if (error) { |
1282 | return error; | |
1283 | } | |
1284 | error = dpif_netdev_mask_from_nlattrs(put->key, put->key_len, | |
1285 | put->mask, put->mask_len, | |
ae2ceebd | 1286 | &match.flow, &match.wc.masks); |
36956a7d BP |
1287 | if (error) { |
1288 | return error; | |
1289 | } | |
ae2ceebd | 1290 | miniflow_init(&miniflow, &match.flow); |
72865317 | 1291 | |
8a4e3a85 | 1292 | ovs_mutex_lock(&dp->flow_mutex); |
4f150744 | 1293 | netdev_flow = dp_netdev_lookup_flow(dp, &miniflow); |
1763b4b8 | 1294 | if (!netdev_flow) { |
89625d1e | 1295 | if (put->flags & DPIF_FP_CREATE) { |
9f361d6b | 1296 | if (cmap_count(&dp->flow_table) < MAX_FLOWS) { |
89625d1e BP |
1297 | if (put->stats) { |
1298 | memset(put->stats, 0, sizeof *put->stats); | |
feebdea2 | 1299 | } |
ae2ceebd | 1300 | error = dp_netdev_flow_add(dp, &match, put->actions, |
5279f8fd | 1301 | put->actions_len); |
72865317 | 1302 | } else { |
5279f8fd | 1303 | error = EFBIG; |
72865317 BP |
1304 | } |
1305 | } else { | |
5279f8fd | 1306 | error = ENOENT; |
72865317 BP |
1307 | } |
1308 | } else { | |
2c0ea78f | 1309 | if (put->flags & DPIF_FP_MODIFY |
ae2ceebd | 1310 | && flow_equal(&match.flow, &netdev_flow->flow)) { |
8a4e3a85 BP |
1311 | struct dp_netdev_actions *new_actions; |
1312 | struct dp_netdev_actions *old_actions; | |
1313 | ||
1314 | new_actions = dp_netdev_actions_create(put->actions, | |
1315 | put->actions_len); | |
1316 | ||
61e7deb1 BP |
1317 | old_actions = dp_netdev_flow_get_actions(netdev_flow); |
1318 | ovsrcu_set(&netdev_flow->actions, new_actions); | |
679ba04c | 1319 | |
a84cb64a BP |
1320 | if (put->stats) { |
1321 | get_dpif_flow_stats(netdev_flow, put->stats); | |
1322 | } | |
1323 | if (put->flags & DPIF_FP_ZERO_STATS) { | |
1324 | clear_stats(netdev_flow); | |
72865317 | 1325 | } |
8a4e3a85 | 1326 | |
61e7deb1 | 1327 | ovsrcu_postpone(dp_netdev_actions_free, old_actions); |
2c0ea78f | 1328 | } else if (put->flags & DPIF_FP_CREATE) { |
5279f8fd | 1329 | error = EEXIST; |
2c0ea78f GS |
1330 | } else { |
1331 | /* Overlapping flow. */ | |
1332 | error = EINVAL; | |
72865317 BP |
1333 | } |
1334 | } | |
8a4e3a85 | 1335 | ovs_mutex_unlock(&dp->flow_mutex); |
5715de14 | 1336 | miniflow_destroy(&miniflow); |
5279f8fd BP |
1337 | |
1338 | return error; | |
72865317 BP |
1339 | } |
1340 | ||
72865317 | 1341 | static int |
b99d3cee | 1342 | dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del) |
72865317 BP |
1343 | { |
1344 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
1763b4b8 | 1345 | struct dp_netdev_flow *netdev_flow; |
14608a15 | 1346 | struct flow key; |
36956a7d BP |
1347 | int error; |
1348 | ||
b99d3cee | 1349 | error = dpif_netdev_flow_from_nlattrs(del->key, del->key_len, &key); |
36956a7d BP |
1350 | if (error) { |
1351 | return error; | |
1352 | } | |
72865317 | 1353 | |
8a4e3a85 | 1354 | ovs_mutex_lock(&dp->flow_mutex); |
2c0ea78f | 1355 | netdev_flow = dp_netdev_find_flow(dp, &key); |
1763b4b8 | 1356 | if (netdev_flow) { |
b99d3cee | 1357 | if (del->stats) { |
1763b4b8 | 1358 | get_dpif_flow_stats(netdev_flow, del->stats); |
feebdea2 | 1359 | } |
8a4e3a85 | 1360 | dp_netdev_remove_flow(dp, netdev_flow); |
72865317 | 1361 | } else { |
5279f8fd | 1362 | error = ENOENT; |
72865317 | 1363 | } |
8a4e3a85 | 1364 | ovs_mutex_unlock(&dp->flow_mutex); |
5279f8fd BP |
1365 | |
1366 | return error; | |
72865317 BP |
1367 | } |
1368 | ||
ac64794a BP |
1369 | struct dpif_netdev_flow_dump { |
1370 | struct dpif_flow_dump up; | |
9f361d6b | 1371 | struct cmap_position pos; |
d2ad7ef1 JS |
1372 | int status; |
1373 | struct ovs_mutex mutex; | |
e723fd32 JS |
1374 | }; |
1375 | ||
ac64794a BP |
1376 | static struct dpif_netdev_flow_dump * |
1377 | dpif_netdev_flow_dump_cast(struct dpif_flow_dump *dump) | |
72865317 | 1378 | { |
ac64794a | 1379 | return CONTAINER_OF(dump, struct dpif_netdev_flow_dump, up); |
e723fd32 JS |
1380 | } |
1381 | ||
ac64794a BP |
1382 | static struct dpif_flow_dump * |
1383 | dpif_netdev_flow_dump_create(const struct dpif *dpif_) | |
e723fd32 | 1384 | { |
ac64794a | 1385 | struct dpif_netdev_flow_dump *dump; |
e723fd32 | 1386 | |
ac64794a BP |
1387 | dump = xmalloc(sizeof *dump); |
1388 | dpif_flow_dump_init(&dump->up, dpif_); | |
9f361d6b | 1389 | memset(&dump->pos, 0, sizeof dump->pos); |
ac64794a BP |
1390 | dump->status = 0; |
1391 | ovs_mutex_init(&dump->mutex); | |
1392 | ||
1393 | return &dump->up; | |
e723fd32 JS |
1394 | } |
1395 | ||
1396 | static int | |
ac64794a | 1397 | dpif_netdev_flow_dump_destroy(struct dpif_flow_dump *dump_) |
e723fd32 | 1398 | { |
ac64794a | 1399 | struct dpif_netdev_flow_dump *dump = dpif_netdev_flow_dump_cast(dump_); |
e723fd32 | 1400 | |
ac64794a BP |
1401 | ovs_mutex_destroy(&dump->mutex); |
1402 | free(dump); | |
704a1e09 BP |
1403 | return 0; |
1404 | } | |
1405 | ||
ac64794a BP |
1406 | struct dpif_netdev_flow_dump_thread { |
1407 | struct dpif_flow_dump_thread up; | |
1408 | struct dpif_netdev_flow_dump *dump; | |
8bb113da RW |
1409 | struct odputil_keybuf keybuf[FLOW_DUMP_MAX_BATCH]; |
1410 | struct odputil_keybuf maskbuf[FLOW_DUMP_MAX_BATCH]; | |
ac64794a BP |
1411 | }; |
1412 | ||
1413 | static struct dpif_netdev_flow_dump_thread * | |
1414 | dpif_netdev_flow_dump_thread_cast(struct dpif_flow_dump_thread *thread) | |
1415 | { | |
1416 | return CONTAINER_OF(thread, struct dpif_netdev_flow_dump_thread, up); | |
1417 | } | |
1418 | ||
1419 | static struct dpif_flow_dump_thread * | |
1420 | dpif_netdev_flow_dump_thread_create(struct dpif_flow_dump *dump_) | |
1421 | { | |
1422 | struct dpif_netdev_flow_dump *dump = dpif_netdev_flow_dump_cast(dump_); | |
1423 | struct dpif_netdev_flow_dump_thread *thread; | |
1424 | ||
1425 | thread = xmalloc(sizeof *thread); | |
1426 | dpif_flow_dump_thread_init(&thread->up, &dump->up); | |
1427 | thread->dump = dump; | |
1428 | return &thread->up; | |
1429 | } | |
1430 | ||
1431 | static void | |
1432 | dpif_netdev_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread_) | |
1433 | { | |
1434 | struct dpif_netdev_flow_dump_thread *thread | |
1435 | = dpif_netdev_flow_dump_thread_cast(thread_); | |
1436 | ||
1437 | free(thread); | |
1438 | } | |
1439 | ||
704a1e09 | 1440 | static int |
ac64794a | 1441 | dpif_netdev_flow_dump_next(struct dpif_flow_dump_thread *thread_, |
8bb113da | 1442 | struct dpif_flow *flows, int max_flows) |
ac64794a BP |
1443 | { |
1444 | struct dpif_netdev_flow_dump_thread *thread | |
1445 | = dpif_netdev_flow_dump_thread_cast(thread_); | |
1446 | struct dpif_netdev_flow_dump *dump = thread->dump; | |
1447 | struct dpif_netdev *dpif = dpif_netdev_cast(thread->up.dpif); | |
8bb113da | 1448 | struct dp_netdev_flow *netdev_flows[FLOW_DUMP_MAX_BATCH]; |
ac64794a | 1449 | struct dp_netdev *dp = get_dp_netdev(&dpif->dpif); |
8bb113da RW |
1450 | int n_flows = 0; |
1451 | int i; | |
14608a15 | 1452 | |
ac64794a | 1453 | ovs_mutex_lock(&dump->mutex); |
8bb113da | 1454 | if (!dump->status) { |
8bb113da RW |
1455 | for (n_flows = 0; n_flows < MIN(max_flows, FLOW_DUMP_MAX_BATCH); |
1456 | n_flows++) { | |
9f361d6b | 1457 | struct cmap_node *node; |
8bb113da | 1458 | |
9f361d6b | 1459 | node = cmap_next_position(&dp->flow_table, &dump->pos); |
8bb113da RW |
1460 | if (!node) { |
1461 | dump->status = EOF; | |
1462 | break; | |
1463 | } | |
1464 | netdev_flows[n_flows] = CONTAINER_OF(node, struct dp_netdev_flow, | |
1465 | node); | |
d2ad7ef1 | 1466 | } |
8a4e3a85 | 1467 | } |
ac64794a | 1468 | ovs_mutex_unlock(&dump->mutex); |
ac64794a | 1469 | |
8bb113da RW |
1470 | for (i = 0; i < n_flows; i++) { |
1471 | struct odputil_keybuf *maskbuf = &thread->maskbuf[i]; | |
1472 | struct odputil_keybuf *keybuf = &thread->keybuf[i]; | |
1473 | struct dp_netdev_flow *netdev_flow = netdev_flows[i]; | |
1474 | struct dpif_flow *f = &flows[i]; | |
1475 | struct dp_netdev_actions *dp_actions; | |
1476 | struct flow_wildcards wc; | |
1477 | struct ofpbuf buf; | |
1478 | ||
1479 | minimask_expand(&netdev_flow->cr.match.mask, &wc); | |
1480 | ||
1481 | /* Key. */ | |
1482 | ofpbuf_use_stack(&buf, keybuf, sizeof *keybuf); | |
1483 | odp_flow_key_from_flow(&buf, &netdev_flow->flow, &wc.masks, | |
1484 | netdev_flow->flow.in_port.odp_port, true); | |
1485 | f->key = ofpbuf_data(&buf); | |
1486 | f->key_len = ofpbuf_size(&buf); | |
1487 | ||
1488 | /* Mask. */ | |
1489 | ofpbuf_use_stack(&buf, maskbuf, sizeof *maskbuf); | |
1490 | odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow, | |
1491 | odp_to_u32(wc.masks.in_port.odp_port), | |
1492 | SIZE_MAX, true); | |
1493 | f->mask = ofpbuf_data(&buf); | |
1494 | f->mask_len = ofpbuf_size(&buf); | |
1495 | ||
1496 | /* Actions. */ | |
1497 | dp_actions = dp_netdev_flow_get_actions(netdev_flow); | |
1498 | f->actions = dp_actions->actions; | |
1499 | f->actions_len = dp_actions->size; | |
1500 | ||
1501 | /* Stats. */ | |
1502 | get_dpif_flow_stats(netdev_flow, &f->stats); | |
1503 | } | |
feebdea2 | 1504 | |
8bb113da | 1505 | return n_flows; |
72865317 BP |
1506 | } |
1507 | ||
1508 | static int | |
758c456d | 1509 | dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute) |
72865317 BP |
1510 | { |
1511 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
8cbf4f47 | 1512 | struct dpif_packet packet, *pp; |
758c456d | 1513 | struct pkt_metadata *md = &execute->md; |
72865317 | 1514 | |
1f317cb5 PS |
1515 | if (ofpbuf_size(execute->packet) < ETH_HEADER_LEN || |
1516 | ofpbuf_size(execute->packet) > UINT16_MAX) { | |
72865317 BP |
1517 | return EINVAL; |
1518 | } | |
1519 | ||
91088554 | 1520 | packet.ofpbuf = *execute->packet; |
8cbf4f47 | 1521 | pp = &packet; |
91088554 | 1522 | |
8cbf4f47 | 1523 | dp_netdev_execute_actions(dp, &pp, 1, false, md, |
df1e5a3b | 1524 | execute->actions, execute->actions_len); |
8a4e3a85 | 1525 | |
91088554 DDP |
1526 | /* Even though may_steal is set to false, some actions could modify or |
1527 | * reallocate the ofpbuf memory. We need to pass those changes to the | |
1528 | * caller */ | |
1529 | *execute->packet = packet.ofpbuf; | |
1530 | ||
758c456d | 1531 | return 0; |
72865317 BP |
1532 | } |
1533 | ||
1a0c894a BP |
1534 | static void |
1535 | dpif_netdev_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops) | |
1536 | { | |
1537 | size_t i; | |
1538 | ||
1539 | for (i = 0; i < n_ops; i++) { | |
1540 | struct dpif_op *op = ops[i]; | |
1541 | ||
1542 | switch (op->type) { | |
1543 | case DPIF_OP_FLOW_PUT: | |
1544 | op->error = dpif_netdev_flow_put(dpif, &op->u.flow_put); | |
1545 | break; | |
1546 | ||
1547 | case DPIF_OP_FLOW_DEL: | |
1548 | op->error = dpif_netdev_flow_del(dpif, &op->u.flow_del); | |
1549 | break; | |
1550 | ||
1551 | case DPIF_OP_EXECUTE: | |
1552 | op->error = dpif_netdev_execute(dpif, &op->u.execute); | |
1553 | break; | |
6fe09f8c JS |
1554 | |
1555 | case DPIF_OP_FLOW_GET: | |
1556 | op->error = dpif_netdev_flow_get(dpif, &op->u.flow_get); | |
1557 | break; | |
1a0c894a BP |
1558 | } |
1559 | } | |
1560 | } | |
1561 | ||
5bf93d67 EJ |
1562 | static int |
1563 | dpif_netdev_queue_to_priority(const struct dpif *dpif OVS_UNUSED, | |
1564 | uint32_t queue_id, uint32_t *priority) | |
1565 | { | |
1566 | *priority = queue_id; | |
1567 | return 0; | |
1568 | } | |
1569 | ||
72865317 | 1570 | \f |
a84cb64a BP |
1571 | /* Creates and returns a new 'struct dp_netdev_actions', with a reference count |
1572 | * of 1, whose actions are a copy of from the 'ofpacts_len' bytes of | |
1573 | * 'ofpacts'. */ | |
1574 | struct dp_netdev_actions * | |
1575 | dp_netdev_actions_create(const struct nlattr *actions, size_t size) | |
1576 | { | |
1577 | struct dp_netdev_actions *netdev_actions; | |
1578 | ||
1579 | netdev_actions = xmalloc(sizeof *netdev_actions); | |
a84cb64a BP |
1580 | netdev_actions->actions = xmemdup(actions, size); |
1581 | netdev_actions->size = size; | |
1582 | ||
1583 | return netdev_actions; | |
1584 | } | |
1585 | ||
a84cb64a | 1586 | struct dp_netdev_actions * |
61e7deb1 | 1587 | dp_netdev_flow_get_actions(const struct dp_netdev_flow *flow) |
a84cb64a | 1588 | { |
61e7deb1 | 1589 | return ovsrcu_get(struct dp_netdev_actions *, &flow->actions); |
a84cb64a BP |
1590 | } |
1591 | ||
61e7deb1 BP |
1592 | static void |
1593 | dp_netdev_actions_free(struct dp_netdev_actions *actions) | |
a84cb64a | 1594 | { |
61e7deb1 BP |
1595 | free(actions->actions); |
1596 | free(actions); | |
a84cb64a BP |
1597 | } |
1598 | \f | |
e4cfed38 | 1599 | |
5794e276 | 1600 | static void |
f7791740 | 1601 | dp_netdev_process_rxq_port(struct dp_netdev *dp, |
e4cfed38 | 1602 | struct dp_netdev_port *port, |
f7791740 | 1603 | struct netdev_rxq *rxq) |
e4cfed38 | 1604 | { |
8cbf4f47 DDP |
1605 | struct dpif_packet *packets[NETDEV_MAX_RX_BATCH]; |
1606 | int error, cnt; | |
e4cfed38 | 1607 | |
8cbf4f47 | 1608 | error = netdev_rxq_recv(rxq, packets, &cnt); |
e4cfed38 | 1609 | if (!error) { |
8cbf4f47 | 1610 | dp_netdev_port_input(dp, packets, cnt, port->port_no); |
e4cfed38 PS |
1611 | } else if (error != EAGAIN && error != EOPNOTSUPP) { |
1612 | static struct vlog_rate_limit rl | |
1613 | = VLOG_RATE_LIMIT_INIT(1, 5); | |
1614 | ||
1615 | VLOG_ERR_RL(&rl, "error receiving data from %s: %s", | |
1616 | netdev_get_name(port->netdev), | |
1617 | ovs_strerror(error)); | |
1618 | } | |
1619 | } | |
1620 | ||
1621 | static void | |
1622 | dpif_netdev_run(struct dpif *dpif) | |
1623 | { | |
1624 | struct dp_netdev_port *port; | |
1625 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
1626 | ||
a532e683 | 1627 | CMAP_FOR_EACH (port, node, &dp->ports) { |
55c955bd PS |
1628 | if (!netdev_is_pmd(port->netdev)) { |
1629 | int i; | |
1630 | ||
1631 | for (i = 0; i < netdev_n_rxq(port->netdev); i++) { | |
1632 | dp_netdev_process_rxq_port(dp, port, port->rxq[i]); | |
1633 | } | |
e4cfed38 PS |
1634 | } |
1635 | } | |
e4cfed38 PS |
1636 | } |
1637 | ||
1638 | static void | |
1639 | dpif_netdev_wait(struct dpif *dpif) | |
1640 | { | |
1641 | struct dp_netdev_port *port; | |
1642 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
1643 | ||
59e6d833 | 1644 | ovs_mutex_lock(&dp_netdev_mutex); |
a532e683 | 1645 | CMAP_FOR_EACH (port, node, &dp->ports) { |
55c955bd PS |
1646 | if (!netdev_is_pmd(port->netdev)) { |
1647 | int i; | |
1648 | ||
1649 | for (i = 0; i < netdev_n_rxq(port->netdev); i++) { | |
1650 | netdev_rxq_wait(port->rxq[i]); | |
1651 | } | |
e4cfed38 PS |
1652 | } |
1653 | } | |
59e6d833 | 1654 | ovs_mutex_unlock(&dp_netdev_mutex); |
e4cfed38 PS |
1655 | } |
1656 | ||
f7791740 | 1657 | struct rxq_poll { |
e4cfed38 | 1658 | struct dp_netdev_port *port; |
55c955bd | 1659 | struct netdev_rxq *rx; |
e4cfed38 PS |
1660 | }; |
1661 | ||
1662 | static int | |
1663 | pmd_load_queues(struct pmd_thread *f, | |
f7791740 | 1664 | struct rxq_poll **ppoll_list, int poll_cnt) |
e4cfed38 PS |
1665 | { |
1666 | struct dp_netdev *dp = f->dp; | |
f7791740 | 1667 | struct rxq_poll *poll_list = *ppoll_list; |
e4cfed38 PS |
1668 | struct dp_netdev_port *port; |
1669 | int id = f->id; | |
1670 | int index; | |
1671 | int i; | |
1672 | ||
1673 | /* Simple scheduler for netdev rx polling. */ | |
e4cfed38 PS |
1674 | for (i = 0; i < poll_cnt; i++) { |
1675 | port_unref(poll_list[i].port); | |
1676 | } | |
1677 | ||
1678 | poll_cnt = 0; | |
1679 | index = 0; | |
1680 | ||
a532e683 | 1681 | CMAP_FOR_EACH (port, node, &f->dp->ports) { |
e4cfed38 | 1682 | if (netdev_is_pmd(port->netdev)) { |
55c955bd PS |
1683 | int i; |
1684 | ||
1685 | for (i = 0; i < netdev_n_rxq(port->netdev); i++) { | |
1686 | if ((index % dp->n_pmd_threads) == id) { | |
1687 | poll_list = xrealloc(poll_list, sizeof *poll_list * (poll_cnt + 1)); | |
e4cfed38 | 1688 | |
55c955bd PS |
1689 | port_ref(port); |
1690 | poll_list[poll_cnt].port = port; | |
1691 | poll_list[poll_cnt].rx = port->rxq[i]; | |
1692 | poll_cnt++; | |
1693 | } | |
1694 | index++; | |
e4cfed38 | 1695 | } |
e4cfed38 PS |
1696 | } |
1697 | } | |
1698 | ||
e4cfed38 PS |
1699 | *ppoll_list = poll_list; |
1700 | return poll_cnt; | |
1701 | } | |
1702 | ||
6c3eee82 | 1703 | static void * |
e4cfed38 | 1704 | pmd_thread_main(void *f_) |
6c3eee82 | 1705 | { |
e4cfed38 | 1706 | struct pmd_thread *f = f_; |
6c3eee82 | 1707 | struct dp_netdev *dp = f->dp; |
e4cfed38 | 1708 | unsigned int lc = 0; |
f7791740 | 1709 | struct rxq_poll *poll_list; |
84067a4c | 1710 | unsigned int port_seq = PMD_INITIAL_SEQ; |
e4cfed38 PS |
1711 | int poll_cnt; |
1712 | int i; | |
6c3eee82 | 1713 | |
e4cfed38 PS |
1714 | poll_cnt = 0; |
1715 | poll_list = NULL; | |
1716 | ||
8617afff | 1717 | pmd_thread_setaffinity_cpu(f->id); |
e4cfed38 PS |
1718 | reload: |
1719 | poll_cnt = pmd_load_queues(f, &poll_list, poll_cnt); | |
6c3eee82 | 1720 | |
e4cfed38 | 1721 | for (;;) { |
6c3eee82 BP |
1722 | int i; |
1723 | ||
e4cfed38 | 1724 | for (i = 0; i < poll_cnt; i++) { |
55c955bd | 1725 | dp_netdev_process_rxq_port(dp, poll_list[i].port, poll_list[i].rx); |
e4cfed38 PS |
1726 | } |
1727 | ||
1728 | if (lc++ > 1024) { | |
84067a4c | 1729 | unsigned int seq; |
6c3eee82 | 1730 | |
e4cfed38 | 1731 | lc = 0; |
84067a4c JR |
1732 | |
1733 | ovsrcu_quiesce(); | |
1734 | ||
91a96379 | 1735 | atomic_read_relaxed(&f->change_seq, &seq); |
84067a4c JR |
1736 | if (seq != port_seq) { |
1737 | port_seq = seq; | |
6c3eee82 BP |
1738 | break; |
1739 | } | |
1740 | } | |
e4cfed38 | 1741 | } |
6c3eee82 | 1742 | |
e4cfed38 PS |
1743 | if (!latch_is_set(&f->dp->exit_latch)){ |
1744 | goto reload; | |
1745 | } | |
6c3eee82 | 1746 | |
e4cfed38 PS |
1747 | for (i = 0; i < poll_cnt; i++) { |
1748 | port_unref(poll_list[i].port); | |
6c3eee82 | 1749 | } |
6c3eee82 | 1750 | |
e4cfed38 | 1751 | free(poll_list); |
6c3eee82 BP |
1752 | return NULL; |
1753 | } | |
1754 | ||
6b31e073 RW |
1755 | static void |
1756 | dp_netdev_disable_upcall(struct dp_netdev *dp) | |
1757 | OVS_ACQUIRES(dp->upcall_rwlock) | |
1758 | { | |
1759 | fat_rwlock_wrlock(&dp->upcall_rwlock); | |
1760 | } | |
1761 | ||
1762 | static void | |
1763 | dpif_netdev_disable_upcall(struct dpif *dpif) | |
1764 | OVS_NO_THREAD_SAFETY_ANALYSIS | |
1765 | { | |
1766 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
1767 | dp_netdev_disable_upcall(dp); | |
1768 | } | |
1769 | ||
1770 | static void | |
1771 | dp_netdev_enable_upcall(struct dp_netdev *dp) | |
1772 | OVS_RELEASES(dp->upcall_rwlock) | |
1773 | { | |
1774 | fat_rwlock_unlock(&dp->upcall_rwlock); | |
1775 | } | |
1776 | ||
1777 | static void | |
1778 | dpif_netdev_enable_upcall(struct dpif *dpif) | |
1779 | OVS_NO_THREAD_SAFETY_ANALYSIS | |
1780 | { | |
1781 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
1782 | dp_netdev_enable_upcall(dp); | |
1783 | } | |
1784 | ||
6c3eee82 | 1785 | static void |
e4cfed38 | 1786 | dp_netdev_set_pmd_threads(struct dp_netdev *dp, int n) |
6c3eee82 BP |
1787 | { |
1788 | int i; | |
1789 | ||
e4cfed38 | 1790 | if (n == dp->n_pmd_threads) { |
6c3eee82 BP |
1791 | return; |
1792 | } | |
1793 | ||
1794 | /* Stop existing threads. */ | |
1795 | latch_set(&dp->exit_latch); | |
e4cfed38 PS |
1796 | dp_netdev_reload_pmd_threads(dp); |
1797 | for (i = 0; i < dp->n_pmd_threads; i++) { | |
1798 | struct pmd_thread *f = &dp->pmd_threads[i]; | |
6c3eee82 BP |
1799 | |
1800 | xpthread_join(f->thread, NULL); | |
1801 | } | |
1802 | latch_poll(&dp->exit_latch); | |
e4cfed38 | 1803 | free(dp->pmd_threads); |
6c3eee82 BP |
1804 | |
1805 | /* Start new threads. */ | |
e4cfed38 PS |
1806 | dp->pmd_threads = xmalloc(n * sizeof *dp->pmd_threads); |
1807 | dp->n_pmd_threads = n; | |
1808 | ||
6c3eee82 | 1809 | for (i = 0; i < n; i++) { |
e4cfed38 | 1810 | struct pmd_thread *f = &dp->pmd_threads[i]; |
6c3eee82 BP |
1811 | |
1812 | f->dp = dp; | |
e4cfed38 | 1813 | f->id = i; |
84067a4c | 1814 | atomic_init(&f->change_seq, PMD_INITIAL_SEQ); |
e4cfed38 PS |
1815 | |
1816 | /* Each thread will distribute all devices rx-queues among | |
1817 | * themselves. */ | |
8ba0a522 | 1818 | f->thread = ovs_thread_create("pmd", pmd_thread_main, f); |
6c3eee82 BP |
1819 | } |
1820 | } | |
e4cfed38 | 1821 | |
6c3eee82 | 1822 | \f |
679ba04c BP |
1823 | static void * |
1824 | dp_netdev_flow_stats_new_cb(void) | |
1825 | { | |
1826 | struct dp_netdev_flow_stats *bucket = xzalloc_cacheline(sizeof *bucket); | |
1827 | ovs_mutex_init(&bucket->mutex); | |
1828 | return bucket; | |
1829 | } | |
1830 | ||
72865317 | 1831 | static void |
1763b4b8 | 1832 | dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow, |
8cbf4f47 DDP |
1833 | int cnt, int size, |
1834 | uint16_t tcp_flags) | |
72865317 | 1835 | { |
679ba04c BP |
1836 | long long int now = time_msec(); |
1837 | struct dp_netdev_flow_stats *bucket; | |
1838 | ||
1839 | bucket = ovsthread_stats_bucket_get(&netdev_flow->stats, | |
1840 | dp_netdev_flow_stats_new_cb); | |
1841 | ||
1842 | ovs_mutex_lock(&bucket->mutex); | |
1843 | bucket->used = MAX(now, bucket->used); | |
8cbf4f47 DDP |
1844 | bucket->packet_count += cnt; |
1845 | bucket->byte_count += size; | |
679ba04c BP |
1846 | bucket->tcp_flags |= tcp_flags; |
1847 | ovs_mutex_unlock(&bucket->mutex); | |
72865317 BP |
1848 | } |
1849 | ||
51852a57 BP |
1850 | static void * |
1851 | dp_netdev_stats_new_cb(void) | |
1852 | { | |
1853 | struct dp_netdev_stats *bucket = xzalloc_cacheline(sizeof *bucket); | |
1854 | ovs_mutex_init(&bucket->mutex); | |
1855 | return bucket; | |
1856 | } | |
1857 | ||
1858 | static void | |
8cbf4f47 | 1859 | dp_netdev_count_packet(struct dp_netdev *dp, enum dp_stat_type type, int cnt) |
51852a57 BP |
1860 | { |
1861 | struct dp_netdev_stats *bucket; | |
1862 | ||
1863 | bucket = ovsthread_stats_bucket_get(&dp->stats, dp_netdev_stats_new_cb); | |
1864 | ovs_mutex_lock(&bucket->mutex); | |
8cbf4f47 | 1865 | bucket->n[type] += cnt; |
51852a57 BP |
1866 | ovs_mutex_unlock(&bucket->mutex); |
1867 | } | |
1868 | ||
623540e4 EJ |
1869 | static int |
1870 | dp_netdev_upcall(struct dp_netdev *dp, struct dpif_packet *packet_, | |
1871 | struct flow *flow, struct flow_wildcards *wc, | |
1872 | enum dpif_upcall_type type, const struct nlattr *userdata, | |
1873 | struct ofpbuf *actions, struct ofpbuf *put_actions) | |
1874 | { | |
1875 | struct ofpbuf *packet = &packet_->ofpbuf; | |
1876 | ||
1877 | if (type == DPIF_UC_MISS) { | |
1878 | dp_netdev_count_packet(dp, DP_STAT_MISS, 1); | |
1879 | } | |
1880 | ||
1881 | if (OVS_UNLIKELY(!dp->upcall_cb)) { | |
1882 | return ENODEV; | |
1883 | } | |
1884 | ||
1885 | if (OVS_UNLIKELY(!VLOG_DROP_DBG(&upcall_rl))) { | |
1886 | struct ds ds = DS_EMPTY_INITIALIZER; | |
1887 | struct ofpbuf key; | |
1888 | char *packet_str; | |
1889 | ||
1890 | ofpbuf_init(&key, 0); | |
1891 | odp_flow_key_from_flow(&key, flow, &wc->masks, flow->in_port.odp_port, | |
1892 | true); | |
1893 | ||
1894 | packet_str = ofp_packet_to_string(ofpbuf_data(packet), | |
1895 | ofpbuf_size(packet)); | |
1896 | ||
1897 | odp_flow_key_format(ofpbuf_data(&key), ofpbuf_size(&key), &ds); | |
1898 | ||
1899 | VLOG_DBG("%s: %s upcall:\n%s\n%s", dp->name, | |
1900 | dpif_upcall_type_to_string(type), ds_cstr(&ds), packet_str); | |
1901 | ||
1902 | ofpbuf_uninit(&key); | |
1903 | free(packet_str); | |
1904 | ds_destroy(&ds); | |
1905 | } | |
1906 | ||
1907 | return dp->upcall_cb(packet, flow, type, userdata, actions, wc, | |
1908 | put_actions, dp->upcall_aux); | |
1909 | } | |
1910 | ||
567bbb2e | 1911 | struct packet_batch { |
8cbf4f47 DDP |
1912 | unsigned int packet_count; |
1913 | unsigned int byte_count; | |
1914 | uint16_t tcp_flags; | |
1915 | ||
1916 | struct dp_netdev_flow *flow; | |
1917 | ||
1918 | struct dpif_packet *packets[NETDEV_MAX_RX_BATCH]; | |
1919 | struct pkt_metadata md; | |
1920 | }; | |
1921 | ||
1922 | static inline void | |
567bbb2e | 1923 | packet_batch_update(struct packet_batch *batch, |
8cbf4f47 DDP |
1924 | struct dpif_packet *packet, const struct miniflow *mf) |
1925 | { | |
1926 | batch->tcp_flags |= miniflow_get_tcp_flags(mf); | |
1927 | batch->packets[batch->packet_count++] = packet; | |
1928 | batch->byte_count += ofpbuf_size(&packet->ofpbuf); | |
1929 | } | |
1930 | ||
1931 | static inline void | |
567bbb2e | 1932 | packet_batch_init(struct packet_batch *batch, struct dp_netdev_flow *flow, |
84d6d5eb | 1933 | struct pkt_metadata *md) |
8cbf4f47 DDP |
1934 | { |
1935 | batch->flow = flow; | |
1936 | batch->md = *md; | |
8cbf4f47 DDP |
1937 | |
1938 | batch->packet_count = 0; | |
1939 | batch->byte_count = 0; | |
1940 | batch->tcp_flags = 0; | |
8cbf4f47 DDP |
1941 | } |
1942 | ||
1943 | static inline void | |
567bbb2e | 1944 | packet_batch_execute(struct packet_batch *batch, struct dp_netdev *dp) |
8cbf4f47 DDP |
1945 | { |
1946 | struct dp_netdev_actions *actions; | |
1947 | struct dp_netdev_flow *flow = batch->flow; | |
1948 | ||
1949 | dp_netdev_flow_used(batch->flow, batch->packet_count, batch->byte_count, | |
1950 | batch->tcp_flags); | |
1951 | ||
1952 | actions = dp_netdev_flow_get_actions(flow); | |
1953 | ||
1954 | dp_netdev_execute_actions(dp, batch->packets, | |
1955 | batch->packet_count, true, &batch->md, | |
1956 | actions->actions, actions->size); | |
1957 | ||
1958 | dp_netdev_count_packet(dp, DP_STAT_HIT, batch->packet_count); | |
1959 | } | |
1960 | ||
72865317 | 1961 | static void |
8cbf4f47 | 1962 | dp_netdev_input(struct dp_netdev *dp, struct dpif_packet **packets, int cnt, |
adcf00ba | 1963 | struct pkt_metadata *md) |
72865317 | 1964 | { |
84d6d5eb EJ |
1965 | struct packet_batch batches[NETDEV_MAX_RX_BATCH]; |
1966 | struct netdev_flow_key keys[NETDEV_MAX_RX_BATCH]; | |
1967 | const struct miniflow *mfs[NETDEV_MAX_RX_BATCH]; /* NULL at bad packets. */ | |
1968 | struct cls_rule *rules[NETDEV_MAX_RX_BATCH]; | |
1969 | size_t n_batches, i; | |
623540e4 | 1970 | bool any_miss; |
8cbf4f47 | 1971 | |
84d6d5eb EJ |
1972 | for (i = 0; i < cnt; i++) { |
1973 | if (OVS_UNLIKELY(ofpbuf_size(&packets[i]->ofpbuf) < ETH_HEADER_LEN)) { | |
1974 | dpif_packet_delete(packets[i]); | |
1975 | mfs[i] = NULL; | |
1976 | continue; | |
1977 | } | |
8cbf4f47 | 1978 | |
84d6d5eb EJ |
1979 | miniflow_initialize(&keys[i].flow, keys[i].buf); |
1980 | miniflow_extract(&packets[i]->ofpbuf, md, &keys[i].flow); | |
1981 | mfs[i] = &keys[i].flow; | |
1982 | } | |
4f150744 | 1983 | |
623540e4 EJ |
1984 | any_miss = !classifier_lookup_miniflow_batch(&dp->cls, mfs, rules, cnt); |
1985 | if (OVS_UNLIKELY(any_miss) && !fat_rwlock_tryrdlock(&dp->upcall_rwlock)) { | |
1986 | uint64_t actions_stub[512 / 8], slow_stub[512 / 8]; | |
1987 | struct ofpbuf actions, put_actions; | |
1988 | struct match match; | |
1989 | ||
1990 | ofpbuf_use_stub(&actions, actions_stub, sizeof actions_stub); | |
1991 | ofpbuf_use_stub(&put_actions, slow_stub, sizeof slow_stub); | |
1992 | ||
1993 | for (i = 0; i < cnt; i++) { | |
1994 | const struct dp_netdev_flow *netdev_flow; | |
1995 | struct ofpbuf *add_actions; | |
1996 | int error; | |
1997 | ||
1998 | if (OVS_LIKELY(rules[i] || !mfs[i])) { | |
1999 | continue; | |
2000 | } | |
2001 | ||
2002 | /* It's possible that an earlier slow path execution installed | |
2003 | * the rule this flow needs. In this case, it's a lot cheaper | |
2004 | * to catch it here than execute a miss. */ | |
2005 | netdev_flow = dp_netdev_lookup_flow(dp, mfs[i]); | |
2006 | if (netdev_flow) { | |
2007 | rules[i] = CONST_CAST(struct cls_rule *, &netdev_flow->cr); | |
2008 | continue; | |
2009 | } | |
2010 | ||
2011 | miniflow_expand(mfs[i], &match.flow); | |
2012 | ||
2013 | ofpbuf_clear(&actions); | |
2014 | ofpbuf_clear(&put_actions); | |
2015 | ||
2016 | error = dp_netdev_upcall(dp, packets[i], &match.flow, &match.wc, | |
2017 | DPIF_UC_MISS, NULL, &actions, | |
2018 | &put_actions); | |
2019 | if (OVS_UNLIKELY(error && error != ENOSPC)) { | |
2020 | continue; | |
2021 | } | |
2022 | ||
2023 | /* We can't allow the packet batching in the next loop to execute | |
2024 | * the actions. Otherwise, if there are any slow path actions, | |
2025 | * we'll send the packet up twice. */ | |
2026 | dp_netdev_execute_actions(dp, &packets[i], 1, false, md, | |
2027 | ofpbuf_data(&actions), | |
2028 | ofpbuf_size(&actions)); | |
2029 | ||
2030 | add_actions = ofpbuf_size(&put_actions) | |
2031 | ? &put_actions | |
2032 | : &actions; | |
2033 | ||
2034 | ovs_mutex_lock(&dp->flow_mutex); | |
2035 | /* XXX: There's a brief race where this flow could have already | |
2036 | * been installed since we last did the flow lookup. This could be | |
2037 | * solved by moving the mutex lock outside the loop, but that's an | |
2038 | * awful long time to be locking everyone out of making flow | |
2039 | * installs. If we move to a per-core classifier, it would be | |
2040 | * reasonable. */ | |
2041 | if (OVS_LIKELY(error != ENOSPC) | |
2042 | && !dp_netdev_lookup_flow(dp, mfs[i])) { | |
2043 | dp_netdev_flow_add(dp, &match, ofpbuf_data(add_actions), | |
2044 | ofpbuf_size(add_actions)); | |
2045 | } | |
2046 | ovs_mutex_unlock(&dp->flow_mutex); | |
2047 | } | |
2048 | ||
2049 | ofpbuf_uninit(&actions); | |
2050 | ofpbuf_uninit(&put_actions); | |
2051 | fat_rwlock_unlock(&dp->upcall_rwlock); | |
2052 | } | |
84d6d5eb EJ |
2053 | |
2054 | n_batches = 0; | |
8cbf4f47 | 2055 | for (i = 0; i < cnt; i++) { |
84d6d5eb EJ |
2056 | struct dp_netdev_flow *flow; |
2057 | struct packet_batch *batch; | |
2058 | size_t j; | |
8cbf4f47 | 2059 | |
623540e4 | 2060 | if (OVS_UNLIKELY(!rules[i] || !mfs[i])) { |
84d6d5eb EJ |
2061 | continue; |
2062 | } | |
2063 | ||
2064 | /* XXX: This O(n^2) algortihm makes sense if we're operating under the | |
2065 | * assumption that the number of distinct flows (and therefore the | |
2066 | * number of distinct batches) is quite small. If this turns out not | |
2067 | * to be the case, it may make sense to pre sort based on the | |
2068 | * netdev_flow pointer. That done we can get the appropriate batching | |
2069 | * in O(n * log(n)) instead. */ | |
2070 | batch = NULL; | |
2071 | flow = dp_netdev_flow_cast(rules[i]); | |
2072 | for (j = 0; j < n_batches; j++) { | |
2073 | if (batches[j].flow == flow) { | |
2074 | batch = &batches[j]; | |
2075 | break; | |
2076 | } | |
8cbf4f47 | 2077 | } |
84d6d5eb EJ |
2078 | |
2079 | if (!batch) { | |
2080 | batch = &batches[n_batches++]; | |
2081 | packet_batch_init(batch, flow, md); | |
2082 | } | |
2083 | packet_batch_update(batch, packets[i], mfs[i]); | |
8cbf4f47 DDP |
2084 | } |
2085 | ||
84d6d5eb EJ |
2086 | for (i = 0; i < n_batches; i++) { |
2087 | packet_batch_execute(&batches[i], dp); | |
72865317 BP |
2088 | } |
2089 | } | |
2090 | ||
adcf00ba | 2091 | static void |
8cbf4f47 DDP |
2092 | dp_netdev_port_input(struct dp_netdev *dp, struct dpif_packet **packets, |
2093 | int cnt, odp_port_t port_no) | |
adcf00ba AZ |
2094 | { |
2095 | uint32_t *recirc_depth = recirc_depth_get(); | |
8cbf4f47 | 2096 | struct pkt_metadata md = PKT_METADATA_INITIALIZER(port_no); |
adcf00ba AZ |
2097 | |
2098 | *recirc_depth = 0; | |
8cbf4f47 | 2099 | dp_netdev_input(dp, packets, cnt, &md); |
adcf00ba AZ |
2100 | } |
2101 | ||
9080a111 JR |
2102 | struct dp_netdev_execute_aux { |
2103 | struct dp_netdev *dp; | |
9080a111 JR |
2104 | }; |
2105 | ||
6b31e073 | 2106 | static void |
623540e4 EJ |
2107 | dpif_netdev_register_upcall_cb(struct dpif *dpif, upcall_callback *cb, |
2108 | void *aux) | |
6b31e073 RW |
2109 | { |
2110 | struct dp_netdev *dp = get_dp_netdev(dpif); | |
623540e4 | 2111 | dp->upcall_aux = aux; |
6b31e073 RW |
2112 | dp->upcall_cb = cb; |
2113 | } | |
2114 | ||
9080a111 | 2115 | static void |
8cbf4f47 | 2116 | dp_execute_cb(void *aux_, struct dpif_packet **packets, int cnt, |
572f732a | 2117 | struct pkt_metadata *md, |
09f9da0b | 2118 | const struct nlattr *a, bool may_steal) |
8a4e3a85 | 2119 | OVS_NO_THREAD_SAFETY_ANALYSIS |
9080a111 JR |
2120 | { |
2121 | struct dp_netdev_execute_aux *aux = aux_; | |
623540e4 EJ |
2122 | uint32_t *depth = recirc_depth_get(); |
2123 | struct dp_netdev *dp = aux->dp; | |
09f9da0b | 2124 | int type = nl_attr_type(a); |
8a4e3a85 | 2125 | struct dp_netdev_port *p; |
8cbf4f47 | 2126 | int i; |
9080a111 | 2127 | |
09f9da0b JR |
2128 | switch ((enum ovs_action_attr)type) { |
2129 | case OVS_ACTION_ATTR_OUTPUT: | |
623540e4 | 2130 | p = dp_netdev_lookup_port(dp, u32_to_odp(nl_attr_get_u32(a))); |
26a5075b | 2131 | if (OVS_LIKELY(p)) { |
8cbf4f47 | 2132 | netdev_send(p->netdev, packets, cnt, may_steal); |
26a5075b DDP |
2133 | } else if (may_steal) { |
2134 | for (i = 0; i < cnt; i++) { | |
2135 | dpif_packet_delete(packets[i]); | |
2136 | } | |
8a4e3a85 | 2137 | } |
09f9da0b JR |
2138 | break; |
2139 | ||
623540e4 EJ |
2140 | case OVS_ACTION_ATTR_USERSPACE: |
2141 | if (!fat_rwlock_tryrdlock(&dp->upcall_rwlock)) { | |
2142 | const struct nlattr *userdata; | |
2143 | struct ofpbuf actions; | |
2144 | struct flow flow; | |
4fc65926 | 2145 | |
623540e4 EJ |
2146 | userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA); |
2147 | ofpbuf_init(&actions, 0); | |
8cbf4f47 | 2148 | |
623540e4 EJ |
2149 | for (i = 0; i < cnt; i++) { |
2150 | int error; | |
2151 | ||
2152 | ofpbuf_clear(&actions); | |
2153 | ||
2154 | flow_extract(&packets[i]->ofpbuf, md, &flow); | |
2155 | error = dp_netdev_upcall(dp, packets[i], &flow, NULL, | |
2156 | DPIF_UC_ACTION, userdata, &actions, | |
2157 | NULL); | |
2158 | if (!error || error == ENOSPC) { | |
2159 | dp_netdev_execute_actions(dp, &packets[i], 1, false, md, | |
2160 | ofpbuf_data(&actions), | |
2161 | ofpbuf_size(&actions)); | |
2162 | } | |
8cbf4f47 | 2163 | |
623540e4 EJ |
2164 | if (may_steal) { |
2165 | dpif_packet_delete(packets[i]); | |
2166 | } | |
db73f716 | 2167 | } |
623540e4 EJ |
2168 | ofpbuf_uninit(&actions); |
2169 | fat_rwlock_unlock(&dp->upcall_rwlock); | |
8cbf4f47 | 2170 | } |
6b31e073 | 2171 | |
09f9da0b | 2172 | break; |
572f732a | 2173 | |
347bf289 AZ |
2174 | case OVS_ACTION_ATTR_HASH: { |
2175 | const struct ovs_action_hash *hash_act; | |
8cbf4f47 | 2176 | struct netdev_flow_key key; |
347bf289 AZ |
2177 | uint32_t hash; |
2178 | ||
2179 | hash_act = nl_attr_get(a); | |
8cbf4f47 DDP |
2180 | |
2181 | miniflow_initialize(&key.flow, key.buf); | |
2182 | ||
2183 | for (i = 0; i < cnt; i++) { | |
2184 | ||
645b8934 | 2185 | /* XXX: this is slow. Use RSS hash in the future */ |
8cbf4f47 DDP |
2186 | miniflow_extract(&packets[i]->ofpbuf, md, &key.flow); |
2187 | ||
2188 | if (hash_act->hash_alg == OVS_HASH_ALG_L4) { | |
2189 | /* Hash need not be symmetric, nor does it need to include | |
2190 | * L2 fields. */ | |
2191 | hash = miniflow_hash_5tuple(&key.flow, hash_act->hash_basis); | |
2192 | } else { | |
2193 | VLOG_WARN("Unknown hash algorithm specified " | |
2194 | "for the hash action."); | |
2195 | hash = 2; | |
2196 | } | |
2197 | ||
347bf289 AZ |
2198 | if (!hash) { |
2199 | hash = 1; /* 0 is not valid */ | |
2200 | } | |
2201 | ||
8cbf4f47 DDP |
2202 | if (i == 0) { |
2203 | md->dp_hash = hash; | |
2204 | } | |
2205 | packets[i]->dp_hash = hash; | |
347bf289 | 2206 | } |
347bf289 AZ |
2207 | break; |
2208 | } | |
2209 | ||
adcf00ba AZ |
2210 | case OVS_ACTION_ATTR_RECIRC: |
2211 | if (*depth < MAX_RECIRC_DEPTH) { | |
572f732a | 2212 | |
adcf00ba | 2213 | (*depth)++; |
8cbf4f47 DDP |
2214 | for (i = 0; i < cnt; i++) { |
2215 | struct dpif_packet *recirc_pkt; | |
2216 | struct pkt_metadata recirc_md = *md; | |
2217 | ||
2218 | recirc_pkt = (may_steal) ? packets[i] | |
2219 | : dpif_packet_clone(packets[i]); | |
2220 | ||
2221 | recirc_md.recirc_id = nl_attr_get_u32(a); | |
2222 | ||
2223 | /* Hash is private to each packet */ | |
61a2647e | 2224 | recirc_md.dp_hash = dpif_packet_get_dp_hash(packets[i]); |
8cbf4f47 | 2225 | |
623540e4 | 2226 | dp_netdev_input(dp, &recirc_pkt, 1, &recirc_md); |
8cbf4f47 | 2227 | } |
adcf00ba AZ |
2228 | (*depth)--; |
2229 | ||
adcf00ba AZ |
2230 | break; |
2231 | } else { | |
2232 | VLOG_WARN("Packet dropped. Max recirculation depth exceeded."); | |
26a5075b DDP |
2233 | if (may_steal) { |
2234 | for (i = 0; i < cnt; i++) { | |
2235 | dpif_packet_delete(packets[i]); | |
2236 | } | |
2237 | } | |
adcf00ba | 2238 | } |
572f732a | 2239 | break; |
572f732a | 2240 | |
09f9da0b JR |
2241 | case OVS_ACTION_ATTR_PUSH_VLAN: |
2242 | case OVS_ACTION_ATTR_POP_VLAN: | |
2243 | case OVS_ACTION_ATTR_PUSH_MPLS: | |
2244 | case OVS_ACTION_ATTR_POP_MPLS: | |
2245 | case OVS_ACTION_ATTR_SET: | |
2246 | case OVS_ACTION_ATTR_SAMPLE: | |
2247 | case OVS_ACTION_ATTR_UNSPEC: | |
2248 | case __OVS_ACTION_ATTR_MAX: | |
2249 | OVS_NOT_REACHED(); | |
da546e07 | 2250 | } |
98403001 BP |
2251 | } |
2252 | ||
4edb9ae9 | 2253 | static void |
8cbf4f47 DDP |
2254 | dp_netdev_execute_actions(struct dp_netdev *dp, |
2255 | struct dpif_packet **packets, int cnt, | |
2256 | bool may_steal, struct pkt_metadata *md, | |
9080a111 | 2257 | const struct nlattr *actions, size_t actions_len) |
72865317 | 2258 | { |
8cbf4f47 | 2259 | struct dp_netdev_execute_aux aux = {dp}; |
9080a111 | 2260 | |
8cbf4f47 DDP |
2261 | odp_execute_actions(&aux, packets, cnt, may_steal, md, actions, |
2262 | actions_len, dp_execute_cb); | |
72865317 BP |
2263 | } |
2264 | ||
2265 | const struct dpif_class dpif_netdev_class = { | |
72865317 | 2266 | "netdev", |
2197d7ab | 2267 | dpif_netdev_enumerate, |
0aeaabc8 | 2268 | dpif_netdev_port_open_type, |
72865317 BP |
2269 | dpif_netdev_open, |
2270 | dpif_netdev_close, | |
7dab847a | 2271 | dpif_netdev_destroy, |
e4cfed38 PS |
2272 | dpif_netdev_run, |
2273 | dpif_netdev_wait, | |
72865317 | 2274 | dpif_netdev_get_stats, |
72865317 BP |
2275 | dpif_netdev_port_add, |
2276 | dpif_netdev_port_del, | |
2277 | dpif_netdev_port_query_by_number, | |
2278 | dpif_netdev_port_query_by_name, | |
98403001 | 2279 | NULL, /* port_get_pid */ |
b0ec0f27 BP |
2280 | dpif_netdev_port_dump_start, |
2281 | dpif_netdev_port_dump_next, | |
2282 | dpif_netdev_port_dump_done, | |
72865317 BP |
2283 | dpif_netdev_port_poll, |
2284 | dpif_netdev_port_poll_wait, | |
72865317 | 2285 | dpif_netdev_flow_flush, |
ac64794a BP |
2286 | dpif_netdev_flow_dump_create, |
2287 | dpif_netdev_flow_dump_destroy, | |
2288 | dpif_netdev_flow_dump_thread_create, | |
2289 | dpif_netdev_flow_dump_thread_destroy, | |
704a1e09 | 2290 | dpif_netdev_flow_dump_next, |
1a0c894a | 2291 | dpif_netdev_operate, |
6b31e073 RW |
2292 | NULL, /* recv_set */ |
2293 | NULL, /* handlers_set */ | |
5bf93d67 | 2294 | dpif_netdev_queue_to_priority, |
6b31e073 RW |
2295 | NULL, /* recv */ |
2296 | NULL, /* recv_wait */ | |
2297 | NULL, /* recv_purge */ | |
2298 | dpif_netdev_register_upcall_cb, | |
2299 | dpif_netdev_enable_upcall, | |
2300 | dpif_netdev_disable_upcall, | |
72865317 | 2301 | }; |
614c4892 | 2302 | |
74cc3969 BP |
2303 | static void |
2304 | dpif_dummy_change_port_number(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
2305 | const char *argv[], void *aux OVS_UNUSED) | |
2306 | { | |
59e6d833 BP |
2307 | struct dp_netdev_port *old_port; |
2308 | struct dp_netdev_port *new_port; | |
74cc3969 | 2309 | struct dp_netdev *dp; |
ff073a71 | 2310 | odp_port_t port_no; |
74cc3969 | 2311 | |
8a4e3a85 | 2312 | ovs_mutex_lock(&dp_netdev_mutex); |
74cc3969 BP |
2313 | dp = shash_find_data(&dp_netdevs, argv[1]); |
2314 | if (!dp || !dpif_netdev_class_is_dummy(dp->class)) { | |
8a4e3a85 | 2315 | ovs_mutex_unlock(&dp_netdev_mutex); |
74cc3969 BP |
2316 | unixctl_command_reply_error(conn, "unknown datapath or not a dummy"); |
2317 | return; | |
2318 | } | |
8a4e3a85 BP |
2319 | ovs_refcount_ref(&dp->ref_cnt); |
2320 | ovs_mutex_unlock(&dp_netdev_mutex); | |
74cc3969 | 2321 | |
59e6d833 BP |
2322 | ovs_mutex_lock(&dp->port_mutex); |
2323 | if (get_port_by_name(dp, argv[2], &old_port)) { | |
74cc3969 | 2324 | unixctl_command_reply_error(conn, "unknown port"); |
8a4e3a85 | 2325 | goto exit; |
74cc3969 BP |
2326 | } |
2327 | ||
ff073a71 BP |
2328 | port_no = u32_to_odp(atoi(argv[3])); |
2329 | if (!port_no || port_no == ODPP_NONE) { | |
74cc3969 | 2330 | unixctl_command_reply_error(conn, "bad port number"); |
8a4e3a85 | 2331 | goto exit; |
74cc3969 | 2332 | } |
ff073a71 | 2333 | if (dp_netdev_lookup_port(dp, port_no)) { |
74cc3969 | 2334 | unixctl_command_reply_error(conn, "port number already in use"); |
8a4e3a85 | 2335 | goto exit; |
74cc3969 | 2336 | } |
59e6d833 BP |
2337 | |
2338 | /* Remove old port. */ | |
2339 | cmap_remove(&dp->ports, &old_port->node, hash_port_no(old_port->port_no)); | |
2340 | ovsrcu_postpone(free, old_port); | |
2341 | ||
2342 | /* Insert new port (cmap semantics mean we cannot re-insert 'old_port'). */ | |
2343 | new_port = xmemdup(old_port, sizeof *old_port); | |
2344 | new_port->port_no = port_no; | |
2345 | cmap_insert(&dp->ports, &new_port->node, hash_port_no(port_no)); | |
2346 | ||
d33ed218 | 2347 | seq_change(dp->port_seq); |
74cc3969 | 2348 | unixctl_command_reply(conn, NULL); |
8a4e3a85 BP |
2349 | |
2350 | exit: | |
59e6d833 | 2351 | ovs_mutex_unlock(&dp->port_mutex); |
8a4e3a85 | 2352 | dp_netdev_unref(dp); |
74cc3969 BP |
2353 | } |
2354 | ||
c40b890f BP |
2355 | static void |
2356 | dpif_dummy_delete_port(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
2357 | const char *argv[], void *aux OVS_UNUSED) | |
2358 | { | |
2359 | struct dp_netdev_port *port; | |
2360 | struct dp_netdev *dp; | |
2361 | ||
2362 | ovs_mutex_lock(&dp_netdev_mutex); | |
2363 | dp = shash_find_data(&dp_netdevs, argv[1]); | |
2364 | if (!dp || !dpif_netdev_class_is_dummy(dp->class)) { | |
2365 | ovs_mutex_unlock(&dp_netdev_mutex); | |
2366 | unixctl_command_reply_error(conn, "unknown datapath or not a dummy"); | |
2367 | return; | |
2368 | } | |
2369 | ovs_refcount_ref(&dp->ref_cnt); | |
2370 | ovs_mutex_unlock(&dp_netdev_mutex); | |
2371 | ||
2372 | ovs_mutex_lock(&dp->port_mutex); | |
2373 | if (get_port_by_name(dp, argv[2], &port)) { | |
2374 | unixctl_command_reply_error(conn, "unknown port"); | |
2375 | } else if (port->port_no == ODPP_LOCAL) { | |
2376 | unixctl_command_reply_error(conn, "can't delete local port"); | |
2377 | } else { | |
2378 | do_del_port(dp, port); | |
2379 | unixctl_command_reply(conn, NULL); | |
2380 | } | |
2381 | ovs_mutex_unlock(&dp->port_mutex); | |
2382 | ||
2383 | dp_netdev_unref(dp); | |
2384 | } | |
2385 | ||
0cbfe35d BP |
2386 | static void |
2387 | dpif_dummy_register__(const char *type) | |
2388 | { | |
2389 | struct dpif_class *class; | |
2390 | ||
2391 | class = xmalloc(sizeof *class); | |
2392 | *class = dpif_netdev_class; | |
2393 | class->type = xstrdup(type); | |
2394 | dp_register_provider(class); | |
2395 | } | |
2396 | ||
614c4892 | 2397 | void |
0cbfe35d | 2398 | dpif_dummy_register(bool override) |
614c4892 | 2399 | { |
0cbfe35d BP |
2400 | if (override) { |
2401 | struct sset types; | |
2402 | const char *type; | |
2403 | ||
2404 | sset_init(&types); | |
2405 | dp_enumerate_types(&types); | |
2406 | SSET_FOR_EACH (type, &types) { | |
2407 | if (!dp_unregister_provider(type)) { | |
2408 | dpif_dummy_register__(type); | |
2409 | } | |
2410 | } | |
2411 | sset_destroy(&types); | |
614c4892 | 2412 | } |
0cbfe35d BP |
2413 | |
2414 | dpif_dummy_register__("dummy"); | |
74cc3969 BP |
2415 | |
2416 | unixctl_command_register("dpif-dummy/change-port-number", | |
2417 | "DP PORT NEW-NUMBER", | |
2418 | 3, 3, dpif_dummy_change_port_number, NULL); | |
c40b890f BP |
2419 | unixctl_command_register("dpif-dummy/delete-port", "DP PORT", |
2420 | 2, 2, dpif_dummy_delete_port, NULL); | |
614c4892 | 2421 | } |