mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 14:25:26 +00:00
Before the cleanup option, the bridge_exit() call was fairly fast,
because it didn't include any particularly long operations. However,
with the cleanup flag, this function destroys a lot of datapath
resources freeing a lot of memory, waiting on RCU and talking to
the kernel. That may take a noticeable amount of time, especially
on a busy system or under profilers/sanitizers. However, the unixctl
'exit' command replies instantly without waiting for any work to
actually be done. This may cause system test failures or other
issues where scripts expect ovs-vswitchd to exit or destroy all the
datapath resources shortly after appctl call.
Fix that by waiting for the bridge_exit() before replying to the user.
At least, all the datapath resources will actually be destroyed by
the time ovs-appctl exits.
Also moving a structure from stack to global. Seems cleaner this way.
Since we're not replying right away and it's technically possible
to have multiple clients requesting exit at the same time, storing
connections in an array.
Fixes: fe13ccdca6
("vswitchd: Add --cleanup option to the 'appctl exit' command")
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
322 lines
8.6 KiB
C
322 lines
8.6 KiB
C
/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_MLOCKALL
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#include "bridge.h"
|
|
#include "command-line.h"
|
|
#include "compiler.h"
|
|
#include "daemon.h"
|
|
#include "dirs.h"
|
|
#include "dpif.h"
|
|
#include "dummy.h"
|
|
#include "fatal-signal.h"
|
|
#include "memory.h"
|
|
#include "netdev.h"
|
|
#include "openflow/openflow.h"
|
|
#include "ovsdb-idl.h"
|
|
#include "ovs-rcu.h"
|
|
#include "ovs-router.h"
|
|
#include "ovs-thread.h"
|
|
#include "openvswitch/poll-loop.h"
|
|
#include "simap.h"
|
|
#include "stream-ssl.h"
|
|
#include "stream.h"
|
|
#include "svec.h"
|
|
#include "timeval.h"
|
|
#include "unixctl.h"
|
|
#include "util.h"
|
|
#include "openvswitch/usdt-probes.h"
|
|
#include "openvswitch/vconn.h"
|
|
#include "openvswitch/vlog.h"
|
|
#include "lib/vswitch-idl.h"
|
|
#include "lib/dns-resolve.h"
|
|
|
|
VLOG_DEFINE_THIS_MODULE(vswitchd);
|
|
|
|
/* --mlockall: If set, locks all process memory into physical RAM, preventing
|
|
* the kernel from paging any of its memory to disk. */
|
|
static bool want_mlockall;
|
|
|
|
/* --hw-rawio-access: If set, retains CAP_SYS_RAWIO privileges. */
|
|
static bool hw_rawio_access;
|
|
|
|
static unixctl_cb_func ovs_vswitchd_exit;
|
|
|
|
static char *parse_options(int argc, char *argv[], char **unixctl_path);
|
|
OVS_NO_RETURN static void usage(void);
|
|
|
|
static struct ovs_vswitchd_exit_args {
|
|
struct unixctl_conn **conns;
|
|
size_t n_conns;
|
|
bool exiting;
|
|
bool cleanup;
|
|
} exit_args;
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct unixctl_server *unixctl;
|
|
char *unixctl_path = NULL;
|
|
char *remote;
|
|
int retval;
|
|
|
|
set_program_name(argv[0]);
|
|
ovsthread_id_init();
|
|
|
|
dns_resolve_init(true);
|
|
ovs_cmdl_proctitle_init(argc, argv);
|
|
service_start(&argc, &argv);
|
|
remote = parse_options(argc, argv, &unixctl_path);
|
|
fatal_ignore_sigpipe();
|
|
|
|
daemonize_start(true, hw_rawio_access);
|
|
|
|
if (want_mlockall) {
|
|
#ifdef HAVE_MLOCKALL
|
|
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
|
|
VLOG_ERR("mlockall failed: %s", ovs_strerror(errno));
|
|
} else {
|
|
set_memory_locked();
|
|
}
|
|
#else
|
|
VLOG_ERR("mlockall not supported on this system");
|
|
#endif
|
|
}
|
|
|
|
retval = unixctl_server_create(unixctl_path, &unixctl);
|
|
if (retval) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
unixctl_command_register("exit", "[--cleanup]", 0, 1,
|
|
ovs_vswitchd_exit, NULL);
|
|
|
|
bridge_init(remote);
|
|
free(remote);
|
|
|
|
while (!exit_args.exiting) {
|
|
OVS_USDT_PROBE(main, run_start);
|
|
memory_run();
|
|
if (memory_should_report()) {
|
|
struct simap usage;
|
|
|
|
simap_init(&usage);
|
|
bridge_get_memory_usage(&usage);
|
|
memory_report(&usage);
|
|
simap_destroy(&usage);
|
|
}
|
|
bridge_run();
|
|
unixctl_server_run(unixctl);
|
|
netdev_run();
|
|
|
|
memory_wait();
|
|
bridge_wait();
|
|
unixctl_server_wait(unixctl);
|
|
netdev_wait();
|
|
if (exit_args.exiting) {
|
|
poll_immediate_wake();
|
|
}
|
|
OVS_USDT_PROBE(main, poll_block);
|
|
poll_block();
|
|
if (should_service_stop()) {
|
|
exit_args.exiting = true;
|
|
}
|
|
}
|
|
bridge_exit(exit_args.cleanup);
|
|
|
|
for (size_t i = 0; i < exit_args.n_conns; i++) {
|
|
unixctl_command_reply(exit_args.conns[i], NULL);
|
|
}
|
|
free(exit_args.conns);
|
|
|
|
unixctl_server_destroy(unixctl);
|
|
service_stop();
|
|
vlog_disable_async();
|
|
ovsrcu_exit();
|
|
dns_resolve_destroy();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
parse_options(int argc, char *argv[], char **unixctl_pathp)
|
|
{
|
|
enum {
|
|
OPT_PEER_CA_CERT = UCHAR_MAX + 1,
|
|
OPT_MLOCKALL,
|
|
OPT_UNIXCTL,
|
|
VLOG_OPTION_ENUMS,
|
|
OPT_BOOTSTRAP_CA_CERT,
|
|
OPT_ENABLE_DUMMY,
|
|
OPT_DISABLE_SYSTEM,
|
|
OPT_DISABLE_SYSTEM_ROUTE,
|
|
DAEMON_OPTION_ENUMS,
|
|
OPT_DPDK,
|
|
SSL_OPTION_ENUMS,
|
|
OPT_DUMMY_NUMA,
|
|
OPT_HW_RAWIO_ACCESS,
|
|
};
|
|
static const struct option long_options[] = {
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{"mlockall", no_argument, NULL, OPT_MLOCKALL},
|
|
{"unixctl", required_argument, NULL, OPT_UNIXCTL},
|
|
DAEMON_LONG_OPTIONS,
|
|
VLOG_LONG_OPTIONS,
|
|
STREAM_SSL_LONG_OPTIONS,
|
|
{"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
|
|
{"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
|
|
{"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY},
|
|
{"disable-system", no_argument, NULL, OPT_DISABLE_SYSTEM},
|
|
{"disable-system-route", no_argument, NULL, OPT_DISABLE_SYSTEM_ROUTE},
|
|
{"dpdk", optional_argument, NULL, OPT_DPDK},
|
|
{"dummy-numa", required_argument, NULL, OPT_DUMMY_NUMA},
|
|
{"hw-rawio-access", no_argument, NULL, OPT_HW_RAWIO_ACCESS},
|
|
{NULL, 0, NULL, 0},
|
|
};
|
|
char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
|
|
|
|
for (;;) {
|
|
int c;
|
|
|
|
c = getopt_long(argc, argv, short_options, long_options, NULL);
|
|
if (c == -1) {
|
|
break;
|
|
}
|
|
|
|
switch (c) {
|
|
case 'h':
|
|
usage();
|
|
|
|
case 'V':
|
|
ovs_print_version(0, 0);
|
|
print_dpdk_version();
|
|
exit(EXIT_SUCCESS);
|
|
|
|
case OPT_MLOCKALL:
|
|
want_mlockall = true;
|
|
break;
|
|
|
|
case OPT_UNIXCTL:
|
|
*unixctl_pathp = optarg;
|
|
break;
|
|
|
|
VLOG_OPTION_HANDLERS
|
|
DAEMON_OPTION_HANDLERS
|
|
STREAM_SSL_OPTION_HANDLERS
|
|
|
|
case OPT_PEER_CA_CERT:
|
|
stream_ssl_set_peer_ca_cert_file(optarg);
|
|
break;
|
|
|
|
case OPT_BOOTSTRAP_CA_CERT:
|
|
stream_ssl_set_ca_cert_file(optarg, true);
|
|
break;
|
|
|
|
case OPT_ENABLE_DUMMY:
|
|
dummy_enable(optarg);
|
|
break;
|
|
|
|
case OPT_DISABLE_SYSTEM:
|
|
dp_disallow_provider("system");
|
|
break;
|
|
|
|
case OPT_DISABLE_SYSTEM_ROUTE:
|
|
ovs_router_disable_system_routing_table();
|
|
break;
|
|
|
|
case '?':
|
|
exit(EXIT_FAILURE);
|
|
|
|
case OPT_DPDK:
|
|
ovs_fatal(0, "Using --dpdk to configure DPDK is not supported.");
|
|
break;
|
|
|
|
case OPT_DUMMY_NUMA:
|
|
ovs_numa_set_dummy(optarg);
|
|
break;
|
|
|
|
case OPT_HW_RAWIO_ACCESS:
|
|
hw_rawio_access = true;
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
free(short_options);
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
switch (argc) {
|
|
case 0:
|
|
return xasprintf("unix:%s/db.sock", ovs_rundir());
|
|
|
|
case 1:
|
|
return xstrdup(argv[0]);
|
|
|
|
default:
|
|
VLOG_FATAL("at most one non-option argument accepted; "
|
|
"use --help for usage");
|
|
}
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
printf("%s: Open vSwitch daemon\n"
|
|
"usage: %s [OPTIONS] [DATABASE]\n"
|
|
"where DATABASE is a socket on which ovsdb-server is listening\n"
|
|
" (default: \"unix:%s/db.sock\").\n",
|
|
program_name, program_name, ovs_rundir());
|
|
stream_usage("DATABASE", true, false, true);
|
|
daemon_usage();
|
|
vlog_usage();
|
|
printf("\nDPDK options:\n"
|
|
"Configuration of DPDK via command-line is removed from this\n"
|
|
"version of Open vSwitch. DPDK is configured through ovsdb.\n"
|
|
);
|
|
printf("\nOther options:\n"
|
|
" --unixctl=SOCKET override default control socket name\n"
|
|
" -h, --help display this help message\n"
|
|
" -V, --version display version information\n");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
ovs_vswitchd_exit(struct unixctl_conn *conn, int argc,
|
|
const char *argv[], void *args OVS_UNUSED)
|
|
{
|
|
exit_args.n_conns++;
|
|
exit_args.conns = xrealloc(exit_args.conns,
|
|
exit_args.n_conns * sizeof *exit_args.conns);
|
|
exit_args.conns[exit_args.n_conns - 1] = conn;
|
|
exit_args.exiting = true;
|
|
if (!exit_args.cleanup) {
|
|
exit_args.cleanup = argc == 2 && !strcmp(argv[1], "--cleanup");
|
|
}
|
|
}
|