From nobody Mon May 25 08:10:51 2026 Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com [209.85.215.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F247E2777FC for ; Sat, 16 May 2026 01:59:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896747; cv=none; b=oq/YqyTRw86iR1YuuMD8eTupPS+eh7vhD3w+wYz917DwWPmlNmqwdQjPR3G5YJOsUaDYnHqKO0KSH4kwoO2ZSe92TN1CeTE0vBR+CG9nS0pv9LsDdGkGiWT6Ro3j0g2EH2GIJABo9Mdn8igGc0umUXL0j0hrplKjBJHr9jyisCU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896747; c=relaxed/simple; bh=+mP6J0GmpPt2dHq9MQGkUGMiaPx2jIw8pRw/8IFhHsM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=qs2eTrwKn0TbcghQNvLCMS8Ct1cAKpZ+oWNbLdd8IAy6lYWVOb+aMAfgWM06mdyx8hjriNrkBKz35dHIWoJY3sG9p2+yW5AppJhQTReakOWwn8wh6W/3mt/afX9gZwQ+xeXLnawKdfHrQDf4IqgDcvIn1T4oM5r21VcnS4+2kmU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=oyz5euQc; arc=none smtp.client-ip=209.85.215.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="oyz5euQc" Received: by mail-pg1-f178.google.com with SMTP id 41be03b00d2f7-c8021c8c42fso138636a12.3 for ; Fri, 15 May 2026 18:59:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778896745; x=1779501545; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=dnydI41OQ3430HCEfE0IbCU2csloNYwU26T/5pTPuPE=; b=oyz5euQc6SV8ZZrpXMh7pjUrNO98G8r0xp0D5cowbGTsbU5dGmZv3xS+XkwGWp0op8 zDLSHqr0y02jJUPMfpydSxULB/qqx5fy9otsCTw6bwdTd823zgMruRJC78sOqemY6pUl ZbbRAAx4K5Vx+bOUhNEvFlbEd+hevjAdNf6JZFUY1kxYknPQ7KrDaHMnK6cnBr5/3H8v MN7zsVQkIDgBVqvNMqEoONJnNF45etdYpu86v2u5cOUiA2lvi0icLVOxGa52OfcQh0oU O7nS0+JjIvAiyWlkApUhmjA9lrMk/AZK45ku76eHz1xoz++OMg/ID8puoSYP3VuOFDtQ +MAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778896745; x=1779501545; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=dnydI41OQ3430HCEfE0IbCU2csloNYwU26T/5pTPuPE=; b=FGHFJ//DS8PykKPbuPrzryXV6Q/9yvYZfTZyMsg7LPYxTmheHLih5zwaofcyMzJ9vK 8VXMkn01xxsJor5OtODitdaY+ovtOs+ph7GHYK3AWZDLoX9u3zoFGuG6QBj7TDLzSO9b 6pmf/hMWBLJ0AyNeFWoqMCptXdGTJFu0yMz9QMFpsxYJ7HGFvzvZtjOn4TRc8pMCg9b6 D70Vyq76v2Bj/84SKXF1bikmldLFwP3Aq87ySZqstmnEU2cRI0t+F67+er1bxdQERi6r goaDOlDFQCEBBdIskZyvSeYmJW3d9bNN4M6G451rqAlmqYBFYp3+9CLP7ooNo+ji0uBE LThA== X-Forwarded-Encrypted: i=1; AFNElJ9gmE7XamU4u/iCk/8KC84FD/4CrlBFBm/782YJuHjrLoifzNwixiXO1OlkD8svEXdZS1WLJoGkDsZqJHg=@vger.kernel.org X-Gm-Message-State: AOJu0YxRk7AAA366tR/YOKf+boi8OBz/dP+hSv9rUb+BQTtzSSTp/sup bDW/fGetdDCZMrf9YD0ag0PAOWmcoAwMoXg6EWr03BiraQN1kARntDpy X-Gm-Gg: Acq92OG1Zg5fn9EDLLKjSccHO8VT9YBXBXljEG9sd9L9hlfdD5mOQ/tHlme6ofBOTpb eIiINQH6dy00dSShVXE6x4Rse37gvyvqx0ZAJ9bVHRIzKNS/mLQE1nFQts3UHQnW3WPrLxxj98z PY1HxDrALIWg5Bi/imsOXksVL1C5tnPkvICj55rCtqXu8qB11nHCJEnJtEklvdNUlNk7J+MqNSb gDdhCWwXxtgu+2awXtiisw/GqsqeYNcJx+yZw6udHE231y6gy6A+82v/3ednAJFIxtRxPjyUALc WEhjlTwykwJl3RhejncAPlrBbXErVUNSQIbWMBZVLMb4WLyn7VaUtY5eDQXRv0zVSqxfviB0zS8 FuFYmz34DQCCHGDRVD+fufytQ2rtHbkOpq4kK6wy4iQQcNDgPFAg/p8rh4Rn0tlVYfPCoGqrreT ELkJSzQOG1nwK7Z7Sjyi8PnhqO9G5Xt3mEHaf8Vy4tjpZlqp0eavlP83UMbP5I4xtlQoxClTl20 4TMi7Vc2hWBf59G7i/KjxsTm/qYCJbWZmekLg== X-Received: by 2002:a05:6a20:7294:b0:3a2:f7bd:a9a5 with SMTP id adf61e73a8af0-3b22ec73c64mr7355013637.38.1778896745059; Fri, 15 May 2026 18:59:05 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c82bb07b007sm6605080a12.11.2026.05.15.18.59.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 18:59:04 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, stephan.gerhold@kernkonzept.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v9 1/5] virtio-mmio: move guest page size setting into vm_reset() Date: Sat, 16 May 2026 10:57:52 +0900 Message-Id: <20260516015756.20948-2-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260516015756.20948-1-baver.bae@gmail.com> References: <20260516015756.20948-1-baver.bae@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Sungho Bae The virtio-mmio legacy spec (Section 4.2.4) requires the driver to write the guest page size "during initialization, before any queues are used". Reset is step 1 of the initialization sequence (Section 3.1), so setting GuestPageSize immediately after the status register reset in vm_reset() is more proper. Currently the GuestPageSize write lives in two separate call sites: - virtio_mmio_probe(), before register_virtio_device() - virtio_mmio_restore(), before virtio_device_restore() Both of these write the value *before* the reset that happens inside register_virtio_device()/virtio_device_restore(), so a device implementation that clears GuestPageSize on reset would lose the value. QEMU's virtio_mmio_reset() for example zeroes guest_page_shift on a full device reset. The current code happens to work because the Linux driver triggers only a "soft reset" (STATUS register write of 0), and QEMU's soft-reset path does not clear guest_page_shift. But relying on this is fragile and not guaranteed by the spec. Move the GuestPageSize write into vm_reset(), right after the status reset. This ensures the value is set: - at the correct point in the initialization sequence per spec, - after every reset (probe, restore, or any future path), and - exactly once, removing the duplication. Fixes: e0c2ce821795 ("virtio_mmio: Restore guest page size on resume") Signed-off-by: Sungho Bae --- drivers/virtio/virtio_mmio.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 595c2274fbb5..daa65b269a36 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -254,6 +254,16 @@ static void vm_reset(struct virtio_device *vdev) =20 /* 0 status means a reset. */ writel(0, vm_dev->base + VIRTIO_MMIO_STATUS); + + /* + * The virtio-mmio legacy spec requires the driver to write the + * guest page size during initialization, before any queues are + * used. Since reset is step 1 of initialization (Section 3.1), + * set it here so it is always in place for subsequent queue setup + * in every code path (probe, restore, etc.). + */ + if (vm_dev->version =3D=3D 1) + writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); } =20 =20 @@ -547,9 +557,6 @@ static int virtio_mmio_restore(struct device *dev) { struct virtio_mmio_device *vm_dev =3D dev_get_drvdata(dev); =20 - if (vm_dev->version =3D=3D 1) - writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); - return virtio_device_restore(&vm_dev->vdev); } =20 @@ -619,8 +626,6 @@ static int virtio_mmio_probe(struct platform_device *pd= ev) vm_dev->vdev.id.vendor =3D readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); =20 if (vm_dev->version =3D=3D 1) { - writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); - rc =3D dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); /* * In the legacy case, ensure our coherently-allocated virtio --=20 2.34.1 From nobody Mon May 25 08:10:51 2026 Received: from mail-pg1-f172.google.com (mail-pg1-f172.google.com [209.85.215.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 09DC9274B28 for ; Sat, 16 May 2026 01:59:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896750; cv=none; b=LfCKpBTPkxtfrPEC3rD5OB208+p9xELbD8F5//tG9BM2eUFXFSbrkwWojCsV2nYt0rwZ0optLKzJLOT1OSX/2Q4LPxetXmliyzLyFcdGq1inQLTmbXBFTy75WsIqFDxhZLMeqfgQC8AfPcAnGxxBfa0aSjN5II0gCaNuhiPyH7g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896750; c=relaxed/simple; bh=ulMWHrhAYz3XNtX+i0GVY0P4shlVGl/ceK8i2vNkHzc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FOJAta8YnACkIHHPwE5POEQkmOqu1rjyTHKYd9po9DBarPUjGd2k5D3o/ALYKTf0BmF0zv84gSqwjkmXG660IRBsZq5e7z++/R+tA3fKjqAj0JUvxqx6CEBYl4hZe4FGihhKrvi0A98HIU39RJulVYxEHZ1kvdOwOlzFx5kWCWY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=g8eC6Fff; arc=none smtp.client-ip=209.85.215.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="g8eC6Fff" Received: by mail-pg1-f172.google.com with SMTP id 41be03b00d2f7-c798fc1a28cso145950a12.3 for ; Fri, 15 May 2026 18:59:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778896748; x=1779501548; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=xmq5Cc6TQnVHvbEbh+VotFn9s+ga18ZxyIwreXpUesE=; b=g8eC6FffWXHuWVEqj677tUVbIbBybkVlfmufNVW/+VjAXDQf0dm4YTj8vNgdBnUG4E gx+6iuSYUBXm4/3WCi6jqbPZil8TCw5OiQcfaZOKpk7Hy0Q1LwsnbqK2gcRXODfEmYQY KVX34SN7PxuuQ9xQaSqftWgcKXddEPju6xTCWa2S9Ej+iTjVHbKK3Xbe72haVJO4cEfN La1vGovE3tut12m+kCbB38bTOvmZI5BaruLvsO2W8kbujyh1m3mklN9LiX9YM9tMAP7r E7BxJUA7bDdv2JjpqE0RvZJOd7aohnVy0QiGQJG4rb+6MRO6II2ngvCRPvFgpIDGW0Kx SGBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778896748; x=1779501548; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=xmq5Cc6TQnVHvbEbh+VotFn9s+ga18ZxyIwreXpUesE=; b=Yv9nze5XNVGAq9GkzFqhhnZAXJRe4zrmpF9xiysp0Ig5j/fdPcEpf/hJ7k/Wm+IWpc acvpS2g7CkfObeno3hzaK6s5Kf32Bf4IiCg1cthjWOZ9Vo3JhApVy6NrrfJM8aCUxohe ut79lY/iskiPDUaltBaUAKo2/McIqOJmo1pK89HiPzFxe8k5aEph3sgO7VKnq8Nlue6s FEwf5hjcp/vpWoKUdO3uSCZ7lMBk0FI6Wr79gk5ujA94Gqb+uemGjoa/XVhW07aoyULO yuIZ42LBzN12IvdAdURn+Ad2iIXSb3ujfJSvQZ4kHS/ggxz0uc8mHiW6nTHiiK61Wg8m fBVQ== X-Forwarded-Encrypted: i=1; AFNElJ/xvagDnPsyD4jbAFNLY3TYsGtA8OuvbS01UAlyNvlhwr31O1IRO71HRRLRHR1s9nN/2yt6y+rTmM7DwPw=@vger.kernel.org X-Gm-Message-State: AOJu0Yw8BcnjsGPVUYzFW1kM/5cBQz/MrK7D9aDbNmhHzAVvWty3xr29 IxHByeLc1sdovx80ywi3f7fCD3SJ1OXv8rFUEIt1Nwt3wuxW74WT6mqW X-Gm-Gg: Acq92OEsbzFGS6g7j07vjLxcE+nN5rHg0LdPko6K0zUfpL9g9MOJgs4BjSBRMQ5qXuC pWGDQLbMytIf25EG0xorrNpfnCCiPSJnOTi0NAb8CoqjzIDMRVVZL154NwzRzW+w+8iKsosSGzL EBgx9IEzGZ2OQiCDW93LjkACbJ4b6McTsDSrhyFmcyfnNFrrHQ/kdnkvc1oof+b1LjJWUH53/Zy 10xI69lDzLgUH6EUNTu5jPeCpJRE57CQvv3LI2ft3PYrhLG6j4kQdNS0Zm8UZxuessszEkbC10m +FClsf8rmcEko3vMQmUMKUPhZQIJ4lYWgyDgurth6OV6B+64Cdz9hMmyWikjzuyu3z2NoCYMh29 xEIiUtvsvl5zKWDI1gmeiOr6qm6JjqRhBuSr2dXExqqyuoIWd05r3yvGgMVsLIcnOIQQlNpg7ly VImCoQanV8RPzfTEi9BMDbkqMP6rKyiiDKYJmqxjTyxtNshBz9R+Dt3THGlgAGXi6xvvVaqUP8B 683kCkj/lL5zjM5hh6xgrBBFn25jMjPjYLzIQ== X-Received: by 2002:a05:6a20:6a04:b0:3ae:b670:6d35 with SMTP id adf61e73a8af0-3b22ecab6f5mr7688061637.24.1778896748145; Fri, 15 May 2026 18:59:08 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c82bb07b007sm6605080a12.11.2026.05.15.18.59.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 18:59:07 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, stephan.gerhold@kernkonzept.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v9 2/5] virtio: separate PM restore and reset_done paths Date: Sat, 16 May 2026 10:57:53 +0900 Message-Id: <20260516015756.20948-3-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260516015756.20948-1-baver.bae@gmail.com> References: <20260516015756.20948-1-baver.bae@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Sungho Bae Refactor virtio_device_restore_priv() by extracting the common device re-initialization sequence into virtio_device_reinit(). This helper performs the full bring-up sequence: reset, status acknowledgment, feature finalization, and feature negotiation. virtio_device_restore() and virtio_device_reset_done() now each call virtio_device_reinit() directly instead of going through a boolean- dispatched wrapper. This makes each path independently readable and extensible without further complicating the dispatch logic. A follow-up series will add noirq PM callbacks that only affect the restore path; having the two paths separated avoids adding more conditionals to a shared function. No functional change. Signed-off-by: Sungho Bae --- drivers/virtio/virtio.c | 81 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 5bdc6b82b30b..98f1875f8df1 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -588,7 +588,7 @@ void unregister_virtio_device(struct virtio_device *dev) } EXPORT_SYMBOL_GPL(unregister_virtio_device); =20 -static int virtio_device_restore_priv(struct virtio_device *dev, bool rest= ore) +static int virtio_device_reinit(struct virtio_device *dev) { struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); int ret; @@ -613,35 +613,9 @@ static int virtio_device_restore_priv(struct virtio_de= vice *dev, bool restore) =20 ret =3D dev->config->finalize_features(dev); if (ret) - goto err; - - ret =3D virtio_features_ok(dev); - if (ret) - goto err; - - if (restore) { - if (drv->restore) { - ret =3D drv->restore(dev); - if (ret) - goto err; - } - } else { - ret =3D drv->reset_done(dev); - if (ret) - goto err; - } - - /* If restore didn't do it, mark device DRIVER_OK ourselves. */ - if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) - virtio_device_ready(dev); - - virtio_config_core_enable(dev); - - return 0; + return ret; =20 -err: - virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); - return ret; + return virtio_features_ok(dev); } =20 #ifdef CONFIG_PM_SLEEP @@ -668,7 +642,33 @@ EXPORT_SYMBOL_GPL(virtio_device_freeze); =20 int virtio_device_restore(struct virtio_device *dev) { - return virtio_device_restore_priv(dev, true); + struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); + int ret; + + ret =3D virtio_device_reinit(dev); + if (ret) + goto err; + + if (!drv) + return 0; + + if (drv->restore) { + ret =3D drv->restore(dev); + if (ret) + goto err; + } + + /* If restore didn't do it, mark device DRIVER_OK ourselves. */ + if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) + virtio_device_ready(dev); + + virtio_config_core_enable(dev); + + return 0; + +err: + virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); + return ret; } EXPORT_SYMBOL_GPL(virtio_device_restore); #endif @@ -698,11 +698,30 @@ EXPORT_SYMBOL_GPL(virtio_device_reset_prepare); int virtio_device_reset_done(struct virtio_device *dev) { struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); + int ret; =20 if (!drv || !drv->reset_done) return -EOPNOTSUPP; =20 - return virtio_device_restore_priv(dev, false); + ret =3D virtio_device_reinit(dev); + if (ret) + goto err; + + ret =3D drv->reset_done(dev); + if (ret) + goto err; + + /* If reset_done didn't do it, mark device DRIVER_OK ourselves. */ + if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) + virtio_device_ready(dev); + + virtio_config_core_enable(dev); + + return 0; + +err: + virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); + return ret; } EXPORT_SYMBOL_GPL(virtio_device_reset_done); =20 --=20 2.34.1 From nobody Mon May 25 08:10:51 2026 Received: from mail-pg1-f182.google.com (mail-pg1-f182.google.com [209.85.215.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E8B012777FC for ; Sat, 16 May 2026 01:59:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896753; cv=none; b=jnj49dzQ7zcDY6V3M9HPdrvcC1NY67OQ1wJ8jNAW5UqUXhED3D5cKg1ZG7LmVK4y8tOsdtpw765gMjn7Rr7Zx/EFwGnJYzq1u0OHLcHtzKYTvyOvQRKWgffd0PHWAcpgFud6I5hcMGymw8rAbqj/P988+kFsSql2lqNIML7KQRc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896753; c=relaxed/simple; bh=3U2JuWoZlQw/6bodDnQIfAdeJKPuMhDGQJGcPm8dlt4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dHvHpQoQN5/1t8AvZuDaX7RHdPhWMqz6F70z5st1Tawm9kugcKcL04QDxYGGHuxOrh4MHUdaa0RIDaHoJ934AhO7ji1uWY+xz0idi0+pekcvoOvjFZBiLl+mweJ5ZJPEmaML6z5wlJGDAIKIyBCFMN9mnHRbFQaXboNgDCiRHzc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=svQ4rDf8; arc=none smtp.client-ip=209.85.215.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="svQ4rDf8" Received: by mail-pg1-f182.google.com with SMTP id 41be03b00d2f7-c80291e6237so305594a12.0 for ; Fri, 15 May 2026 18:59:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778896751; x=1779501551; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Y+/zy+vJjZtJy4sI1VAJYrcW/C5hH9QlOgZTXMyZTJA=; b=svQ4rDf837jhDCHRZ1xJkCbPIXJIiyULoP3SmJVWKv6OVGXp5ny2mAo6XAMWdfSmCX +zMzT8g5WpHwL11Q3w7g1gzqskIbb5uazYKZHqDgDbsMLt6dtNK6Py0EN5KSToMzqrcn 8QpKxUsUlRPykAaEWasIi5CO7KzvuHOQYm88x/oZ+fOR+eAExZXhDQQP/sPX6wCPckNI DJ4LMVZchOfH21dPXC0c3JIJsviqVVU0y1jBCZ0ESGCdKuZghZl6a5T+Frhajy7FPG2r shEbcFJOxlOViGtJzF0OyDvQwzZ7Sc2jqkYTf8T8r7ZKFoS2w/UxGutUxfHG6In4pkIX Ri4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778896751; x=1779501551; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Y+/zy+vJjZtJy4sI1VAJYrcW/C5hH9QlOgZTXMyZTJA=; b=lRtq7zbF3ERjdy3rizOC94IbyuJOn6SawVFjZXQyXGP6ORqu/hElFmUzrliooeKnL0 vhjpqZzfdpOCISQDIXVM8bxigmlaoVdXqR/LZPWH1AkgdNhRjWghc2x1+vIv23WC/3ZP PXD/iA72+/c/eHF8eVhb9Kl/EcnL5zwBWtaywc16G97zWxzb6D9okLMgC8KWaTkiDIPv tGfIbtM3V0HGGoAbXSsRWBJqaIBcCXtPffM8Gdl+WwkTXUY0E5SMkcyARMJpj01CE4oB X3amq6g1NUL0FunA7NIBn+zc00DYpg/haXYZNA0KLeHsX4tCd8ZHqL0Kl22rUvGE3XpJ U0Cg== X-Forwarded-Encrypted: i=1; AFNElJ+05qH43SSIdS18NIVpJJHTohYUP+zcAJpMdtq4m2WlqR2UxTUQ0wHEu7j8In/cDoRT79Xye8DlSQFwDqM=@vger.kernel.org X-Gm-Message-State: AOJu0Yze6rnp1t2QTr/utWmy66sWvbp2fdyiEUYkBEJczNFW7XFvirbV RnWNk9z65FdDlLW1ZjMBxe/Gmo8ChiD5KBTuuhjGQdjkx0+z0tbNSXJw X-Gm-Gg: Acq92OFEgQn08SQNnpeapPJVCLpYZe1OZfFlDTjEH8tWMmixNHmCL4Vf9+/PgRUMXAU EU/XQx9zcfEuwEdK7+IUXmux4LIDJoOxEcspD+WCvFWIhS6nS8XNSC0U0d1JFeDim61idKSgBDB ihC4KQ7Am0f5HXujgcD3IYJCyQf+SZQ3tn3f2GKtMkYLT6ZhI10opGniVOFRlz4NaJ8OEmg8ujz 8t3WHQxyU4ERAzYbF4Tmc6lOVYeJdhL7XyvXUy7Zq75oIBtpggvwD+ODzPfzTY10U71rAB0hFDu Pu1oJVlXZhsmDsA1B3pQDJRn3QfBv2vsu3MZpVOiPTuTy+/865/tU3ZAcSh90C3LavmTS92n+Zy 4rt41Wz3AOuC8AvXUtpdpad2SMMLES/SuRUVq4UNCJ45E/Vk9w7dHwRr6lfvwZqp9HsVxto+XzQ 4BemtQHhwO4dyvlDbX9Tx5oRjoRT0/SQn7daHUNP9HdqsaamBSe+CbfSz1J6pLW/cFcGzIrDu6o SQYgLVAwPgSAmeFLdZlDKqy5+Q= X-Received: by 2002:a05:6a20:a120:b0:3a2:c685:f9bf with SMTP id adf61e73a8af0-3b22edfcbf2mr7146917637.47.1778896750996; Fri, 15 May 2026 18:59:10 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c82bb07b007sm6605080a12.11.2026.05.15.18.59.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 18:59:10 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, stephan.gerhold@kernkonzept.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v9 3/5] virtio_ring: export virtqueue_reinit_vring() for noirq restore Date: Sat, 16 May 2026 10:57:54 +0900 Message-Id: <20260516015756.20948-4-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260516015756.20948-1-baver.bae@gmail.com> References: <20260516015756.20948-1-baver.bae@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Sungho Bae After a device reset in noirq context the existing vrings must be re-initialized without any memory allocation, because GFP_KERNEL is not available. The internal helpers virtqueue_reset_split() and virtqueue_reset_packed() already reset vring indices and descriptor state in place. Add a thin exported wrapper, virtqueue_reinit_vring(), that dispatches to the appropriate helper based on the ring layout. This will be used by a subsequent patch that adds noirq system-sleep PM callbacks for virtio-mmio. Signed-off-by: Sungho Bae --- drivers/virtio/virtio_ring.c | 58 ++++++++++++++++++++++++++++++++++++ include/linux/virtio_ring.h | 3 ++ 2 files changed, 61 insertions(+) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index fbca7ce1c6bf..d3339b820f6b 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -506,6 +506,15 @@ static void virtqueue_init(struct vring_virtqueue *vq,= u32 num) vq->event_triggered =3D false; vq->num_added =3D 0; =20 + /* + * Keep IN_ORDER state aligned with a freshly initialized/reset queue. + * For packed IN_ORDER, free_head is unused but harmlessly reset. + */ + if (virtqueue_is_in_order(vq)) { + vq->free_head =3D 0; + vq->batch_last.id =3D UINT_MAX; + } + #ifdef DEBUG vq->in_use =3D false; vq->last_add_time_valid =3D false; @@ -3936,5 +3945,54 @@ void virtqueue_map_sync_single_range_for_device(cons= t struct virtqueue *_vq, } EXPORT_SYMBOL_GPL(virtqueue_map_sync_single_range_for_device); =20 +/** + * virtqueue_reinit_vring - reinitialize vring state without reallocation + * @_vq: the virtqueue + * + * Reset the avail/used indices and descriptor state of an existing + * virtqueue so it can be reused after a device reset. No memory is + * allocated or freed, making this safe for use in noirq context. + * + * Preconditions for callers: + * 1) The vq must be fully quiesced (no concurrent add/get/kick/IRQ callba= ck). + * 2) Transport/device side must already have stopped/reset this queue. + * 3) All in-flight buffers must already be completed or detached. + * + * If called with outstanding descriptors, free-list state can be corrupte= d: + * num_free is restored to full capacity while desc_extra next-chain/free_= head + * may still represent a partially consumed list. + * + * Return: + * 0 on success, or -EBUSY if preconditions are not met. + */ +int virtqueue_reinit_vring(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq =3D to_vvq(_vq); + unsigned int num =3D virtqueue_is_packed(vq) ? + vq->packed.vring.num : vq->split.vring.num; + + /* All in-flight descriptors must be completed or detached */ + if (WARN_ON(vq->vq.num_free !=3D num)) + return -EBUSY; + + if (virtqueue_is_packed(vq)) { + virtqueue_reset_packed(vq); + } else { + /* + * Split queue shadow index should match the visible avail + * index when the queue is fully quiesced. + */ + if (WARN_ON(vq->split.avail_idx_shadow !=3D + virtio16_to_cpu(vq->vq.vdev, + vq->split.vring.avail->idx))) + return -EBUSY; + + virtqueue_reset_split(vq); + } + + return 0; +} +EXPORT_SYMBOL_GPL(virtqueue_reinit_vring); + MODULE_DESCRIPTION("Virtio ring implementation"); MODULE_LICENSE("GPL"); diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index c97a12c1cda3..8b421fef4fef 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -118,6 +118,9 @@ void vring_del_virtqueue(struct virtqueue *vq); /* Filter out transport-specific feature bits. */ void vring_transport_features(struct virtio_device *vdev); =20 +/* Reinitialize a virtqueue without reallocation (safe in noirq context) */ +int virtqueue_reinit_vring(struct virtqueue *_vq); + irqreturn_t vring_interrupt(int irq, void *_vq); =20 u32 vring_notification_data(struct virtqueue *_vq); --=20 2.34.1 From nobody Mon May 25 08:10:51 2026 Received: from mail-pg1-f181.google.com (mail-pg1-f181.google.com [209.85.215.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CD30D277007 for ; Sat, 16 May 2026 01:59:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896757; cv=none; b=o2u3BNPOdETiVLLnVqEewR77lhmi5qW/HyZnkljxvcUkUuvmlK9/Te//D/njk7KTozghVEPuO/WL2mRJfIr6lz6ec8wKjVSc2/4Cz+AYujFXNWiFhj/xy1HcDu9pX1iUB06XPtCJVp6LNE/u/JUccnvWxots2mzDhunPaPF+wvI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896757; c=relaxed/simple; bh=MjThCERKgOiyNs46b1Xie0y/FaEaMJ/jQ5MZThmrJlg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=O+V2PhnlPNf+QiDCzXEKo6+UI/mboBtzyuuotvNjz77gCk/boPq1dqHCOUOSDIsAPtZlGVJ3NwDhW6YxMMi8RDBqqQP4ObjnrZNCoK6prnh3WUIqVmbJZNWgYhrxF1NVdd8emqs6aUf5juylSty6h149zgEXOO3hEz0zDFGNI40= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XemVUah8; arc=none smtp.client-ip=209.85.215.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XemVUah8" Received: by mail-pg1-f181.google.com with SMTP id 41be03b00d2f7-c828daf83e2so167663a12.2 for ; Fri, 15 May 2026 18:59:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778896754; x=1779501554; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=xSU76uJ4Y1kHfzLYvNBsa2bJ7y1O6pl/Uvrz7foUqtA=; b=XemVUah83QlngGfPVT+iJgfy9E1umgYO09rX/Icgm6estZWX0Wy8OjAWioqk8HXzZx ZF/4QUfb1xskb+jen3xaqKiq1OaSxxyIh4r6tOXaBW1KgWl0AX8gvSw/l44WNUYNHrzE ch9Nzz7BnKLAff7pjmuXHDQsZeefuT+aGFWa2rAXZSZNFA+c5SdBw6fUSD/6gfPam9kF 6qDqSAkZfImU+PC60GvkvSlOO9hrsuW9ZLiJNvQI8ulW6U9U3Mo72m8dchRNSvb3NYDX DhXcbQrbhvfmngVW+kGWq7OYHOH7UWyv09IFceMBy5SzsqUcPX1JWEQZ5kYaHvfnos+X JP7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778896754; x=1779501554; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=xSU76uJ4Y1kHfzLYvNBsa2bJ7y1O6pl/Uvrz7foUqtA=; b=tQ6OcPyEC24p435+ANZuyj/V0zX7oSGXQ6X6IGhSv3lR+gZDin+k3SaDQCLg9L44Tn LLmksTwwgpTLUwTKUQ+eH9DSeIcx9539gA2MHDmsfmi5szWJ0MWUaviEdfE5nPpB1IE1 5ExqmcXZisJGnvEuRln0Zzaqh/MeNvP59lm0rZ3N1+rmXjrh7+HTwsSNs9qw18x/spbR LjFizydPTVeRf+fqBGTrv7m2OOBgz943HESbtnQdHA4CbmQvMkxIFdZaKut90IM67LW/ 1+o2nwN6cgUKMjWWnNY1SZJzIMJBtdLiiMWdClcb37Lo4LlVuWUWu+EvALCCvQvUsd40 Mg4Q== X-Forwarded-Encrypted: i=1; AFNElJ9LWXqXQ9tGWsJmNsVtGY1rKMltiqqsYR2Q604UYwt6USlWpI+dhAvayKY1cWxMVJyNhOEkehQ/AG4XJgQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyiL7xbWY2MSKksczXd7E9KMHseOA6Q7oq7wXtn4EbcoJtccs2d UkHzVZbMq9T8dCTIq7UxVaxmuHddTRrsvhG4Gvuv9xSWS6SP262JYFNs5KENvUEX X-Gm-Gg: Acq92OHiNy95bdTqs3bQkMIczvieRPKUNox6TMbHdgZadK8NmqxnAGTo0KG16/2RAGa 3OP/3ngFixdG5WtT4Fab3vSDYOhcmf+bOgRPLun+RHjwF+6qLA24NPKPznXqzm5qzaCs3EzvskA 8TzFDeyiNruDJxsOu+tj1kBgf+V/iZnLH3MYkMwO9472mw9zc4qyvSbLyJPo1kdmhbVNa/1T/Vv h2ur7jK3EH0qRCpJis7YWqRWIYM02iHSDfIU46D5Ksc6ywDv+2DK3ISg6ix2O7mXx7I9Y0s/qqE 1Ol23fdG86zcDcsSK7emjIbipFJ4uV5to4vmJy5sVmMirnt7KqqcCXE8vRQFF0rH85nwztV26JU I9gRYagIyUeFYOsseZDr17MIrwhWSmkiCnQVmfAc6hns0wzSayx0yF0giy62JwX7ZWsKALBrotX 1/mcgrVoZh5MCmGv8gPG0vqoTeHulHXOfuthd/LddE8V/DjdW8y6bMTWXV6giRr+IaXT7xdL48z fvffjUy2x62pbSPTdSALNho7uj4DgVyGryf+w== X-Received: by 2002:a05:6a20:939d:b0:3a2:d68d:9e83 with SMTP id adf61e73a8af0-3b22e6682e1mr7561639637.5.1778896753857; Fri, 15 May 2026 18:59:13 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c82bb07b007sm6605080a12.11.2026.05.15.18.59.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 18:59:13 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, stephan.gerhold@kernkonzept.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v9 4/5] virtio: add noirq system sleep PM infrastructure Date: Sat, 16 May 2026 10:57:55 +0900 Message-Id: <20260516015756.20948-5-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260516015756.20948-1-baver.bae@gmail.com> References: <20260516015756.20948-1-baver.bae@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Sungho Bae Some virtio-mmio devices, such as virtio-clock or virtio-regulator, must become operational before the regular PM restore callback runs because other devices may depend on them. Add the core infrastructure needed to support noirq system-sleep PM callbacks for virtio transports: - virtio_add_status_noirq(): status helper without might_sleep(). - virtio_features_ok_noirq(): feature negotiation without might_sleep(). - virtio_reset_device_noirq(): device reset that skips virtio_synchronize_cbs() (IRQ handlers are already quiesced in the noirq phase). - virtio_device_reinit_noirq(): full noirq bring-up sequence using the above helpers. - virtio_config_core_enable_noirq(): config enable with irqsave locking. - virtio_device_ready_noirq(): marks DRIVER_OK without virtio_synchronize_cbs(). Not all transports can safely call reset, get_status, set_status, or finalize_features during the noirq phase: transports like virtio-ccw issue channel commands and wait for a completion interrupt, which will never be delivered because device interrupts are masked at the interrupt controller during noirq suspend/resume. To address this, introduce a boolean field noirq_safe in struct virtio_config_ops. Transports that implement the above operations via simple MMIO reads/writes (e.g. virtio-mmio) set this flag; all others leave it at the default false. The noirq helpers assert noirq_safe via WARN_ON at runtime. virtio_device_freeze() enforces the contract at freeze time, returning -EOPNOTSUPP early if the driver provides restore_noirq but the transport does not meet the requirements, to prevent a deadlock on resume. virtio_device_freeze_noirq() and virtio_device_restore_noirq() perform secondary checks as safety nets. Add freeze_noirq/restore_noirq callbacks to struct virtio_driver and provide matching helper wrappers in the virtio core: - virtio_device_freeze_noirq(): validates noirq_safe and reset_vqs requirements, then forwards to drv->freeze_noirq(). - virtio_device_restore_noirq(): guards against unsafe transports, runs the noirq bring-up sequence, resets existing vrings via the new config_ops->reset_vqs() hook, then calls drv->restore_noirq(). Modify virtio_device_restore() so that when a driver provides restore_noirq, the normal-phase restore skips the re-initialization that was already done in the noirq phase. Signed-off-by: Sungho Bae --- drivers/virtio/virtio.c | 305 +++++++++++++++++++++++++++++++++- include/linux/virtio.h | 42 +++++ include/linux/virtio_config.h | 39 +++++ 3 files changed, 382 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 98f1875f8df1..97a3ed5c2985 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -193,6 +193,17 @@ static void virtio_config_core_enable(struct virtio_de= vice *dev) spin_unlock_irq(&dev->config_lock); } =20 +static void virtio_config_core_enable_noirq(struct virtio_device *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->config_lock, flags); + dev->config_core_enabled =3D true; + if (dev->config_change_pending) + __virtio_config_changed(dev); + spin_unlock_irqrestore(&dev->config_lock, flags); +} + void virtio_add_status(struct virtio_device *dev, unsigned int status) { might_sleep(); @@ -200,6 +211,21 @@ void virtio_add_status(struct virtio_device *dev, unsi= gned int status) } EXPORT_SYMBOL_GPL(virtio_add_status); =20 +/* + * Same as virtio_add_status() but without the might_sleep() assertion, + * so it is safe to call from noirq context. + * + * Requires the transport to have set config_ops->noirq_safe, which declar= es + * that reset, get_status, and set_status do not wait for a completion + * interrupt and are therefore safe during the noirq PM phase. + */ +void virtio_add_status_noirq(struct virtio_device *dev, unsigned int statu= s) +{ + WARN_ON(!dev->config->noirq_safe); + dev->config->set_status(dev, dev->config->get_status(dev) | status); +} +EXPORT_SYMBOL_GPL(virtio_add_status_noirq); + /* Do some validation, then set FEATURES_OK */ static int virtio_features_ok(struct virtio_device *dev) { @@ -234,6 +260,32 @@ static int virtio_features_ok(struct virtio_device *de= v) return 0; } =20 +/* noirq-safe variant: no might_sleep(), uses virtio_add_status_noirq() */ +static int virtio_features_ok_noirq(struct virtio_device *dev) +{ + unsigned int status; + + /* + * Skip virtio_check_mem_acc_cb() here: it may sleep (e.g. Xen's + * xen_virtio_restricted_mem_acc() calls devm_kzalloc with GFP_KERNEL). + * The check was already performed during probe in virtio_features_ok(); + * features cannot change across suspend/resume so the constraint is + * still satisfied. + */ + + if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) + return 0; + + virtio_add_status_noirq(dev, VIRTIO_CONFIG_S_FEATURES_OK); + status =3D dev->config->get_status(dev); + if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) { + dev_err(&dev->dev, "virtio: device refuses features: %x\n", + status); + return -ENODEV; + } + return 0; +} + /** * virtio_reset_device - quiesce device for removal * @dev: the device to reset @@ -267,6 +319,28 @@ void virtio_reset_device(struct virtio_device *dev) } EXPORT_SYMBOL_GPL(virtio_reset_device); =20 +/** + * virtio_reset_device_noirq - noirq-safe variant of virtio_reset_device() + * @dev: the device to reset + * + * Requires the transport to have set config_ops->noirq_safe. + */ +void virtio_reset_device_noirq(struct virtio_device *dev) +{ + WARN_ON(!dev->config->noirq_safe); + +#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION + /* + * The noirq stage runs with device IRQ handlers disabled, so + * virtio_synchronize_cbs() must not be called here. + */ + virtio_break_device(dev); +#endif + + dev->config->reset(dev); +} +EXPORT_SYMBOL_GPL(virtio_reset_device_noirq); + static int virtio_dev_probe(struct device *_d) { int err, i; @@ -539,6 +613,7 @@ int register_virtio_device(struct virtio_device *dev) dev->config_driver_disabled =3D false; dev->config_core_enabled =3D false; dev->config_change_pending =3D false; + dev->noirq_state =3D VIRTIO_NOIRQ_NONE; =20 INIT_LIST_HEAD(&dev->vqs); spin_lock_init(&dev->vqs_list_lock); @@ -618,7 +693,63 @@ static int virtio_device_reinit(struct virtio_device *= dev) return virtio_features_ok(dev); } =20 +/* + * noirq-safe variant of virtio_device_reinit(). + * + * Requires the transport to declare config_ops->noirq_safe, which means + * reset, get_status, set_status, and finalize_features are safe to call + * during the noirq PM phase. + */ +static int virtio_device_reinit_noirq(struct virtio_device *dev) +{ + struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); + int ret; + + /* + * We always start by resetting the device, in case a previous + * driver messed it up. + */ + virtio_reset_device_noirq(dev); + + /* Acknowledge that we've seen the device. */ + virtio_add_status_noirq(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); + + /* + * Maybe driver failed before freeze. + * Restore the failed status, for debugging. + */ + if (dev->failed) + virtio_add_status_noirq(dev, VIRTIO_CONFIG_S_FAILED); + + if (!drv) + return 0; + + /* We have a driver! */ + virtio_add_status_noirq(dev, VIRTIO_CONFIG_S_DRIVER); + + ret =3D dev->config->finalize_features(dev); + if (ret) + return ret; + + return virtio_features_ok_noirq(dev); +} + #ifdef CONFIG_PM_SLEEP +static inline bool virtio_has_valid_pm_cbs(struct virtio_driver *drv) +{ + /* Each callback pair must be fully implemented or fully absent. */ + bool has_freeze =3D drv->freeze; + bool has_restore =3D drv->restore; + bool has_freeze_noirq =3D drv->freeze_noirq; + bool has_restore_noirq =3D drv->restore_noirq; + + if (has_freeze !=3D has_restore) + return false; + if (has_freeze_noirq !=3D has_restore_noirq) + return false; + return true; +} + int virtio_device_freeze(struct virtio_device *dev) { struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); @@ -627,6 +758,34 @@ int virtio_device_freeze(struct virtio_device *dev) virtio_config_core_disable(dev); =20 dev->failed =3D dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; + dev->noirq_state =3D VIRTIO_NOIRQ_NONE; + + /* + * freeze_noirq and restore_noirq must be implemented as a pair. + * freeze_noirq performs full device teardown that only + * restore_noirq knows how to undo, and restore_noirq assumes + * freeze_noirq prepared the device for re-initialization. + */ + if (drv && !virtio_has_valid_pm_cbs(drv)) { + dev_warn(&dev->dev, + "freeze/restore and freeze_noirq/restore_noirq must each be paired\n"); + virtio_config_core_enable(dev); + return -EINVAL; + } + + /* + * If the driver provides noirq callbacks, verify that the + * transport supports noirq PM. The driver's freeze_noirq or + * restore_noirq may call transport ops (reset, get_status, + * set_status) that could wait for an interrupt that will never + * arrive if the transport is not noirq-safe. + */ + if (drv && drv->restore_noirq && !dev->config->noirq_safe) { + dev_warn(&dev->dev, + "transport does not support noirq PM\n"); + virtio_config_core_enable(dev); + return -EOPNOTSUPP; + } =20 if (drv && drv->freeze) { ret =3D drv->freeze(dev); @@ -645,12 +804,42 @@ int virtio_device_restore(struct virtio_device *dev) struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); int ret; =20 - ret =3D virtio_device_reinit(dev); - if (ret) + /* + * If the driver implements restore_noirq and the noirq phase was + * actually entered (freeze_noirq ran), but restore_noirq did not + * complete successfully, the noirq phase must have failed. PM core + * may continue later resume phases for global recovery, but virtio + * does not use the normal restore path as an implicit same-device + * fallback. + */ + if (drv && drv->restore_noirq && + dev->noirq_state =3D=3D VIRTIO_NOIRQ_ENTERED) { + ret =3D -EIO; goto err; + } =20 - if (!drv) - return 0; + /* + * Re-initialization is needed only for drivers that do not + * implement restore_noirq. When restore_noirq exists, either: + * - NOIRQ_NONE: noirq phase was never entered, so no noirq-specific + * teardown occurred and the device is still live. + * - NOIRQ_RESTORED: noirq phase already performed reinit. + * (NOIRQ_ENTERED is caught above as -EIO.) + * + * Note: when a driver implements restore_noirq, freeze() must NOT + * perform full device teardown (e.g., must not destroy virtqueues). + * Full teardown is deferred to freeze_noirq(). If suspend is aborted + * before the noirq phase, restore() is called to undo only the + * partial quiesce performed by freeze(), with the device still live + * and in DRIVER_OK state. + */ + if (!drv || !drv->restore_noirq) { + ret =3D virtio_device_reinit(dev); + if (ret) + goto err; + if (!drv) + return 0; + } =20 if (drv->restore) { ret =3D drv->restore(dev); @@ -671,6 +860,114 @@ int virtio_device_restore(struct virtio_device *dev) return ret; } EXPORT_SYMBOL_GPL(virtio_device_restore); + +int virtio_device_freeze_noirq(struct virtio_device *dev) +{ + struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); + + if (!drv) + return 0; + + /* + * Pairing is validated in virtio_device_freeze(); since both + * callbacks must co-exist, checking one implies the other. + * + * Verify the transport supports noirq PM. This should normally + * have been caught at freeze time, but guard here as well. + */ + if (drv->freeze_noirq && !dev->config->noirq_safe) { + dev_warn(&dev->dev, + "transport does not support noirq PM\n"); + return -EOPNOTSUPP; + } + + /* + * If the driver provides noirq callbacks and has active vqs, + * the transport must support reset_vqs to restore them. + * Fail here so the PM core can abort the transition gracefully, + * rather than hitting -EOPNOTSUPP on resume. + */ + if (drv->freeze_noirq && !list_empty(&dev->vqs) && + !dev->config->reset_vqs) { + dev_warn(&dev->dev, + "transport does not support noirq PM restore with active vqs (missing = reset_vqs)\n"); + return -EOPNOTSUPP; + } + + /* + * Invoke the driver's freeze_noirq callback and mark noirq + * phase entered on success. Pairing is enforced in + * virtio_device_freeze(), so restore_noirq also exists. + * + * If freeze_noirq fails, the driver must have rolled back to + * the pre-call state (per kernel PM convention), so + * noirq_state remains NONE to allow restore() to proceed. + */ + if (drv->freeze_noirq) { + int ret =3D drv->freeze_noirq(dev); + + if (!ret) + dev->noirq_state =3D VIRTIO_NOIRQ_ENTERED; + + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(virtio_device_freeze_noirq); + +int virtio_device_restore_noirq(struct virtio_device *dev) +{ + struct virtio_driver *drv =3D drv_to_virtio(dev->dev.driver); + int ret; + + if (!drv || !drv->restore_noirq) + return 0; + + /* + * All transport ops called below (reset, get_status, set_status) must + * be noirq-safe. Return early if not - this should normally have + * been caught at freeze_noirq time. + */ + if (!dev->config->noirq_safe) { + dev_warn(&dev->dev, + "transport does not support noirq PM; skipping restore\n"); + return -EOPNOTSUPP; + } + + ret =3D virtio_device_reinit_noirq(dev); + if (ret) + goto err; + + if (!list_empty(&dev->vqs)) { + if (!dev->config->reset_vqs) { + ret =3D -EOPNOTSUPP; + goto err; + } + + ret =3D dev->config->reset_vqs(dev); + if (ret) + goto err; + } + + ret =3D drv->restore_noirq(dev); + if (ret) + goto err; + + /* Mark that noirq restore has completed successfully. */ + dev->noirq_state =3D VIRTIO_NOIRQ_RESTORED; + + /* If restore_noirq set DRIVER_OK, enable config now. */ + if (dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK) + virtio_config_core_enable_noirq(dev); + + return 0; + +err: + virtio_add_status_noirq(dev, VIRTIO_CONFIG_S_FAILED); + return ret; +} +EXPORT_SYMBOL_GPL(virtio_device_restore_noirq); #endif =20 int virtio_device_reset_prepare(struct virtio_device *dev) diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 3bbc4cb6a672..937bc3c56bb8 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -143,6 +143,18 @@ struct virtio_admin_cmd { int ret; }; =20 +/** + * enum virtio_noirq_state - tracks noirq PM phase progress + * @VIRTIO_NOIRQ_NONE: noirq phase was not entered (only freeze ran) + * @VIRTIO_NOIRQ_ENTERED: freeze_noirq ran; restore_noirq is expected + * @VIRTIO_NOIRQ_RESTORED: restore_noirq completed successfully + */ +enum virtio_noirq_state { + VIRTIO_NOIRQ_NONE, + VIRTIO_NOIRQ_ENTERED, + VIRTIO_NOIRQ_RESTORED, +}; + /** * struct virtio_device - representation of a device using virtio * @index: unique position on the virtio bus @@ -151,6 +163,7 @@ struct virtio_admin_cmd { * @config_driver_disabled: configuration change reporting disabled by * a driver * @config_change_pending: configuration change reported while disabled + * @noirq_state: tracks noirq PM phase progress for restore coordination * @config_lock: protects configuration change reporting * @vqs_list_lock: protects @vqs. * @dev: underlying device. @@ -171,6 +184,7 @@ struct virtio_device { bool config_core_enabled; bool config_driver_disabled; bool config_change_pending; + enum virtio_noirq_state noirq_state; spinlock_t config_lock; spinlock_t vqs_list_lock; struct device dev; @@ -209,8 +223,12 @@ void virtio_config_driver_enable(struct virtio_device = *dev); #ifdef CONFIG_PM_SLEEP int virtio_device_freeze(struct virtio_device *dev); int virtio_device_restore(struct virtio_device *dev); +int virtio_device_freeze_noirq(struct virtio_device *dev); +int virtio_device_restore_noirq(struct virtio_device *dev); #endif void virtio_reset_device(struct virtio_device *dev); +void virtio_reset_device_noirq(struct virtio_device *dev); +void virtio_add_status_noirq(struct virtio_device *dev, unsigned int statu= s); int virtio_device_reset_prepare(struct virtio_device *dev); int virtio_device_reset_done(struct virtio_device *dev); =20 @@ -237,6 +255,28 @@ size_t virtio_max_dma_size(const struct virtio_device = *vdev); * changes; may be called in interrupt context. * @freeze: optional function to call during suspend/hibernation. * @restore: optional function to call on resume. + * When @restore_noirq is not implemented, core resets and reinitializes + * the device before calling this. When @restore_noirq succeeded, core + * skips reinitialization; drivers should avoid calling virtio_device_r= eady() + * if DRIVER_OK was already set in the noirq phase. + * When @restore_noirq failed, this callback is not invoked for same-de= vice + * recovery; the saved noirq error is propagated instead. + * When the noirq phase was entirely skipped (e.g. suspend aborted befo= re + * suspend_noirq), core skips reinitialization for drivers that impleme= nt + * @restore_noirq and calls @restore (if provided) to undo the freeze() + * quiesce. Drivers without @restore_noirq follow the normal reinit + + * restore path. + * @freeze_noirq: optional function to call during noirq suspend/hibernati= on. + * @restore_noirq: optional function to call on noirq resume. + * If this callback fails, PM core may still continue later resume phas= es + * for global system recovery. Virtio does not treat @restore as an + * implicit same-device fallback for @restore_noirq failure; drivers sh= ould + * only implement @restore_noirq when noirq resume is their required + * recovery point. + * A noirq restore failure is detected by the normal restore path + * (noirq_state =3D=3D VIRTIO_NOIRQ_ENTERED, meaning freeze_noirq ran b= ut + * restore_noirq did not complete) and returns -EIO instead of attempti= ng + * same-device recovery. * @reset_prepare: optional function to call when a transport specific res= et * occurs. * @reset_done: optional function to call after transport specific reset @@ -258,6 +298,8 @@ struct virtio_driver { void (*config_changed)(struct virtio_device *dev); int (*freeze)(struct virtio_device *dev); int (*restore)(struct virtio_device *dev); + int (*freeze_noirq)(struct virtio_device *dev); + int (*restore_noirq)(struct virtio_device *dev); int (*reset_prepare)(struct virtio_device *dev); int (*reset_done)(struct virtio_device *dev); void (*shutdown)(struct virtio_device *dev); diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 69f84ea85d71..0110b091f634 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -70,6 +70,9 @@ struct virtqueue_info { * vqs_info: array of virtqueue info structures * Returns 0 on success or error status * @del_vqs: free virtqueues found by find_vqs(). + * @reset_vqs: reinitialize existing virtqueues without allocating or + * freeing them (optional). Used during noirq restore. + * Returns 0 on success or error status. * @synchronize_cbs: synchronize with the virtqueue callbacks (optional) * The function guarantees that all memory operations on the * queue before it are visible to the vring_interrupt() that is @@ -108,6 +111,14 @@ struct virtqueue_info { * Returns 0 on success or error status * If disable_vq_and_reset is set, then enable_vq_after_reset must also be * set. + * @noirq_safe: set to true if @reset, @get_status, @set_status, and + * @finalize_features are safe to call during the noirq phase of system + * suspend/resume. Transports that implement these operations via simple + * MMIO reads/writes (e.g. virtio-mmio) can set this flag. Transports + * that issue channel commands and wait for a completion interrupt (e.g. + * virtio-ccw) must NOT set it, because device interrupts are masked at + * the interrupt controller during the noirq phase, which would cause the + * wait to hang. */ struct virtio_config_ops { void (*get)(struct virtio_device *vdev, unsigned offset, @@ -123,6 +134,7 @@ struct virtio_config_ops { struct virtqueue_info vqs_info[], struct irq_affinity *desc); void (*del_vqs)(struct virtio_device *); + int (*reset_vqs)(struct virtio_device *vdev); void (*synchronize_cbs)(struct virtio_device *); u64 (*get_features)(struct virtio_device *vdev); void (*get_extended_features)(struct virtio_device *vdev, @@ -137,6 +149,7 @@ struct virtio_config_ops { struct virtio_shm_region *region, u8 id); int (*disable_vq_and_reset)(struct virtqueue *vq); int (*enable_vq_after_reset)(struct virtqueue *vq); + bool noirq_safe; }; =20 /** @@ -371,6 +384,32 @@ void virtio_device_ready(struct virtio_device *dev) dev->config->set_status(dev, status | VIRTIO_CONFIG_S_DRIVER_OK); } =20 +/** + * virtio_device_ready_noirq - noirq-safe variant of virtio_device_ready() + * @dev: the virtio device + * + * Requires the transport to have set config_ops->noirq_safe, which declar= es + * that get_status and set_status do not wait for a completion interrupt. + */ +static inline +void virtio_device_ready_noirq(struct virtio_device *dev) +{ + unsigned int status =3D dev->config->get_status(dev); + + WARN_ON(!dev->config->noirq_safe); + WARN_ON(status & VIRTIO_CONFIG_S_DRIVER_OK); + +#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION + /* + * The noirq stage runs with device IRQ handlers disabled, so + * virtio_synchronize_cbs() must not be called here. + */ + __virtio_unbreak_device(dev); +#endif + + dev->config->set_status(dev, status | VIRTIO_CONFIG_S_DRIVER_OK); +} + static inline const char *virtio_bus_name(struct virtio_device *vdev) { --=20 2.34.1 From nobody Mon May 25 08:10:51 2026 Received: from mail-pg1-f170.google.com (mail-pg1-f170.google.com [209.85.215.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C53D926CE2C for ; Sat, 16 May 2026 01:59:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896759; cv=none; b=PvTapqkEM6XhzOofYGYKyW1BUmxqy9BaKS2Sf3FKtpNUHpix8Ky/bPOFcbOqW8ADKmIbHoI1K+nCVC1WjMsHG7m1OthwypJl8XkY5aUdSJLnpsVaOqt6qoDb1xBQYC/EfivIo0xJZVTjJuDapD3ltq+DCexSvtQMRYR3bRyVA30= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778896759; c=relaxed/simple; bh=10Pv4wCRLRsYVFcl2tTr/I+ll5JXYgM6y1NwK70LFG0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RiIibLCxUINSw5SzmuLTMU2oPaQEupoN/ECia4WPDfXK+bKd8CeCdQysXW03q5K04N5IFy2lcWiZMsm0/yKlFMKZNGshoHkQuC5f19nXB07BgWaqGL2Nn6UHc01XLyeHufku3t204TEpJ846oP/mXeJ/2pirjM339bqh7wJ3tbc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=KOe7bClt; arc=none smtp.client-ip=209.85.215.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="KOe7bClt" Received: by mail-pg1-f170.google.com with SMTP id 41be03b00d2f7-c801b30188dso157871a12.3 for ; Fri, 15 May 2026 18:59:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778896757; x=1779501557; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=hrmjFxK/MuxHR0Xepi3mNFkJ9BfGyVR/W7w3N4yVTKs=; b=KOe7bClta9CJoJfRYxIqeAZVQ1mxkrAGMXNfVoZdms7q0DQNEwNQ9nG87iucXFmYgU CJGQGKMts7SGe6VHwpkZLUMH2AFhQByxl4wQU8BBDdPBAZo6HQ45J3ZLp71kXvFAXAaP uWLSKVmU4lTAasX0PuAIsDx8dnA+G1hKDIc2AiY5Rit51RGnb+Y3VJzfEc7h3umtUYei 8mv9Tsv5XmStibXNeANONxLWonBnbdP3f2c5NXcIDW0JxDH6xO0qfDueHJ2+Q1ti5Y/a SUNbFj0oGu+1IlByPi/Ohve5l3/XUO6+LYN5TWKAHBpgW9ljO/nbeZklD1VDPDO3judC G24w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778896757; x=1779501557; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=hrmjFxK/MuxHR0Xepi3mNFkJ9BfGyVR/W7w3N4yVTKs=; b=cRiCH7iPc93fc6gmc/6X6mNYVUwIi25tpX+yfb5Tl9R8MM3X7CJUgkYhd5/VdDhe4V DH5IGprk91dwP0RQYRlQPmuaQAnRBz0hEXsx37OffkeWxgIxT+kEZHlFsuvXCg6XJFk1 U/TQnqns1h1Kfmt98YEwAL0uOrkM/+M5fS2wsE3TgBZ/Xdr4PMQxTndIh/EjBcdIlz34 tymnjgCOcNQN4gkDozHTOASyN0hYBtrY7G+gGOyOakeY/n3ngFi7Ch3ZUPJ/YrT+GT4k cnoabd/YQUqciXTingqN3d3+KF4fsFbSlobR4mg2LSath+a6caIV08Vb/3i8txg6i/ja rx+A== X-Forwarded-Encrypted: i=1; AFNElJ/JxZeH8/ZkWh2xyh6Va7UcNVNPxNN+VQhq22TWxWIWJkNS/1JJ0UAJ31S3hhDW9AFhigAPZLdQsV8BFco=@vger.kernel.org X-Gm-Message-State: AOJu0Yz4gRrry8sdk2ISOfKfd4F9R7Dua2kZlQOJdQQwx7t6g17EjwH5 zaukpOnTz33aFo27SUf+UHaQJM/OStofBobS9qk+9+GDXSyEVNX7l79G X-Gm-Gg: Acq92OH5q+Q5Tu1euW+QXoGxrWlyIjziYW9Uwi2BwnATuCsVkE7SpiARuJae6+JFWzG q3dToqPRXUgRxr+UNTWjT9Omm5SDsYjNO0UqjI5iJYzyhtlkUTG6IJgxTGmsyZjuXPutnYRv2t0 9G/NxOavzjDNN/2fP3fiUrBg+52jo+zt9T8d+I6qNftdk4d2+S6uETr5NWwoVxMp2/Fso2llo/s PEEtudBDpyDjMktEj4Y1oiuQlNgy96GgHulM4aJvylNqtTpYxyZE9ZfJiS8IG1zeq2EDkre5JHf XYogST7Du9jQ296ymwhTWO2o4FUSiG2dJyLUcKvQRShsPDzXf/Y8Dp1T+gW0z5WyumsoiPqjL7x bWZ09+Watrq29WvnRPU+R/0ULWw4LH0vPNe9XD9mxhSAA7YLEVvtVk1leT5mjiJQ/xQ58cZnlCM F7R2ZU4GpSMlLb9kRJ+zch6LWXLf+sn/oshbhdSl0Wqi4XjnIxdxs9AYeDc8gM7JMBheV3d1J7N la0CXIbluDTjEKr79Er6X54+ZrbYXyi1bzDKg== X-Received: by 2002:a05:6a20:e212:b0:3a3:a5cd:5621 with SMTP id adf61e73a8af0-3b22ecf84f9mr7273028637.51.1778896756599; Fri, 15 May 2026 18:59:16 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c82bb07b007sm6605080a12.11.2026.05.15.18.59.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 18:59:16 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, stephan.gerhold@kernkonzept.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v9 5/5] virtio-mmio: wire up noirq system sleep PM callbacks Date: Sat, 16 May 2026 10:57:56 +0900 Message-Id: <20260516015756.20948-6-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260516015756.20948-1-baver.bae@gmail.com> References: <20260516015756.20948-1-baver.bae@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Sungho Bae Add noirq system-sleep PM support to the virtio-mmio transport. This change wires noirq freeze/restore callbacks into virtio-mmio and hooks queue reset/reactivation into the transport config ops so virtqueues can be reinitialized and reused across suspend/resume. This enables virtio-mmio based devices to participate safely in the noirq PM phase, which is required for early-restore users. Signed-off-by: Sungho Bae --- drivers/virtio/virtio_mmio.c | 131 ++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 39 deletions(-) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index daa65b269a36..fbad0ee8312f 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -346,6 +346,77 @@ static void vm_del_vqs(struct virtio_device *vdev) free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev); } =20 +static int vm_active_vq(struct virtio_device *vdev, struct virtqueue *vq) +{ + struct virtio_mmio_device *vm_dev =3D to_virtio_mmio_device(vdev); + int q_num =3D virtqueue_get_vring_size(vq); + + writel(q_num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); + if (vm_dev->version =3D=3D 1) { + u64 q_pfn =3D virtqueue_get_desc_addr(vq) >> PAGE_SHIFT; + + /* + * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something + * that doesn't fit in 32bit, fail the setup rather than + * pretending to be successful. + */ + if (q_pfn >> 32) { + dev_err(&vdev->dev, + "platform bug: legacy virtio-mmio must not be used with RAM above 0x%l= lxGB\n", + 0x1ULL << (32 + PAGE_SHIFT - 30)); + return -E2BIG; + } + + writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); + writel(q_pfn, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + } else { + u64 addr; + + addr =3D virtqueue_get_desc_addr(vq); + writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW); + writel((u32)(addr >> 32), + vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH); + + addr =3D virtqueue_get_avail_addr(vq); + writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW); + writel((u32)(addr >> 32), + vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); + + addr =3D virtqueue_get_used_addr(vq); + writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW); + writel((u32)(addr >> 32), + vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH); + + writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); + } + + return 0; +} + +static int vm_reset_vqs(struct virtio_device *vdev) +{ + struct virtio_mmio_device *vm_dev =3D to_virtio_mmio_device(vdev); + struct virtqueue *vq; + int err; + + virtio_device_for_each_vq(vdev, vq) { + /* Re-initialize vring state */ + err =3D virtqueue_reinit_vring(vq); + if (err < 0) + return err; + + /* Select the queue we're interested in */ + writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); + + /* Activate the queue */ + err =3D vm_active_vq(vdev, vq); + if (err < 0) + return err; + } + + return 0; +} + static void vm_synchronize_cbs(struct virtio_device *vdev) { struct virtio_mmio_device *vm_dev =3D to_virtio_mmio_device(vdev); @@ -398,45 +469,9 @@ static struct virtqueue *vm_setup_vq(struct virtio_dev= ice *vdev, unsigned int in vq->num_max =3D num; =20 /* Activate the queue */ - writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM= ); - if (vm_dev->version =3D=3D 1) { - u64 q_pfn =3D virtqueue_get_desc_addr(vq) >> PAGE_SHIFT; - - /* - * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something - * that doesn't fit in 32bit, fail the setup rather than - * pretending to be successful. - */ - if (q_pfn >> 32) { - dev_err(&vdev->dev, - "platform bug: legacy virtio-mmio must not be used with RAM above 0x%l= lxGB\n", - 0x1ULL << (32 + PAGE_SHIFT - 30)); - err =3D -E2BIG; - goto error_bad_pfn; - } - - writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); - writel(q_pfn, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); - } else { - u64 addr; - - addr =3D virtqueue_get_desc_addr(vq); - writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW); - writel((u32)(addr >> 32), - vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH); - - addr =3D virtqueue_get_avail_addr(vq); - writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW); - writel((u32)(addr >> 32), - vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); - - addr =3D virtqueue_get_used_addr(vq); - writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW); - writel((u32)(addr >> 32), - vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH); - - writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); - } + err =3D vm_active_vq(vdev, vq); + if (err < 0) + goto error_bad_pfn; =20 return vq; =20 @@ -538,11 +573,13 @@ static const struct virtio_config_ops virtio_mmio_con= fig_ops =3D { .reset =3D vm_reset, .find_vqs =3D vm_find_vqs, .del_vqs =3D vm_del_vqs, + .reset_vqs =3D vm_reset_vqs, .get_features =3D vm_get_features, .finalize_features =3D vm_finalize_features, .bus_name =3D vm_bus_name, .get_shm_region =3D vm_get_shm_region, .synchronize_cbs =3D vm_synchronize_cbs, + .noirq_safe =3D true, }; =20 #ifdef CONFIG_PM_SLEEP @@ -560,8 +597,24 @@ static int virtio_mmio_restore(struct device *dev) return virtio_device_restore(&vm_dev->vdev); } =20 +static int virtio_mmio_freeze_noirq(struct device *dev) +{ + struct virtio_mmio_device *vm_dev =3D dev_get_drvdata(dev); + + return virtio_device_freeze_noirq(&vm_dev->vdev); +} + +static int virtio_mmio_restore_noirq(struct device *dev) +{ + struct virtio_mmio_device *vm_dev =3D dev_get_drvdata(dev); + + return virtio_device_restore_noirq(&vm_dev->vdev); +} + static const struct dev_pm_ops virtio_mmio_pm_ops =3D { SET_SYSTEM_SLEEP_PM_OPS(virtio_mmio_freeze, virtio_mmio_restore) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(virtio_mmio_freeze_noirq, + virtio_mmio_restore_noirq) }; #endif =20 --=20 2.34.1