From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 87703C54F4C for ; Tue, 28 Jul 2026 15:53:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=Fzy1oMLqWEotL6046KyN5CEFXwdL5POF6NaWp8nNNRI=; b=HvcjKekAfOCm7n qPDP1id3chF2G672U8jbVAFlBT73E9CfhSwKzNbYlBbfhT86VLg8qP/6eOjSY1zajPVhpOKb5vmsc E11xnSNRXHA6qMVibY2p2P5iFEwsEV53dN8cYdrDCyy3yFCr2GTYF2hhrNpL9u/NL7w112nn+XyY6 5mSJqyUJjhnerYUgybNqY+YwKCfvLXteIrU1vllPyNeWRGwRZaRMTayPi9CStSm/MUAi8U0K+i7WK 3KRJKDeouwPpyonBU/g9Q8tM6tODivSKMF5mToCu3xSSwg88LNpMNKLJgCjV12II9/X3Dr1xTkJMl dHGv+5c2F2zyZcqb5yeg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1wok7R-00000005jDg-0sTa; Tue, 28 Jul 2026 15:53:25 +0000 Received: from mgamail.intel.com ([198.175.65.21]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1wok7P-00000005jBg-0Fjh for linux-i3c@lists.infradead.org; Tue, 28 Jul 2026 15:53:24 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1785254003; x=1816790003; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=sUuzF0P0I00dkfsnkeCtRsCG4DemX0OdtpK7Rx0cxw4=; b=HQFsk/RChzyujIjqkAsKZtFPNm7nW6jCEEaxvLeRNSuugB74kj4t2EUb hfiUBg8nM4VzXEAKL7GjyHVaRxlRGd6jm8G2KxVI8ClBpEaAOwpYVYIgt 9ov/PbK1MDI6sa0Kxv963xetmQSa+N4aqC5eZ87RE/j0DopX5CJtVSRIk ncUyuFAEQPmSVIvYmKnWYpwtOx+WMtpjkHUMRdVJxnUf+d0l61x/sEsop 0KWmFshAXg40bp6JzP+ern0FkyOb8PwGISelIHmhXGm2zp1GRU33EiSDJ JkSgoGTvc3mej3VsN7BdTyV9ig+mv6n73kRznvyMpIEcf9//+uFktkAIs Q==; X-CSE-ConnectionGUID: /8MswP8tRqWcLc2IzT57UA== X-CSE-MsgGUID: dXqeteHTTJWoyTf1VTigNQ== X-IronPort-AV: E=McAfee;i="6800,10657,11859"; a="85696472" X-IronPort-AV: E=Sophos;i="6.25,190,1779174000"; d="scan'208";a="85696472" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by orvoesa113.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Jul 2026 08:53:21 -0700 X-CSE-ConnectionGUID: sYDiD1c9TOWmrDvo8mpT2g== X-CSE-MsgGUID: 8BvvLYzkRa2v3m+6vHpMKg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,190,1779174000"; d="scan'208";a="255817400" Received: from mkosciow-mobl1.ger.corp.intel.com (HELO ahunter6-desk) ([10.245.244.126]) by fmviesa010-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Jul 2026 08:53:19 -0700 From: Adrian Hunter To: alexandre.belloni@bootlin.com Cc: Frank.Li@nxp.com, rafael@kernel.org, linux-i3c@lists.infradead.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH V2 1/8] i3c: master: Fix recursive locking during device registration Date: Tue, 28 Jul 2026 18:53:01 +0300 Message-ID: <20260728155308.142713-2-adrian.hunter@intel.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260728155308.142713-1-adrian.hunter@intel.com> References: <20260728155308.142713-1-adrian.hunter@intel.com> MIME-Version: 1.0 Organization: Intel Finland Oy, Registered Address: c/o Alberga Business Park, 6 krs, Bertel Jungin Aukio 5, 02600 Espoo, Business Identity Code: 0357606 - 4, Domiciled in Helsinki X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260728_085323_177284_1F93FA49 X-CRM114-Status: GOOD ( 17.54 ) X-BeenThere: linux-i3c@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-i3c" Errors-To: linux-i3c-bounces+linux-i3c=archiver.kernel.org@lists.infradead.org i3c_master_register_new_i3c_devs() registers newly discovered devices while holding i3c_bus_normaluse_lock(), a down_read(). device_register() can immediately probe the device, and probe callbacks typically invoke I3C helpers that take i3c_bus_normaluse_lock() again, leading to a recursive acquisition of the same rwsem. rwsems do not support recursive read locking and can deadlock when a writer is waiting. See the "Recursive read locks" section of Documentation/locking/lockdep-design.rst. For example, with Intel LPSS I3C, LOCKDEP generates a WARNING like: # echo intel-lpss-i3c.0 > /sys/bus/platform/drivers/mipi-i3c-hci/unbind # echo intel-lpss-i3c.0 > /sys/bus/platform/drivers/mipi-i3c-hci/bind WARNING: possible recursive locking detected kworker/5:1/94 is trying to acquire lock: ffff88811c810d78 (&i3cbus->lock){++++}-{4:4}, at: i3c_device_match_id+0x45/0x370 but task is already holding lock: ffff88811c810d78 (&i3cbus->lock){++++}-{4:4}, at: i3c_master_reg_work_fn+0x21/0x5f0 Fix this by separating device creation from device registration. Populate desc->dev under the maintenance lock, collect the devices that still need registration into a local list, then release the lock before calling device_register(). Finally retake the lock and clean up any devices that failed to register. Use the maintenance lock rather than the normal-use lock while adding device objects. A write-side maintenance lock prevents readers from observing a partially initialized desc->dev during initial device population, or desc->dev disappearing if registration fails. The local list requires a list node, so add a list node member to struct i3c_device. Fixes: 3a379bbcea0a ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter --- Changes in V2: New patch drivers/i3c/master.c | 45 ++++++++++++++++++++++++++++---------- include/linux/i3c/master.h | 2 ++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index fd3e79d10c84..874f6e11b296 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1973,12 +1973,21 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, static void i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) { + struct i3c_device *i3cdev, *tmp; struct i3c_dev_desc *desc; + LIST_HEAD(i3c_unreg_devs); int ret; if (!master->init_done) return; + i3c_bus_maintenance_lock(&master->bus); + + if (master->shutting_down) { + i3c_bus_maintenance_unlock(&master->bus); + return; + } + i3c_bus_for_each_i3cdev(&master->bus, desc) { if (desc->dev || !desc->info.dyn_addr || desc == master->this) continue; @@ -1999,25 +2008,37 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) if (desc->boardinfo) desc->dev->dev.of_node = desc->boardinfo->of_node; - ret = device_register(&desc->dev->dev); - if (ret) { - dev_err(&master->dev, - "Failed to add I3C device (err = %d)\n", ret); - desc->dev->desc = NULL; - put_device(&desc->dev->dev); - desc->dev = NULL; - } + list_add_tail(&desc->dev->node, &i3c_unreg_devs); + } + + i3c_bus_maintenance_unlock(&master->bus); + + list_for_each_entry_safe(i3cdev, tmp, &i3c_unreg_devs, node) { + ret = device_register(&i3cdev->dev); + if (ret) + dev_err(&master->dev, "Failed to add I3C device (err = %d)\n", ret); + else + list_del_init(&i3cdev->node); + } + + i3c_bus_maintenance_lock(&master->bus); + + list_for_each_entry_safe(i3cdev, tmp, &i3c_unreg_devs, node) { + list_del(&i3cdev->node); + desc = i3cdev->desc; + i3cdev->desc = NULL; + put_device(&i3cdev->dev); + desc->dev = NULL; } + + i3c_bus_maintenance_unlock(&master->bus); } static void i3c_master_reg_work_fn(struct work_struct *work) { struct i3c_master_controller *master = container_of(work, typeof(*master), reg_work); - i3c_bus_normaluse_lock(&master->bus); - if (!master->shutting_down) - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + i3c_master_register_new_i3c_devs(master); } /** diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 4d2a68793324..fa1c98f0f5c1 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -228,6 +228,7 @@ struct i3c_dev_desc { * every time the I3C device is rediscovered with a different dynamic * address assigned * @bus: I3C bus this device is attached to + * @node: unregistered device list node * * I3C device object exposed to I3C device drivers. The takes care of linking * this object to the relevant &struct_i3c_dev_desc one. @@ -238,6 +239,7 @@ struct i3c_device { struct device dev; struct i3c_dev_desc *desc; struct i3c_bus *bus; + struct list_head node; }; /* -- 2.53.0 -- linux-i3c mailing list linux-i3c@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-i3c