From patchwork Sat May 20 00:31:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ilya Maximets X-Patchwork-Id: 1784022 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QNPlm6sTHz20PV for ; Sat, 20 May 2023 10:31:32 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 1E85C61333; Sat, 20 May 2023 00:31:31 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 1E85C61333 X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id aV4ZMkfWS8UM; Sat, 20 May 2023 00:31:29 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTPS id 3B9FA612FE; Sat, 20 May 2023 00:31:28 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 3B9FA612FE Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 0E4BFC0035; Sat, 20 May 2023 00:31:28 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 0FA7DC002A for ; Sat, 20 May 2023 00:31:27 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id D199B61306 for ; Sat, 20 May 2023 00:31:21 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org D199B61306 X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 7ieQ1ZFiWpDe for ; Sat, 20 May 2023 00:31:18 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 633AA612F0 Received: from relay7-d.mail.gandi.net (relay7-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::227]) by smtp3.osuosl.org (Postfix) with ESMTPS id 633AA612F0 for ; Sat, 20 May 2023 00:31:17 +0000 (UTC) Received: (Authenticated sender: i.maximets@ovn.org) by mail.gandi.net (Postfix) with ESMTPSA id 3749E20005; Sat, 20 May 2023 00:31:15 +0000 (UTC) From: Ilya Maximets To: ovs-dev@openvswitch.org Date: Sat, 20 May 2023 02:31:20 +0200 Message-Id: <20230520003120.1070717-4-i.maximets@ovn.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230520003120.1070717-1-i.maximets@ovn.org> References: <20230520003120.1070717-1-i.maximets@ovn.org> MIME-Version: 1.0 Cc: Ilya Maximets Subject: [ovs-dev] [PATCH 3/3] netdev-vport: RCU-fy tunnel config. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Tunnel config can be accessed by multiple threads at the same time and it is supposed to be protected by the netdev_vport mutex. However, many functions are getting direct access to it via netdev API without taking the mutex, creating a potential for various race conditions. Fix that by protecting the tunnel config with RCU. The whole structure is replaced on configuration changes. Individual fields are never updated and the structure itself is constant. This way it can be safely used by different threads within RCU grace period. Signed-off-by: Ilya Maximets Reviewed-by: Simon Horman --- lib/netdev-native-tnl.c | 84 +++++------------ lib/netdev-vport-private.h | 3 +- lib/netdev-vport.c | 189 +++++++++++++++++++++---------------- lib/netdev.h | 3 + 4 files changed, 134 insertions(+), 145 deletions(-) diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index e31d61dd5..53dde61f1 100644 --- a/lib/netdev-native-tnl.c +++ b/lib/netdev-native-tnl.c @@ -320,7 +320,7 @@ netdev_tnl_ip_build_header(struct ovs_action_push_tnl *data, } static void * -udp_build_header(struct netdev_tunnel_config *tnl_cfg, +udp_build_header(const struct netdev_tunnel_config *tnl_cfg, struct ovs_action_push_tnl *data, const struct netdev_tnl_build_header_params *params) { @@ -476,16 +476,11 @@ netdev_gre_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct netdev_tnl_build_header_params *params) { - struct netdev_vport *dev = netdev_vport_cast(netdev); - struct netdev_tunnel_config *tnl_cfg; + const struct netdev_tunnel_config *tnl_cfg; struct gre_base_hdr *greh; ovs_16aligned_be32 *options; unsigned int hlen; - /* XXX: RCUfy tnl_cfg. */ - ovs_mutex_lock(&dev->mutex); - tnl_cfg = &dev->tnl_cfg; - greh = netdev_tnl_ip_build_header(data, params, IPPROTO_GRE); if (params->flow->packet_type == htonl(PT_ETH)) { @@ -493,8 +488,7 @@ netdev_gre_build_header(const struct netdev *netdev, } else if (pt_ns(params->flow->packet_type) == OFPHTN_ETHERTYPE) { greh->protocol = pt_ns_type_be(params->flow->packet_type); } else { - ovs_mutex_unlock(&dev->mutex); - return 1; + return EINVAL; } greh->flags = 0; @@ -505,6 +499,8 @@ netdev_gre_build_header(const struct netdev *netdev, options++; } + tnl_cfg = netdev_get_tunnel_config(netdev); + if (tnl_cfg->out_key_present) { greh->flags |= htons(GRE_KEY); put_16aligned_be32(options, be64_to_be32(params->flow->tunnel.tun_id)); @@ -517,8 +513,6 @@ netdev_gre_build_header(const struct netdev *netdev, options++; } - ovs_mutex_unlock(&dev->mutex); - hlen = (uint8_t *) options - (uint8_t *) greh; data->header_len += hlen; @@ -628,8 +622,7 @@ netdev_erspan_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct netdev_tnl_build_header_params *params) { - struct netdev_vport *dev = netdev_vport_cast(netdev); - struct netdev_tunnel_config *tnl_cfg; + const struct netdev_tunnel_config *tnl_cfg; struct gre_base_hdr *greh; struct erspan_base_hdr *ersh; unsigned int hlen; @@ -637,21 +630,19 @@ netdev_erspan_build_header(const struct netdev *netdev, int erspan_ver; uint16_t sid; - /* XXX: RCUfy tnl_cfg. */ - ovs_mutex_lock(&dev->mutex); - tnl_cfg = &dev->tnl_cfg; greh = netdev_tnl_ip_build_header(data, params, IPPROTO_GRE); ersh = ERSPAN_HDR(greh); tun_id = ntohl(be64_to_be32(params->flow->tunnel.tun_id)); /* ERSPAN only has 10-bit session ID */ if (tun_id & ~ERSPAN_SID_MASK) { - ovs_mutex_unlock(&dev->mutex); - return 1; + return EINVAL; } else { sid = (uint16_t) tun_id; } + tnl_cfg = netdev_get_tunnel_config(netdev); + if (tnl_cfg->erspan_ver_flow) { erspan_ver = params->flow->tunnel.erspan_ver; } else { @@ -698,12 +689,9 @@ netdev_erspan_build_header(const struct netdev *netdev, hlen = ERSPAN_GREHDR_LEN + sizeof *ersh + ERSPAN_V2_MDSIZE; } else { VLOG_WARN_RL(&err_rl, "ERSPAN version error %d", tnl_cfg->erspan_ver); - ovs_mutex_unlock(&dev->mutex); - return 1; + return EINVAL; } - ovs_mutex_unlock(&dev->mutex); - data->header_len += hlen; if (params->is_ipv6) { @@ -809,13 +797,12 @@ netdev_gtpu_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct netdev_tnl_build_header_params *params) { - struct netdev_vport *dev = netdev_vport_cast(netdev); - struct netdev_tunnel_config *tnl_cfg; + const struct netdev_tunnel_config *tnl_cfg; struct gtpuhdr *gtph; unsigned int gtpu_hlen; - ovs_mutex_lock(&dev->mutex); - tnl_cfg = &dev->tnl_cfg; + tnl_cfg = netdev_get_tunnel_config(netdev); + gtph = udp_build_header(tnl_cfg, data, params); /* Set to default if not set in flow. */ @@ -831,7 +818,6 @@ netdev_gtpu_build_header(const struct netdev *netdev, gtph->md.flags |= GTPU_S_MASK; gtpu_hlen += sizeof(struct gtpuhdr_opt); } - ovs_mutex_unlock(&dev->mutex); data->header_len += gtpu_hlen; data->tnl_type = OVS_VPORT_TYPE_GTPU; @@ -844,19 +830,15 @@ netdev_srv6_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct netdev_tnl_build_header_params *params) { - struct netdev_vport *dev = netdev_vport_cast(netdev); - struct netdev_tunnel_config *tnl_cfg; + const struct netdev_tunnel_config *tnl_cfg; const struct in6_addr *segs; struct srv6_base_hdr *srh; struct in6_addr *s; ovs_be16 dl_type; - int err = 0; int nr_segs; int i; - ovs_mutex_lock(&dev->mutex); - tnl_cfg = &dev->tnl_cfg; - + tnl_cfg = netdev_get_tunnel_config(netdev); if (tnl_cfg->srv6_num_segs) { nr_segs = tnl_cfg->srv6_num_segs; segs = tnl_cfg->srv6_segs; @@ -870,8 +852,7 @@ netdev_srv6_build_header(const struct netdev *netdev, } if (!ipv6_addr_equals(&segs[0], ¶ms->flow->tunnel.ipv6_dst)) { - err = EINVAL; - goto out; + return EINVAL; } srh = netdev_tnl_ip_build_header(data, params, IPPROTO_ROUTING); @@ -888,8 +869,7 @@ netdev_srv6_build_header(const struct netdev *netdev, } else if (dl_type == htons(ETH_TYPE_IPV6)) { srh->rt_hdr.nexthdr = IPPROTO_IPV6; } else { - err = EOPNOTSUPP; - goto out; + return EOPNOTSUPP; } s = ALIGNED_CAST(struct in6_addr *, @@ -902,10 +882,8 @@ netdev_srv6_build_header(const struct netdev *netdev, data->header_len += sizeof *srh + 8 * srh->rt_hdr.hdrlen; data->tnl_type = OVS_VPORT_TYPE_SRV6; -out: - ovs_mutex_unlock(&dev->mutex); - return err; + return 0; } void @@ -1044,13 +1022,10 @@ netdev_vxlan_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct netdev_tnl_build_header_params *params) { - struct netdev_vport *dev = netdev_vport_cast(netdev); - struct netdev_tunnel_config *tnl_cfg; + const struct netdev_tunnel_config *tnl_cfg; struct vxlanhdr *vxh; - /* XXX: RCUfy tnl_cfg. */ - ovs_mutex_lock(&dev->mutex); - tnl_cfg = &dev->tnl_cfg; + tnl_cfg = netdev_get_tunnel_config(netdev); vxh = udp_build_header(tnl_cfg, data, params); @@ -1075,10 +1050,10 @@ netdev_vxlan_build_header(const struct netdev *netdev, vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_ETHERNET; break; default: - goto drop; + return EINVAL; } } else { - goto drop; + return EINVAL; } } else { put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS)); @@ -1086,14 +1061,9 @@ netdev_vxlan_build_header(const struct netdev *netdev, htonl(ntohll(params->flow->tunnel.tun_id) << 8)); } - ovs_mutex_unlock(&dev->mutex); data->header_len += sizeof *vxh; data->tnl_type = OVS_VPORT_TYPE_VXLAN; return 0; - -drop: - ovs_mutex_unlock(&dev->mutex); - return 1; } struct dp_packet * @@ -1157,22 +1127,14 @@ netdev_geneve_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct netdev_tnl_build_header_params *params) { - struct netdev_vport *dev = netdev_vport_cast(netdev); - struct netdev_tunnel_config *tnl_cfg; struct genevehdr *gnh; int opt_len; bool crit_opt; - /* XXX: RCUfy tnl_cfg. */ - ovs_mutex_lock(&dev->mutex); - tnl_cfg = &dev->tnl_cfg; - - gnh = udp_build_header(tnl_cfg, data, params); + gnh = udp_build_header(netdev_get_tunnel_config(netdev), data, params); put_16aligned_be32(&gnh->vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8)); - ovs_mutex_unlock(&dev->mutex); - opt_len = tun_metadata_to_geneve_header(¶ms->flow->tunnel, gnh->options, &crit_opt); diff --git a/lib/netdev-vport-private.h b/lib/netdev-vport-private.h index e3c3bdb43..586231057 100644 --- a/lib/netdev-vport-private.h +++ b/lib/netdev-vport-private.h @@ -28,6 +28,8 @@ struct netdev_vport { struct netdev up; + OVSRCU_TYPE(const struct netdev_tunnel_config *) tnl_cfg; + /* Sequence number for outgoing GRE packets. */ atomic_count gre_seqno; @@ -38,7 +40,6 @@ struct netdev_vport { struct netdev_stats stats; /* Tunnels. */ - struct netdev_tunnel_config tnl_cfg; char egress_iface[IFNAMSIZ]; bool carrier_status; diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 6bbaa2feb..480117a14 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -69,8 +69,8 @@ static int get_patch_config(const struct netdev *netdev, struct smap *args); static int get_tunnel_config(const struct netdev *, struct smap *args); static bool tunnel_check_status_change__(struct netdev_vport *); static void update_vxlan_global_cfg(struct netdev *, - struct netdev_tunnel_config *, - struct netdev_tunnel_config *); + const struct netdev_tunnel_config *, + const struct netdev_tunnel_config *); struct vport_class { const char *dpif_port; @@ -91,10 +91,16 @@ vport_class_cast(const struct netdev_class *class) return CONTAINER_OF(class, struct vport_class, netdev_class); } +static const struct netdev_tunnel_config * +vport_tunnel_config(struct netdev_vport *netdev) +{ + return ovsrcu_get(const struct netdev_tunnel_config *, &netdev->tnl_cfg); +} + static const struct netdev_tunnel_config * get_netdev_tunnel_config(const struct netdev *netdev) { - return &netdev_vport_cast(netdev)->tnl_cfg; + return vport_tunnel_config(netdev_vport_cast(netdev)); } bool @@ -135,8 +141,6 @@ netdev_vport_get_dpif_port(const struct netdev *netdev, } if (netdev_vport_needs_dst_port(netdev)) { - const struct netdev_vport *vport = netdev_vport_cast(netdev); - /* * Note: IFNAMSIZ is 16 bytes long. Implementations should choose * a dpif port name that is short enough to fit including any @@ -145,7 +149,7 @@ netdev_vport_get_dpif_port(const struct netdev *netdev, BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ); ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ); snprintf(namebuf, bufsize, "%s_%d", dpif_port, - ntohs(vport->tnl_cfg.dst_port)); + ntohs(netdev_get_tunnel_config(netdev)->dst_port)); return namebuf; } else { return dpif_port; @@ -163,12 +167,14 @@ netdev_vport_route_changed(void) vports = netdev_get_vports(&n_vports); for (i = 0; i < n_vports; i++) { + const struct netdev_tunnel_config *tnl_cfg; struct netdev *netdev_ = vports[i]; struct netdev_vport *netdev = netdev_vport_cast(netdev_); ovs_mutex_lock(&netdev->mutex); /* Finds all tunnel vports. */ - if (ipv6_addr_is_set(&netdev->tnl_cfg.ipv6_dst)) { + tnl_cfg = netdev_get_tunnel_config(netdev_); + if (tnl_cfg && ipv6_addr_is_set(&tnl_cfg->ipv6_dst)) { if (tunnel_check_status_change__(netdev)) { netdev_change_seq_changed(netdev_); } @@ -208,26 +214,31 @@ netdev_vport_construct(struct netdev *netdev_) port = atoi(p); } + struct netdev_tunnel_config *tnl_cfg = xzalloc(sizeof *tnl_cfg); + /* If a destination port for tunnel ports is specified in the netdev * name, use it instead of the default one. Otherwise, use the default * destination port */ if (!strcmp(type, "geneve")) { - dev->tnl_cfg.dst_port = port ? htons(port) : htons(GENEVE_DST_PORT); + tnl_cfg->dst_port = port ? htons(port) : htons(GENEVE_DST_PORT); } else if (!strcmp(type, "vxlan")) { - dev->tnl_cfg.dst_port = port ? htons(port) : htons(VXLAN_DST_PORT); - update_vxlan_global_cfg(netdev_, NULL, &dev->tnl_cfg); + tnl_cfg->dst_port = port ? htons(port) : htons(VXLAN_DST_PORT); + update_vxlan_global_cfg(netdev_, NULL, tnl_cfg); } else if (!strcmp(type, "lisp")) { - dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT); + tnl_cfg->dst_port = port ? htons(port) : htons(LISP_DST_PORT); } else if (!strcmp(type, "stt")) { - dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT); + tnl_cfg->dst_port = port ? htons(port) : htons(STT_DST_PORT); } else if (!strcmp(type, "gtpu")) { - dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT); + tnl_cfg->dst_port = port ? htons(port) : htons(GTPU_DST_PORT); } else if (!strcmp(type, "bareudp")) { - dev->tnl_cfg.dst_port = htons(port); + tnl_cfg->dst_port = htons(port); } - dev->tnl_cfg.dont_fragment = true; - dev->tnl_cfg.ttl = DEFAULT_TTL; + tnl_cfg->dont_fragment = true; + tnl_cfg->ttl = DEFAULT_TTL; + + ovsrcu_set(&dev->tnl_cfg, tnl_cfg); + return 0; } @@ -235,12 +246,15 @@ static void netdev_vport_destruct(struct netdev *netdev_) { struct netdev_vport *netdev = netdev_vport_cast(netdev_); + const struct netdev_tunnel_config *tnl_cfg = vport_tunnel_config(netdev); const char *type = netdev_get_type(netdev_); if (!strcmp(type, "vxlan")) { - update_vxlan_global_cfg(netdev_, &netdev->tnl_cfg, NULL); + update_vxlan_global_cfg(netdev_, tnl_cfg, NULL); } + ovsrcu_set(&netdev->tnl_cfg, NULL); + ovsrcu_postpone(free, CONST_CAST(struct netdev_tunnel_config *, tnl_cfg)); free(netdev->peer); ovs_mutex_destroy(&netdev->mutex); } @@ -283,15 +297,16 @@ static bool tunnel_check_status_change__(struct netdev_vport *netdev) OVS_REQUIRES(netdev->mutex) { + const struct netdev_tunnel_config *tnl_cfg = vport_tunnel_config(netdev); + const struct in6_addr *route; char iface[IFNAMSIZ]; bool status = false; - struct in6_addr *route; struct in6_addr gw; uint32_t mark; iface[0] = '\0'; - route = &netdev->tnl_cfg.ipv6_dst; - mark = netdev->tnl_cfg.egress_pkt_mark; + route = &tnl_cfg->ipv6_dst; + mark = tnl_cfg->egress_pkt_mark; if (ovs_router_lookup(mark, route, iface, NULL, &gw)) { struct netdev *egress_netdev; @@ -498,8 +513,8 @@ vxlan_get_port_ext_gbp_str(uint16_t port, bool gbp, static void update_vxlan_global_cfg(struct netdev *netdev, - struct netdev_tunnel_config *old_cfg, - struct netdev_tunnel_config *new_cfg) + const struct netdev_tunnel_config *old_cfg, + const struct netdev_tunnel_config *new_cfg) { unsigned int count; char namebuf[20]; @@ -543,19 +558,20 @@ static bool is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev, const struct netdev_tunnel_config *tnl_cfg) { - char namebuf[20]; - const char *type = netdev_get_type(&dev->up); + const struct netdev_tunnel_config *dev_tnl_cfg = vport_tunnel_config(dev); struct vport_class *vclass = vport_class_cast(netdev_get_class(&dev->up)); + const char *type = netdev_get_type(&dev->up); + char namebuf[20]; if (strcmp(type, "vxlan")) { return false; } - if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port && - (dev->tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GBP)) == + if (dev_tnl_cfg->dst_port == tnl_cfg->dst_port && + (dev_tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) == (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP))) { - if (ntohs(dev->tnl_cfg.dst_port) == VXLAN_DST_PORT) { + if (ntohs(dev_tnl_cfg->dst_port) == VXLAN_DST_PORT) { /* Special case where we kept the default port/gbp, only ok if the opposite of the default does not exits */ vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port), @@ -571,9 +587,9 @@ is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev, } /* Same port: ok if no one is left with the previous configuration */ - if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port) { - vxlan_get_port_ext_gbp_str(ntohs(dev->tnl_cfg.dst_port), - dev->tnl_cfg.exts & + if (dev_tnl_cfg->dst_port == tnl_cfg->dst_port) { + vxlan_get_port_ext_gbp_str(ntohs(dev_tnl_cfg->dst_port), + dev_tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP), namebuf, sizeof(namebuf)); @@ -601,6 +617,7 @@ static int set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) { struct netdev_vport *dev = netdev_vport_cast(dev_); + const struct netdev_tunnel_config *curr_tnl_cfg; const char *name = netdev_get_name(dev_); const char *type = netdev_get_type(dev_); struct ds errors = DS_EMPTY_INITIALIZER; @@ -902,11 +919,16 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) err = EEXIST; goto out; } - update_vxlan_global_cfg(dev_, &dev->tnl_cfg, &tnl_cfg); ovs_mutex_lock(&dev->mutex); - if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) { - dev->tnl_cfg = tnl_cfg; + + curr_tnl_cfg = vport_tunnel_config(dev); + update_vxlan_global_cfg(dev_, curr_tnl_cfg, &tnl_cfg); + + if (memcmp(curr_tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) { + ovsrcu_set(&dev->tnl_cfg, xmemdup(&tnl_cfg, sizeof tnl_cfg)); + ovsrcu_postpone(free, CONST_CAST(struct netdev_tunnel_config *, + curr_tnl_cfg)); tunnel_check_status_change__(dev); netdev_change_seq_changed(dev_); } @@ -931,61 +953,60 @@ out: static int get_tunnel_config(const struct netdev *dev, struct smap *args) { - struct netdev_vport *netdev = netdev_vport_cast(dev); + const struct netdev_tunnel_config *tnl_cfg = netdev_get_tunnel_config(dev); const char *type = netdev_get_type(dev); - struct netdev_tunnel_config tnl_cfg; - ovs_mutex_lock(&netdev->mutex); - tnl_cfg = netdev->tnl_cfg; - ovs_mutex_unlock(&netdev->mutex); + if (!tnl_cfg) { + return 0; + } - if (ipv6_addr_is_set(&tnl_cfg.ipv6_dst)) { - smap_add_ipv6(args, "remote_ip", &tnl_cfg.ipv6_dst); - } else if (tnl_cfg.ip_dst_flow) { + if (ipv6_addr_is_set(&tnl_cfg->ipv6_dst)) { + smap_add_ipv6(args, "remote_ip", &tnl_cfg->ipv6_dst); + } else if (tnl_cfg->ip_dst_flow) { smap_add(args, "remote_ip", "flow"); } - if (ipv6_addr_is_set(&tnl_cfg.ipv6_src)) { - smap_add_ipv6(args, "local_ip", &tnl_cfg.ipv6_src); - } else if (tnl_cfg.ip_src_flow) { + if (ipv6_addr_is_set(&tnl_cfg->ipv6_src)) { + smap_add_ipv6(args, "local_ip", &tnl_cfg->ipv6_src); + } else if (tnl_cfg->ip_src_flow) { smap_add(args, "local_ip", "flow"); } - if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) { + if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) { smap_add(args, "key", "flow"); - } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present - && tnl_cfg.in_key == tnl_cfg.out_key) { - smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key)); + } else if (tnl_cfg->in_key_present && tnl_cfg->out_key_present + && tnl_cfg->in_key == tnl_cfg->out_key) { + smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg->in_key)); } else { - if (tnl_cfg.in_key_flow) { + if (tnl_cfg->in_key_flow) { smap_add(args, "in_key", "flow"); - } else if (tnl_cfg.in_key_present) { + } else if (tnl_cfg->in_key_present) { smap_add_format(args, "in_key", "%"PRIu64, - ntohll(tnl_cfg.in_key)); + ntohll(tnl_cfg->in_key)); } - if (tnl_cfg.out_key_flow) { + if (tnl_cfg->out_key_flow) { smap_add(args, "out_key", "flow"); - } else if (tnl_cfg.out_key_present) { + } else if (tnl_cfg->out_key_present) { smap_add_format(args, "out_key", "%"PRIu64, - ntohll(tnl_cfg.out_key)); + ntohll(tnl_cfg->out_key)); } } - if (tnl_cfg.ttl_inherit) { + if (tnl_cfg->ttl_inherit) { smap_add(args, "ttl", "inherit"); - } else if (tnl_cfg.ttl != DEFAULT_TTL) { - smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl); + } else if (tnl_cfg->ttl != DEFAULT_TTL) { + smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg->ttl); } - if (tnl_cfg.tos_inherit) { + if (tnl_cfg->tos_inherit) { smap_add(args, "tos", "inherit"); - } else if (tnl_cfg.tos) { - smap_add_format(args, "tos", "0x%x", tnl_cfg.tos); + } else if (tnl_cfg->tos) { + smap_add_format(args, "tos", "0x%x", tnl_cfg->tos); } - if (tnl_cfg.dst_port) { - uint16_t dst_port = ntohs(tnl_cfg.dst_port); + if (tnl_cfg->dst_port) { + uint16_t dst_port = ntohs(tnl_cfg->dst_port); if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || @@ -997,33 +1018,33 @@ get_tunnel_config(const struct netdev *dev, struct smap *args) } } - if (tnl_cfg.csum) { + if (tnl_cfg->csum) { smap_add(args, "csum", "true"); } - if (tnl_cfg.set_seq) { + if (tnl_cfg->set_seq) { smap_add(args, "seq", "true"); } - enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); - if (tnl_cfg.pt_mode != default_pt_mode(layers)) { + enum tunnel_layers layers = tunnel_supported_layers(type, tnl_cfg); + if (tnl_cfg->pt_mode != default_pt_mode(layers)) { smap_add(args, "packet_type", - tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" - : tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" + tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" + : tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" : "ptap"); } - if (!tnl_cfg.dont_fragment) { + if (!tnl_cfg->dont_fragment) { smap_add(args, "df_default", "false"); } - if (tnl_cfg.set_egress_pkt_mark) { + if (tnl_cfg->set_egress_pkt_mark) { smap_add_format(args, "egress_pkt_mark", - "%"PRIu32, tnl_cfg.egress_pkt_mark); + "%"PRIu32, tnl_cfg->egress_pkt_mark); } if (!strcmp("erspan", type) || !strcmp("ip6erspan", type)) { - if (tnl_cfg.erspan_ver_flow) { + if (tnl_cfg->erspan_ver_flow) { /* since version number is not determined, * assume print all other as flow */ @@ -1032,27 +1053,27 @@ get_tunnel_config(const struct netdev *dev, struct smap *args) smap_add(args, "erspan_dir", "flow"); smap_add(args, "erspan_hwid", "flow"); } else { - smap_add_format(args, "erspan_ver", "%d", tnl_cfg.erspan_ver); + smap_add_format(args, "erspan_ver", "%d", tnl_cfg->erspan_ver); - if (tnl_cfg.erspan_ver == 1) { - if (tnl_cfg.erspan_idx_flow) { + if (tnl_cfg->erspan_ver == 1) { + if (tnl_cfg->erspan_idx_flow) { smap_add(args, "erspan_idx", "flow"); } else { smap_add_format(args, "erspan_idx", "0x%x", - tnl_cfg.erspan_idx); + tnl_cfg->erspan_idx); } - } else if (tnl_cfg.erspan_ver == 2) { - if (tnl_cfg.erspan_dir_flow) { + } else if (tnl_cfg->erspan_ver == 2) { + if (tnl_cfg->erspan_dir_flow) { smap_add(args, "erspan_dir", "flow"); } else { smap_add_format(args, "erspan_dir", "%d", - tnl_cfg.erspan_dir); + tnl_cfg->erspan_dir); } - if (tnl_cfg.erspan_hwid_flow) { + if (tnl_cfg->erspan_hwid_flow) { smap_add(args, "erspan_hwid", "flow"); } else { smap_add_format(args, "erspan_hwid", "0x%x", - tnl_cfg.erspan_hwid); + tnl_cfg->erspan_hwid); } } } @@ -1182,9 +1203,11 @@ netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats) static enum netdev_pt_mode netdev_vport_get_pt_mode(const struct netdev *netdev) { - struct netdev_vport *dev = netdev_vport_cast(netdev); + const struct netdev_tunnel_config *tnl_cfg; + + tnl_cfg = netdev_get_tunnel_config(netdev); - return dev->tnl_cfg.pt_mode; + return tnl_cfg ? tnl_cfg->pt_mode : NETDEV_PT_UNKNOWN; } diff --git a/lib/netdev.h b/lib/netdev.h index 1fab91273..aaec9ded1 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -72,6 +72,9 @@ struct sset; struct ovs_action_push_tnl; enum netdev_pt_mode { + /* Unknown mode. The netdev is not configured yet. */ + NETDEV_PT_UNKNOWN = 0, + /* The netdev is packet type aware. It can potentially carry any kind of * packet. This "modern" mode is appropriate for both netdevs that handle * only a single kind of packet (such as a virtual or physical Ethernet