diff options
Diffstat (limited to 'src/core/nm-dcb.c')
| -rw-r--r-- | src/core/nm-dcb.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/src/core/nm-dcb.c b/src/core/nm-dcb.c new file mode 100644 index 00000000..b3958caa --- /dev/null +++ b/src/core/nm-dcb.c @@ -0,0 +1,389 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include <sys/wait.h> + +#include "nm-dcb.h" +#include "platform/nm-platform.h" +#include "NetworkManagerUtils.h" + +static const char *helper_names[] = {"dcbtool", "fcoeadm"}; + +gboolean +do_helper(const char *iface, + guint which, + DcbFunc run_func, + gpointer user_data, + GError ** error, + const char *fmt, + ...) +{ + gs_free const char **split = NULL; + gs_free char * cmdline = NULL; + gs_free const char **argv = NULL; + gsize i; + gsize u; + va_list args; + + g_return_val_if_fail(fmt != NULL, FALSE); + + va_start(args, fmt); + cmdline = g_strdup_vprintf(fmt, args); + va_end(args); + + split = nm_utils_strsplit_set_with_empty(cmdline, " "); + if (!split) { + g_set_error(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_FAILED, + "failure parsing %s command line", + helper_names[which]); + return FALSE; + } + + /* Allocate space for path, custom arg, interface name, arguments, and NULL */ + i = 0; + argv = g_new(const char *, NM_PTRARRAY_LEN(split) + 4); + argv[i++] = NULL; /* Placeholder for dcbtool path */ + if (which == DCBTOOL) { + argv[i++] = "sc"; + argv[i++] = (char *) iface; + } + for (u = 0; split[u]; u++) + argv[i++] = split[u]; + argv[i++] = NULL; + + if (!run_func((char **) argv, which, user_data, error)) { + g_assert(!error || *error); + return FALSE; + } + + return TRUE; +} + +gboolean +_dcb_enable(const char *iface, + gboolean enable, + DcbFunc run_func, + gpointer user_data, + GError ** error) +{ + if (enable) + return do_helper(iface, DCBTOOL, run_func, user_data, error, "dcb on"); + else + return do_helper(iface, DCBTOOL, run_func, user_data, error, "dcb off"); +} + +#define SET_FLAGS(f, tag) \ + G_STMT_START \ + { \ + if (!do_helper(iface, \ + DCBTOOL, \ + run_func, \ + user_data, \ + error, \ + tag " e:%c a:%c w:%c", \ + f & NM_SETTING_DCB_FLAG_ENABLE ? '1' : '0', \ + f & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0', \ + f & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0')) \ + return FALSE; \ + } \ + G_STMT_END + +#define SET_APP(f, s, tag) \ + G_STMT_START \ + { \ + int prio = nm_setting_dcb_get_app_##tag##_priority(s); \ + \ + SET_FLAGS(f, "app:" #tag); \ + if ((f & NM_SETTING_DCB_FLAG_ENABLE) && (prio >= 0)) { \ + if (!do_helper(iface, \ + DCBTOOL, \ + run_func, \ + user_data, \ + error, \ + "app:" #tag " appcfg:%02x", \ + (1 << prio))) \ + return FALSE; \ + } \ + } \ + G_STMT_END + +gboolean +_dcb_setup(const char * iface, + NMSettingDcb *s_dcb, + DcbFunc run_func, + gpointer user_data, + GError ** error) +{ + NMSettingDcbFlags flags; + guint i; + + g_assert(s_dcb); + + /* FCoE */ + flags = nm_setting_dcb_get_app_fcoe_flags(s_dcb); + SET_APP(flags, s_dcb, fcoe); + + /* iSCSI */ + flags = nm_setting_dcb_get_app_iscsi_flags(s_dcb); + SET_APP(flags, s_dcb, iscsi); + + /* FIP */ + flags = nm_setting_dcb_get_app_fip_flags(s_dcb); + SET_APP(flags, s_dcb, fip); + + /* Priority Flow Control */ + flags = nm_setting_dcb_get_priority_flow_control_flags(s_dcb); + SET_FLAGS(flags, "pfc"); + if (flags & NM_SETTING_DCB_FLAG_ENABLE) { + char buf[10]; + + for (i = 0; i < 8; i++) + buf[i] = nm_setting_dcb_get_priority_flow_control(s_dcb, i) ? '1' : '0'; + buf[i] = 0; + if (!do_helper(iface, DCBTOOL, run_func, user_data, error, "pfc pfcup:%s", buf)) + return FALSE; + } + + /* Priority Groups */ + flags = nm_setting_dcb_get_priority_group_flags(s_dcb); + if (flags & NM_SETTING_DCB_FLAG_ENABLE) { + GString *s; + gboolean success; + guint id; + + s = g_string_sized_new(150); + + g_string_append_printf(s, + "pg e:1 a:%c w:%c", + flags & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0', + flags & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0'); + + /* Priority Groups */ + g_string_append(s, " pgid:"); + for (i = 0; i < 8; i++) { + id = nm_setting_dcb_get_priority_group_id(s_dcb, i); + g_assert(id < 8 || id == 15); + g_string_append_c(s, (id < 8) ? ('0' + id) : 'f'); + } + + /* Priority Group Bandwidth */ + g_string_append_printf(s, + " pgpct:%u,%u,%u,%u,%u,%u,%u,%u", + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 0), + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 1), + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 2), + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 3), + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 4), + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 5), + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 6), + nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 7)); + + /* Priority Bandwidth */ + g_string_append_printf(s, + " uppct:%u,%u,%u,%u,%u,%u,%u,%u", + nm_setting_dcb_get_priority_bandwidth(s_dcb, 0), + nm_setting_dcb_get_priority_bandwidth(s_dcb, 1), + nm_setting_dcb_get_priority_bandwidth(s_dcb, 2), + nm_setting_dcb_get_priority_bandwidth(s_dcb, 3), + nm_setting_dcb_get_priority_bandwidth(s_dcb, 4), + nm_setting_dcb_get_priority_bandwidth(s_dcb, 5), + nm_setting_dcb_get_priority_bandwidth(s_dcb, 6), + nm_setting_dcb_get_priority_bandwidth(s_dcb, 7)); + + /* Strict Bandwidth */ + g_string_append(s, " strict:"); + for (i = 0; i < 8; i++) + g_string_append_c(s, + nm_setting_dcb_get_priority_strict_bandwidth(s_dcb, i) ? '1' : '0'); + + /* Priority Traffic Class */ + g_string_append(s, " up2tc:"); + for (i = 0; i < 8; i++) { + id = nm_setting_dcb_get_priority_traffic_class(s_dcb, i); + g_assert(id < 8); + g_string_append_c(s, '0' + id); + } + + success = do_helper(iface, DCBTOOL, run_func, user_data, error, "%s", s->str); + g_string_free(s, TRUE); + if (!success) + return FALSE; + } else { + /* Ignore disable failure since lldpad <= 0.9.46 does not support disabling + * priority groups without specifying an entire PG config. + */ + (void) do_helper(iface, DCBTOOL, run_func, user_data, error, "pg e:0"); + } + + return TRUE; +} + +gboolean +_dcb_cleanup(const char *iface, DcbFunc run_func, gpointer user_data, GError **error) +{ + const char *cmds[] = + {"app:fcoe e:0", "app:iscsi e:0", "app:fip e:0", "pfc e:0", "pg e:0", NULL}; + const char **iter = cmds; + gboolean success = TRUE; + + /* Turn everything off and return first error we get (if any) */ + while (iter && *iter) { + if (!do_helper(iface, DCBTOOL, run_func, user_data, success ? error : NULL, "%s", *iter)) + success = FALSE; + iter++; + } + + if (!_dcb_enable(iface, FALSE, run_func, user_data, success ? error : NULL)) + success = FALSE; + + return success; +} + +gboolean +_fcoe_setup(const char * iface, + NMSettingDcb *s_dcb, + DcbFunc run_func, + gpointer user_data, + GError ** error) +{ + NMSettingDcbFlags flags; + + g_assert(s_dcb); + + flags = nm_setting_dcb_get_app_fcoe_flags(s_dcb); + if (flags & NM_SETTING_DCB_FLAG_ENABLE) { + const char *mode = nm_setting_dcb_get_app_fcoe_mode(s_dcb); + + if (!do_helper(NULL, FCOEADM, run_func, user_data, error, "-m %s -c %s", mode, iface)) + return FALSE; + } else { + if (!do_helper(NULL, FCOEADM, run_func, user_data, error, "-d %s", iface)) + return FALSE; + } + + return TRUE; +} + +gboolean +_fcoe_cleanup(const char *iface, DcbFunc run_func, gpointer user_data, GError **error) +{ + return do_helper(NULL, FCOEADM, run_func, user_data, error, "-d %s", iface); +} + +static gboolean +run_helper(char **argv, guint which, gpointer user_data, GError **error) +{ + const char *helper_path; + int exit_status = 0; + gboolean success; + char * errmsg = NULL, *outmsg = NULL; + char * cmdline; + + helper_path = nm_utils_find_helper((which == DCBTOOL) ? "dcbtool" : "fcoeadm", NULL, error); + if (!helper_path) + return FALSE; + + argv[0] = (char *) helper_path; + cmdline = g_strjoinv(" ", argv); + nm_log_dbg(LOGD_DCB, "%s", cmdline); + + success = g_spawn_sync("/", + argv, + NULL, + 0 /*G_SPAWN_DEFAULT*/, + NULL, + NULL, + &outmsg, + &errmsg, + &exit_status, + error); + /* Log any stderr output */ + if (success && WIFEXITED(exit_status) && WEXITSTATUS(exit_status) && (errmsg || outmsg)) { + gboolean ignore_error = FALSE; + + /* Ignore fcoeadm "success" errors like when FCoE is already set up */ + if (errmsg && strstr(errmsg, "Connection already created")) + ignore_error = TRUE; + + if (ignore_error == FALSE) { + nm_log_warn(LOGD_DCB, + "'%s' failed: '%s'", + cmdline, + (errmsg && strlen(errmsg)) ? errmsg : outmsg); + g_set_error(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_FAILED, + "Failed to run '%s'", + cmdline); + success = FALSE; + } + } + + g_free(outmsg); + g_free(errmsg); + g_free(cmdline); + return success; +} + +gboolean +nm_dcb_enable(const char *iface, gboolean enable, GError **error) +{ + return _dcb_enable(iface, enable, run_helper, GUINT_TO_POINTER(DCBTOOL), error); +} + +gboolean +nm_dcb_setup(const char *iface, NMSettingDcb *s_dcb, GError **error) +{ + gboolean success; + + success = _dcb_setup(iface, s_dcb, run_helper, GUINT_TO_POINTER(DCBTOOL), error); + if (success) + success = _fcoe_setup(iface, s_dcb, run_helper, GUINT_TO_POINTER(FCOEADM), error); + + return success; +} + +static void +carrier_wait(const char *iface, guint secs, gboolean up) +{ + int ifindex, count = secs * 10; + + g_return_if_fail(iface != NULL); + + ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, iface); + if (ifindex > 0) { + /* To work around driver quirks and lldpad handling of carrier status, + * we must wait a short period of time to see if the carrier goes + * down, and then wait for the carrier to come back up again. Otherwise + * subsequent lldpad calls may fail with "Device not found, link down + * or DCB not enabled" errors. + */ + nm_log_dbg(LOGD_DCB, "(%s): cleanup waiting for carrier %s", iface, up ? "up" : "down"); + g_usleep(G_USEC_PER_SEC / 4); + while (nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex) != up && count-- > 0) { + g_usleep(G_USEC_PER_SEC / 10); + nm_platform_link_refresh(NM_PLATFORM_GET, ifindex); + } + } +} + +gboolean +nm_dcb_cleanup(const char *iface, GError **error) +{ + /* Ignore FCoE cleanup errors */ + _fcoe_cleanup(iface, run_helper, GUINT_TO_POINTER(FCOEADM), NULL); + + /* Must pause a bit to wait for carrier-up since disabling FCoE may + * cause the device to take the link down, making lldpad return errors. + */ + carrier_wait(iface, 2, FALSE); + carrier_wait(iface, 4, TRUE); + + return _dcb_cleanup(iface, run_helper, GUINT_TO_POINTER(DCBTOOL), error); +} |