From patchwork Thu Aug 11 14:54:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ilya Maximets X-Patchwork-Id: 1665597 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: 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 RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4M3VGT39flz9sG6 for ; Fri, 12 Aug 2022 00:55:12 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 2D13E6110E; Thu, 11 Aug 2022 14:55:11 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 2D13E6110E 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 Lkhx9L7BAcCV; Thu, 11 Aug 2022 14:55:10 +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 1B56F610FE; Thu, 11 Aug 2022 14:55:09 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 1B56F610FE Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id DE34EC0033; Thu, 11 Aug 2022 14:55:08 +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 23AC5C002D for ; Thu, 11 Aug 2022 14:55:08 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id E584A61108 for ; Thu, 11 Aug 2022 14:55:07 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org E584A61108 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 u3kSEJdSOIr3 for ; Thu, 11 Aug 2022 14:55:06 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 5517B610FF Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by smtp3.osuosl.org (Postfix) with ESMTPS id 5517B610FF for ; Thu, 11 Aug 2022 14:55:06 +0000 (UTC) Received: (Authenticated sender: i.maximets@ovn.org) by mail.gandi.net (Postfix) with ESMTPSA id 6545340009; Thu, 11 Aug 2022 14:55:03 +0000 (UTC) From: Ilya Maximets To: ovs-dev@openvswitch.org Date: Thu, 11 Aug 2022 16:54:59 +0200 Message-Id: <20220811145459.1751516-1-i.maximets@ovn.org> X-Mailer: git-send-email 2.34.3 MIME-Version: 1.0 Cc: Ilya Maximets Subject: [ovs-dev] [PATCH] bond: Avoid deadlock while updating post recirculation rules. 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" If the PACKET_OUT from controller ends up with sending packet to a bond interface, the main thread will take locks in the following order: handle_openflow --> take ofproto_mutex handle_packet_out packet_xlate output_normal bond_update_post_recirc_rules --> take rwlock in bond.c If at the same time revalidator thread is processing other packet with the output to the same bond: xlate_actions output_normal bond_update_post_recirc_rules --> take rwlock in bond.c update_recirc_rules ofproto_dpif_add_internal_flow ofproto_flow_mod --> take ofproto_mutex So, it is possible for these 2 threads to lock each other by taking one lock and waiting for another thread to release the second lock. It might also be possible for the main thread to lock itself up by trying to acquire ofproto_mutex for the second time, if it will actually proceed with update_recirc_rules() after taking the bond rwlock. The problem appears to be that bond_update_post_recirc_rules() is called during the flow translation even if side effects are prohibited, which is the case for openflow PACKET_OUT handling. Skipping actual flow updates during the flow translation if side effects are disabled to avoid the deadlock. Since flow are not installed now when actions translated for very first packet, installing initial flows in bond_reconfigure(). This will cover the case of allocating a new recirc_id. Also checking if we need to update flows in bond_run() to cover link state changes. Reported-at: https://github.com/openvswitch/ovs-issues/issues/259 Fixes: adcf00ba35a0 ("ofproto/bond: Implement bond megaflow using recirculation") Signed-off-by: Ilya Maximets Acked-by: Mike Pattrick --- I'm not sure how to create a unit test for the lock race with revalidator, but it might be possible to trigger the self-lockup issue on the main thread. I'll try to work on the test while waiting for review. ofproto/bond.c | 30 ++++++++++++++++++++++++++++++ ofproto/bond.h | 3 +++ ofproto/ofproto-dpif-xlate.c | 15 ++++++++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/ofproto/bond.c b/ofproto/bond.c index f06cf20c9..a3cc795bf 100644 --- a/ofproto/bond.c +++ b/ofproto/bond.c @@ -187,10 +187,15 @@ static struct bond_member *choose_output_member(const struct bond *, uint16_t vlan) OVS_REQ_RDLOCK(rwlock); static void update_recirc_rules__(struct bond *); +static bool bond_may_recirc(const struct bond *bond); +static void bond_update_post_recirc_rules__(struct bond *bond, + const bool force) + OVS_REQ_WRLOCK(rwlock); static bool bond_is_falling_back_to_ab(const struct bond *); static void bond_add_lb_output_buckets(const struct bond *); static void bond_del_lb_output_buckets(const struct bond *); + /* Attempts to parse 's' as the name of a bond balancing mode. If successful, * stores the mode in '*balance' and returns true. Otherwise returns false * without modifying '*balance'. */ @@ -515,6 +520,12 @@ bond_reconfigure(struct bond *bond, const struct bond_settings *s) bond_entry_reset(bond); } + if (bond->ofproto->backer->rt_support.odp.recirc + && bond_may_recirc(bond)) { + /* Update rules to reflect possible recirc_id changes. */ + update_recirc_rules(bond); + } + ovs_rwlock_unlock(&rwlock); return revalidate; } @@ -728,6 +739,12 @@ bond_run(struct bond *bond, enum lacp_status lacp_status) bond_choose_active_member(bond); } + if (bond->ofproto->backer->rt_support.odp.recirc + && bond_may_recirc(bond)) { + /* Update rules to reflect possible link state changes. */ + bond_update_post_recirc_rules__(bond, false); + } + revalidate = bond->bond_revalidate; bond->bond_revalidate = false; ovs_rwlock_unlock(&rwlock); @@ -1091,6 +1108,19 @@ bond_update_post_recirc_rules(struct bond *bond, uint32_t *recirc_id, } } +void +bond_get_recirc_id_and_hash_basis(struct bond *bond, uint32_t *recirc_id, + uint32_t *hash_basis) +{ + ovs_rwlock_rdlock(&rwlock); + if (bond_may_recirc(bond)) { + *recirc_id = bond->recirc_id; + *hash_basis = bond->basis; + } else { + *recirc_id = *hash_basis = 0; + } + ovs_rwlock_unlock(&rwlock); +} /* Rebalancing. */ diff --git a/ofproto/bond.h b/ofproto/bond.h index 2eb0c95a7..9c2b994c6 100644 --- a/ofproto/bond.h +++ b/ofproto/bond.h @@ -131,6 +131,9 @@ void bond_rebalance(struct bond *); void bond_update_post_recirc_rules(struct bond *, uint32_t *recirc_id, uint32_t *hash_basis); +void bond_get_recirc_id_and_hash_basis(struct bond *bond, uint32_t *recirc_id, + uint32_t *hash_basis); + bool bond_use_lb_output_action(const struct bond *bond); #endif /* bond.h */ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index fda802e83..60ce7bb5b 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -2466,9 +2466,18 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle, /* In case recirculation is not actually in use, 'xr.recirc_id' * will be set to '0', since a valid 'recirc_id' can * not be zero. */ - bond_update_post_recirc_rules(out_xbundle->bond, - &xr.recirc_id, - &xr.hash_basis); + if (ctx->xin->allow_side_effects) { + bond_update_post_recirc_rules(out_xbundle->bond, + &xr.recirc_id, + &xr.hash_basis); + } else { + /* If side effects are not allowed, only getting the bond + * configuration. Rule updates will be handled by the + * main thread later. */ + bond_get_recirc_id_and_hash_basis(out_xbundle->bond, + &xr.recirc_id, + &xr.hash_basis); + } if (xr.recirc_id) { /* Use recirculation instead of output. */ use_recirc = true;