From nobody Wed Jun 17 07:20:37 2026 Received: from mail-pf1-f170.google.com (mail-pf1-f170.google.com [209.85.210.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 9536F3A960A for ; Thu, 23 Apr 2026 17:40:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966057; cv=none; b=D1AmFov42RNdAnQrstfm0yNDrA1eq0h9RfcC1WrTIxihJDSg4tHlvL0hxy6DFFZkHIOc/WOKnXxBTfQJdakPEExuPwbkQuW1W44R6wzPMSnEAmZP506rw/AyXe+nMuBTuXaoqYwxXzRDWl9U2DiHUGt+K8TEx1wA2K+xhnWsOqo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966057; c=relaxed/simple; bh=ElyyymLN3BzEU2n3paseyXgS1oFdDA7FSr0KdZ24I/8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZKDi4+mFeQSIVM2E9Yohn7HgXY9z4hBH4MrJGpo+FOO5AxNP3dvRo2264PGSSMCdJFtpWWrDohBUjGdNK04ChY8dXgQxeqYkKtcUmHXMExHnQMv0Etg5UJtLZ4lv9ULrurB2/z1kPaT3DVz+rioedl8pDpItFvciFLrPiXC+igM= 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=M5FLU98J; arc=none smtp.client-ip=209.85.210.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="M5FLU98J" Received: by mail-pf1-f170.google.com with SMTP id d2e1a72fcca58-82f1bfc9b8fso3217682b3a.1 for ; Thu, 23 Apr 2026 10:40:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776966055; x=1777570855; 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=lmg393/U6YNbXTTx1LhzYWJYt4NvUfYyO+r0SFXM9Jg=; b=M5FLU98JnBuSFRRrLhhSqXcGo6WZgWHe+5rZMWyClQx+HTj9UllxGOQJKnlyF0Owmt dFVZdBPH8RdDmVl9z3nEnJEUVE2ts9yE/wNYpNhAP4yn+Gupdi8QhudTylPK/w4eqqY4 kJoOLhOYGw7rW1hC5sK0pFfFA9x0FztoTwxYBxyec+q+FJ5oyanuamhpLexnEPt56KUl +vjQYrlrh4DTEVFI76V2pXXV6kbswr02VTEt+jplicyQpCb4G7yr+HreXJnkPEvOz9ow Z78XvXE5FPsVVBRB1QZNeVkdQ5nG6On+BseehDg9AXy7AiXiMwWhkCF5KFskS9Al2GFM OuoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776966055; x=1777570855; 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=lmg393/U6YNbXTTx1LhzYWJYt4NvUfYyO+r0SFXM9Jg=; b=dw4d/3v+S4c08p47f5wqU0oasOlj/APY6+TVzL483XJAHdQ+n8Pt+bX0NDViHwkvoB G541MkLo6Yur7oTQPuN+p7fAfLnLzTu3xMeWc5mSChVLHFtl355PnOU+0vPMh/ovJdzQ D1J6pG2/4KlHdWjJ2ZWU7xHKGFNAbbuO7uM9MMxAYspMLBh31m5cbWrPXIlg2w83asZd 6zzOTeVVevuT1ojWeWTBQOXEQd7DdJPT8wo05JjfzTutfBR7BfqgDrohJ4cUtA+oXreX Spwzp4ZAneA/A/tUzIbu4uIoCTx8aOYYDpCRB7TfxkonKAIoQG4w9jJQEUGkvroTfj8W LKTA== X-Forwarded-Encrypted: i=1; AFNElJ8tmcda3nmwgzs7VjbagCDVuo/dx43nPQEWgw6pFtmVODVX7hNmqkDuI9TYFxBJmN+4TN/F/F6tXmTHDc4=@vger.kernel.org X-Gm-Message-State: AOJu0Yy179ZKDKdmQv3RTthDh/9ScupfPlyjTIj2FqjQhgdq2yn+XCSY PXBFxA+jTUCpkAuxbYOTLBH1TcAZWYRHsHva627lbqIkvlMht0tm4+3a X-Gm-Gg: AeBDieuhpFPOK/twuuKnhxXmmHtq0Og3svhZ4xkkMA/Xk3pi/Aj/QFGQau4Qy4F8RFd jVwlyL/CSy8nuN4OSITZQFtJaaJUFON6bszvSP+R5jAR9OJe4YNppnecbrDo7DIeUAQ+Lpgl2G9 oi9FMT0R6rilYOBKnGF9UA+EdzJn+ggNf53pUEAq/IK/mDBWtHJET9SzdfSxc5LWSZ6ttPPsXoK 24Fpmgud+EnNXIfASw7X/0rNq3M+Crfic8sd3vaWVHHtY8AoMzl8Q9HuuoMboW/p+WIqAFO6EuK h8tXT9YA7Exq3J54s8iTzRlL4SA00rPLqYclC4hR9Ly9EFEVsOWCG6j2no8FYkT/tUvQYMTDRTv tXHMio46IQhwHruJNHzkxytfU2x7ypmKL4w9aBXCOL9sbb96thLGBq5zk556fJRmin71zbmS57U x6BxTgeqkR/7eeNK1zYo/zjEOviPxNO/1M8joY/8MN4157AAG45jalxPpkpco5bp1waD+PBIdM2 8Vr9akURSOvDQunn0m7WVD4kihBQtD9XtdnvA== X-Received: by 2002:a05:6a00:1ac6:b0:829:8041:83f with SMTP id d2e1a72fcca58-82f8c7df387mr27720535b3a.7.1776966054769; Thu, 23 Apr 2026 10:40:54 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82f8ebcc9easm27014498b3a.39.2026.04.23.10.40.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Apr 2026 10:40:54 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v4 1/4] virtio: separate PM restore and reset_done paths Date: Fri, 24 Apr 2026 02:40:36 +0900 Message-Id: <20260423174039.276-2-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260423174039.276-1-baver.bae@gmail.com> References: <20260423174039.276-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.43.0 From nobody Wed Jun 17 07:20:37 2026 Received: from mail-pf1-f180.google.com (mail-pf1-f180.google.com [209.85.210.180]) (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 D41343A9DB1 for ; Thu, 23 Apr 2026 17:40:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966059; cv=none; b=eS/9fyF03npzWMl4k6PGdE07mF+vCYaKjkxZ82+aW9i9N3a0XTq6LyZeRr1r/SHwi8GYxj30FRPkrf1lIqkbf6vJtArCgzXvD6+ed7wQPyTVSpkIfFRCwkNgGdcZcBG3XwM9ViTh1byUL8jJKSx3V71pQVOanwM9rVhPLWcxs0I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966059; c=relaxed/simple; bh=Wp+rXTYV6cCR7GQckednAxBMcKVMT6SIRlSrHu1tg4E=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=AbDyUlWXyj2fiEEFgllRcGVn97TbLpS4uaCC/XfbFHqdpPypn3c8lTbZ0+LQ4bnqJTYlYxWn4CqF0h4s0Y7WJBn0aVdNEU5s5KT7uTcuwe3ax0Tb03gLHI+JnzYG4s1noyJwfUveoWYMs3VpErniRwmv2PxDMwPPhyKlY6GzgKI= 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=dmdfgOvQ; arc=none smtp.client-ip=209.85.210.180 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="dmdfgOvQ" Received: by mail-pf1-f180.google.com with SMTP id d2e1a72fcca58-82f4a53ae20so5136460b3a.3 for ; Thu, 23 Apr 2026 10:40:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776966057; x=1777570857; 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=zfrLDAY7+No90zlR6HffZqbGM74lFTvO1IbltHEt5HY=; b=dmdfgOvQIsyETR+//uYcHYtOHE9TZzX9XxGCBLhI55IHg3f3Mo0P1mW9u/vBwC0aGF pAyDLoQTAjt+8bKUvBjiSmcP9A6poKkakuD7dobZQRaJ9a253XAeK6HD3uKA9Aos6kxr FTFBk52f2OXVUejBrW2GOApjIcqvpuyJ0x5085Naw+VrTsEhd95ejr+YXdZQNOCMbbKq XMBk0M3Cl1TVKxYhSHmhPAHzb7oxA2qX3D2RWsP7jLy1yBYOSjFv1qa/zmB2Y22GmZwQ hiJ0gqi3hth9pc6e0esHCTLI8fn6eWFHYR6a6df08i2uRAXUpU+xZBdYkQsNkUpg0wtv k/Cw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776966057; x=1777570857; 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=zfrLDAY7+No90zlR6HffZqbGM74lFTvO1IbltHEt5HY=; b=pkOpVXc1yQl5ASGjpQIPCxirbJYHVQ2PMpvH5updm0aLjp3gISh09a/86a7bFtvCFw uAK6h6UTVrmw7mFVoXA40Lux19MmbWs6wIXsEAbY2DCgiRHO4282Ws5Iis5V0XjOCssN QYrTkkLarP4UgWSxw/LdUUEhO/C23lMPDatPcVO5Z5TZpVnyJex+aKdV0NkFWifC3t3L +M+P8/nQ2SzOgWZ9pv5IhHM/ngD7mJCwS07+02M4BObyOOoUs/MojU6TsI4nlirDKgVz kzVIbtd/UVrvtYIYye/hipXNO88HruH5ILPcGLSDIER2E6TxOtitl5ow2teABwCwKhHS WfnA== X-Forwarded-Encrypted: i=1; AFNElJ8Es//i/Tgqn7zt0rEPkvevJNepFRInEGCvD1uP/JglXvVtJfceXP3L8WCqxiA80zJXEQ2kLEjznyhfldE=@vger.kernel.org X-Gm-Message-State: AOJu0YwkCp9Ag0/m+sqs9wBYJHwwt6AjIqS1qGbQggCi75+LlnLi3soZ rYsQne47PONDLE26aj0BLW7iw3cZ7iud28J7nwLN1Uz3l4s+JdRR1I0T X-Gm-Gg: AeBDieu0y3Ixs8WwQyEsZIERKNfd3CaU+z0BJNbXHCmTJGJyHJ4ny0Qyp4+uqJuKahR 3E3gwtQdDy8ob3UqzRezIHoD+KhQOmKKj001sLDcznDCdZkGVqN6kNY21TLzZTUCeU8TdNruZOP V4/hdcPJ421T8Ulr9chNdoZs2iZ5nkEoi3X6/A53CllS5T/BtV6OA88NS3IrYQQLzOpBB0UwLrF 2dKynIWBRMhDXzN5ExDvYF2wrtphPGkNeZJyPpWWzoSqkb3z7dauEFmgBgZFIejhL7QUN+FhbOD 6tdrsWc5R3+ZRbNBoi0Ub9qk/gOlwIocMnr8LHUNTTrH1xvLBGTK3KcVl00n7NfsTlVP9nkQFtQ zAxdcjI+9sTLMEW3X2bD7cn3/35av+v5aOfQG4MqghTtBjBjebdoCBQVTfk7vR6qvnU6vQyG6/e RTLFkR6Llxo5FRRHFDGKuiPWAC1jdG+FWhCYC2mlQ654SOM8wPg6z5TXdZdjTyRUyjZPDkoqVOX u2TXjw8N+kRCB+9wIOpxBDQm/axrSxrNM2zyw== X-Received: by 2002:a05:6a00:e1a:b0:82f:7bec:2050 with SMTP id d2e1a72fcca58-82f8c90d174mr30741300b3a.39.1776966057112; Thu, 23 Apr 2026 10:40:57 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82f8ebcc9easm27014498b3a.39.2026.04.23.10.40.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Apr 2026 10:40:56 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v4 2/4] virtio_ring: export virtqueue_reinit_vring() for noirq restore Date: Fri, 24 Apr 2026 02:40:37 +0900 Message-Id: <20260423174039.276-3-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260423174039.276-1-baver.bae@gmail.com> References: <20260423174039.276-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 | 51 ++++++++++++++++++++++++++++++++++++ include/linux/virtio_ring.h | 3 +++ 2 files changed, 54 insertions(+) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index fbca7ce1c6bf..6631c30cb706 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,47 @@ 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. + */ +void 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 */ + WARN_ON(vq->vq.num_free !=3D num); + + 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. + */ + WARN_ON(vq->split.avail_idx_shadow !=3D + virtio16_to_cpu(vq->vq.vdev, + vq->split.vring.avail->idx)); + + virtqueue_reset_split(vq); + } +} +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..26c7c9d0a151 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) */ +void virtqueue_reinit_vring(struct virtqueue *_vq); + irqreturn_t vring_interrupt(int irq, void *_vq); =20 u32 vring_notification_data(struct virtqueue *_vq); --=20 2.43.0 From nobody Wed Jun 17 07:20:37 2026 Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.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 5BC263AC0C6 for ; Thu, 23 Apr 2026 17:41:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966062; cv=none; b=LXVegzAbnNh9OTVAvrbEIaQVwnG6+NoorHS+zr6Gh1sCPYG4zT/MaWnDuNZUEBJkjlImWrhcVBy4VBzbUZfHA5fYyBCPBl0SelZfTDsrjDox3tkBGJucqNuscnBa8wrWrDmVZV6xTkT/JXpwXMvtm39iVJYRrwp1zxwSUtPsdiY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966062; c=relaxed/simple; bh=ywihiWi5sgNV4rv0+p62fq6OahFHuO8mK0kqZSBO/hM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=hk3d/bsrC/hz1CpEdFX5tCLrZRqAYsyRip5g9goQzveNfYDrITeL5IneyFl9rK3QXt8cv9xwclu2H3JZQPFCZ+aHmV7H2Ka0MN98FuAyrKEI7WejSH9GAIHRElY5MEIMotXYaQj07/acui1f/CkT4W2epDBZ0snwVXL22j8H95M= 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=lDkd/1bI; arc=none smtp.client-ip=209.85.210.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="lDkd/1bI" Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-82f351ca23cso3607638b3a.2 for ; Thu, 23 Apr 2026 10:41:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776966060; x=1777570860; 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=qoHwCYhkOoOoCVpFvAAtjl8akaFWmoh5EGgeHJdhgck=; b=lDkd/1bIrymW6bvgANGTnxtxDD82rZvBNMFr1s8TwZfbqbj5XEXrXotlIDLANOBz25 Kq9AdTdKYc41btvqB3d4eoT+PDh6rNalJhFwqV/lbMOmDg07BodasvL27BkEG5veVJg1 TwR1U96pwf0yoKJ5y37fdv6ZxDx/DUNz0J0MY0j1pxHLxaSTlgSv21VaZDBctnkIXYag hXlhdZtennb/EDPhQ6saNFXqY011lEmQ1fbq0q0ZY8VmfbHIpAP/UtDYnFanO5GoGOw6 PTkTXK9+Cc4rPeiVyLLe1/CKA84+GI4WamdSgS3MAjbwq2vUuRBkECD2hZJMckTC72g/ 5WyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776966060; x=1777570860; 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=qoHwCYhkOoOoCVpFvAAtjl8akaFWmoh5EGgeHJdhgck=; b=WlPqm3Rx5//FKyV5+gKcNj91uh+/H9PmmGkzFyH2U8pmyhaual1WAn7od2TCpetV56 kTJHB539xrf9JkR6Ojbs9PMKhoE9DqbNxL6E8Rem+jPSQrzZ3r0m7Tu7yihAG666pJlc Jzsl8sevr/3CtvP0MiDMHPaoUqzTYTkn0hE60moevlYxpyjzSQnwAEfruCQrJwb/ERZh 1O5nP1Bru2IOyhQUK5HZO9CcPBs/jemUd/sSeeCK3rbOICLUtYKQfiV5bRxzEC3JTiJq W2cKkb/wNFd+qNZtwW/K1vkMAqelRW1XBLeamPQeA9Z8yP8dfDIoHaELdmzAtc6l0aJL xrmQ== X-Forwarded-Encrypted: i=1; AFNElJ+fm4Z9t2E1l0uj7LZOTacR0JAqKBQh2HeK2ZBHn1VnzAp27IEZqihf+ytnvjyhKyk76N1RlP2LuEeP9Lk=@vger.kernel.org X-Gm-Message-State: AOJu0YzllsN7vzpja8AFc8pTmESiT7/Tphw87IYRb0wdM3gfJXZ3cAMl egM0R4Q6ApOcG/s3FBhOzjwhCCJjRCOVGmu0x+NzJHqWl+1IWKN3gTA6 X-Gm-Gg: AeBDietkoHEm0rCqjk0A90oGIOwrFaM6r9YyigfJi4ElPc9N8wnsI19YFEewGPkFG4d OSgr5QoFTaR420iRHOUUqCjr/ULCD4C+0lO5a3/JreH6KYxZlQ3tIFEQNpz1JR1QxZvwUD90g5c DHf03z4OtxaTYv6mDGn6lYtTBeDYU7gKtOse3dn+zP9zthGjwTcyEGK5oemdfkSU0AGi0ZaRNSU 2el9w+1dFLivLSq7wBTVL2lxPuGcca0sRGQnIi+XqBkUHfquDMY7ZmD/kNoIL5Kq3xQY2lK/dtX Kjj9c+YoCL4wkg7rw287OQLrEiXPxchBw8DXiweTLMzARCP1LfDN0KHwj7TknQCf+T5k9Ne7g8x CL21s0Kln+GZR5tt7DEjJ4DTnXKhd4OlzDl78uRGj9wr7arE7Jxp2mJJj0vgNe2qzmjoAhEUftQ synyPBiJ+fZMNWArNV0XH+jpswwDrJ4F6r0phxx/xd8A5n1yzpUDB24vzlzvnW5WWaB+51OQeRg Kz6MkAl1ZVZIJ9cc95sJZUzeEex7yuYj9Ndbg== X-Received: by 2002:a05:6a00:4b56:b0:82f:5154:d1d9 with SMTP id d2e1a72fcca58-82f8c82e20dmr26770681b3a.6.1776966059513; Thu, 23 Apr 2026 10:40:59 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82f8ebcc9easm27014498b3a.39.2026.04.23.10.40.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Apr 2026 10:40:59 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v4 3/4] virtio: add noirq system sleep PM infrastructure Date: Fri, 24 Apr 2026 02:40:38 +0900 Message-Id: <20260423174039.276-4-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260423174039.276-1-baver.bae@gmail.com> References: <20260423174039.276-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_noirq() 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_restore_noirq() performs a second check as a safety net in case freeze_noirq was not called. 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 | 232 +++++++++++++++++++++++++++++++++- include/linux/virtio.h | 10 ++ include/linux/virtio_config.h | 39 ++++++ 3 files changed, 275 insertions(+), 6 deletions(-) diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 98f1875f8df1..5a46685f5ef9 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,38 @@ 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; + + if (virtio_check_mem_acc_cb(dev)) { + if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) { + dev_warn(&dev->dev, + "device must provide VIRTIO_F_VERSION_1\n"); + return -ENODEV; + } + + if (!virtio_has_feature(dev, VIRTIO_F_ACCESS_PLATFORM)) { + dev_warn(&dev->dev, + "device must provide VIRTIO_F_ACCESS_PLATFORM\n"); + return -ENODEV; + } + } + + 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 +325,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 +619,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_restore_done =3D false; =20 INIT_LIST_HEAD(&dev->vqs); spin_lock_init(&dev->vqs_list_lock); @@ -618,6 +699,47 @@ 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 int virtio_device_freeze(struct virtio_device *dev) { @@ -627,6 +749,7 @@ 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_restore_done =3D false; =20 if (drv && drv->freeze) { ret =3D drv->freeze(dev); @@ -645,12 +768,17 @@ 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) - goto err; - - if (!drv) - return 0; + /* + * If this device was already brought up in the noirq phase, + * skip the re-initialization here. + */ + if (!drv || !dev->noirq_restore_done) { + 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 +799,98 @@ 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; + + /* + * restore_noirq requires that the transport's config ops + * (reset, get_status, set_status) are safe to call during the noirq + * PM phase. Catch the mismatch early at freeze time so the PM core + * can abort cleanly rather than deadlocking on resume. + */ + if (drv->restore_noirq && !dev->config->noirq_safe) { + dev_warn(&dev->dev, + "transport does not support noirq PM\n"); + return -EOPNOTSUPP; + } + + /* + * If the driver provides restore_noirq 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->restore_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; + } + + if (drv->freeze_noirq) + return drv->freeze_noirq(dev); + + 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. */ + dev->noirq_restore_done =3D true; + + /* 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..ab66a3799310 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -151,6 +151,7 @@ struct virtio_admin_cmd { * @config_driver_disabled: configuration change reporting disabled by * a driver * @config_change_pending: configuration change reported while disabled + * @noirq_restore_done: set if the noirq restore phase completed successfu= lly * @config_lock: protects configuration change reporting * @vqs_list_lock: protects @vqs. * @dev: underlying device. @@ -171,6 +172,7 @@ struct virtio_device { bool config_core_enabled; bool config_driver_disabled; bool config_change_pending; + bool noirq_restore_done; spinlock_t config_lock; spinlock_t vqs_list_lock; struct device dev; @@ -209,8 +211,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 +243,8 @@ 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. + * @freeze_noirq: optional function to call during noirq suspend/hibernati= on. + * @restore_noirq: optional function to call on noirq resume. * @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 +266,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..81af2ad6a7c3 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.43.0 From nobody Wed Jun 17 07:20:37 2026 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) (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 60CF43AC0F6 for ; Thu, 23 Apr 2026 17:41:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966065; cv=none; b=UPoMrRMNzJIoGz9iamDEm4S/LwmZ6eDPGNiNOh5O71zSofwVX0Vafj2zeVH1RFZfOxzg/zBJRQ94geFjgj23mKEMoR9bdAHsOJKl2T2t3u2kjQmHhqnuOiIhNHyeKYNUoNYf0JNJM2cYjwvoTndC6sIaSrIh2FB87ogfa55BX0s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776966065; c=relaxed/simple; bh=wLKpK3tMbIqIN8Ol6qX8hkN7d3LwE17kRaqy9JLPUw8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EPq1BnDOCs0ERDE8DBKn5nd0EVLdYDHwqtVUUOBxJhIyGm/W2GnZYMgTIUDcFj7wzrdhmOXXgfGisiWPVijz6DI29FCdqJltowPMqsN2jWjZpequ5IgkAZwTajEoMIsgf7cEkiHR/Wz3up6da9rmtuICfO9rlhMkd2tdFPVivUs= 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=UcVZdahm; arc=none smtp.client-ip=209.85.210.169 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="UcVZdahm" Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-8318293f02bso65007b3a.0 for ; Thu, 23 Apr 2026 10:41:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776966062; x=1777570862; 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=jhejpkHi44NTY/EhjfX/VPO4q3I84QLwQ+oZB4nOOiw=; b=UcVZdahmQfy2Nz1lRo79/TkgZ+i0UrVHh3zOOLlfYLB3I++l0jdepbivhUNBYcjZLw nBC9+3tatGPwbGDUxWqQrs7cRbhrGf6XBbUhuXHMRtem0kY7Q6Wq5cCDRM4MjZThaibV oZ0KgsSYqTLBe0/MEeqz/laR/MviBl96RqgtmImWO6rgNaYz/b34yNpRu4QZtJTZBZgB 1uUDBi8eyV8IrgIgeRDg57MgawFwk9GEurnEJvKhoZSBaogygUs4RPTD5Vo2uOgv02ad KAzcH6sY64G8WitrN6mz0LynyS5fB6Sj3rZ8piHu1q+WpIUHgL5pRvcvyxnRqsN1+47M ztZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776966062; x=1777570862; 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=jhejpkHi44NTY/EhjfX/VPO4q3I84QLwQ+oZB4nOOiw=; b=WNJSyDTHy6lmEDS3PMbyZOjIODM/7LJ4WT8Wbkl4xTZkKCA/XjlZNVM5ZPPDadmHlj QsacATdo+wv7Y9MM5517YI94on4hWUYrFi5Bnvj8hHDXhonlUKmt8pNItPGzVJWg0ayg zIFHqijz0qqcqrFDkrv9SodiXm+jUyfhnoZtK4XGcr+qE2dDnyu12FFJeENs/hhcj8qx N+36xI9P+KFAG12+VP+NonW+IGUyPBvjfwvY2zHYwRzDjmnrYsMZLT96hjkSMqSkMLSw f0Hh4v9e3ZOzvQWin94zFFHYkJH9Pm6VNft2lsU7OB3VJlbEfdNIDN2kayxJVe9wkHI1 CJkg== X-Forwarded-Encrypted: i=1; AFNElJ+6I2CzWGJJoXgUsV0aLiXKs6B0bPcE91/4mj9j6v0SMoUERblHYZwv1ph/pjmGbsOr+98x+hoqYBXd5iw=@vger.kernel.org X-Gm-Message-State: AOJu0Yy728MtQ9LhoJpT5hTGYPWbgWaffSYVnor+tffDdKNjPGyuWVsT haLqGg0gkGLgosdRHtN3RhTgAxVGagpJASPzPyhERgJtV4FdMIDQ5Y4F X-Gm-Gg: AeBDieurUM3ZWbRXh5l4X+/rGR+0qcQ2PhWol4IODLkRRYdr7OFBINXgflVQuwW/51A bF1txtJggCfwNcbR5Q8AbCBptlxGbYnzDuoNjrLMKFm7lk2HxGl1KBJLS9VSolI4S2LpeRlaAOg oizJQFGnhZZ2SpoipaBzVpy7pvVQOgELt6DE/LQck9XSxP+IUKqL831m6LEso8mVIIELMv5um02 rTviUd+4bafZ5FtoRucp38YKrhh3f/FVoVv/+momzsW9+ZRTjoPwhtSsJknGQBlF8Whpgc+cIjj eI0ItbD85qgb5/wytP6l7NTtVTwMsUy/vRfnXTy8UBwhcLzyMFjvjEBMiD6YW31sZFZfzTcccWL njwJVcqNuKVrEUXqsaxVwQ/79tI+BHR6XV6ELu8PhYj26USYxv7sJzsldE7JeRr1gG8DI/N9OEM kAGKY02TXY5j2hPlRnXCgCwnfoDy247Xj5bYGaGUxyE/xSDcsxcAqn8DMrGI+ZGQbKrKP7tHsWf CN4j5L+7DYSajdPKpEsWtBZ1vaHwo5BbK/OIA== X-Received: by 2002:a05:6a00:2988:b0:82f:66e8:4257 with SMTP id d2e1a72fcca58-82f8b565509mr24255180b3a.32.1776966061864; Thu, 23 Apr 2026 10:41:01 -0700 (PDT) Received: from baver-zenith.localdomain ([124.49.88.131]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82f8ebcc9easm27014498b3a.39.2026.04.23.10.40.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Apr 2026 10:41:01 -0700 (PDT) From: Sungho Bae To: mst@redhat.com, jasowang@redhat.com Cc: xuanzhuo@linux.alibaba.com, eperezma@redhat.com, virtualization@lists.linux.dev, linux-kernel@vger.kernel.org, Sungho Bae Subject: [RFC PATCH v4 4/4] virtio-mmio: wire up noirq system sleep PM callbacks Date: Fri, 24 Apr 2026 02:40:39 +0900 Message-Id: <20260423174039.276-5-baver.bae@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260423174039.276-1-baver.bae@gmail.com> References: <20260423174039.276-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. For legacy (v1) devices, keep GUEST_PAGE_SIZE programming aligned with the noirq restore path while avoiding duplicate programming in normal restore. 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 | 134 ++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 40 deletions(-) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 595c2274fbb5..1cd262f9f8b6 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -336,6 +336,75 @@ 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 */ + virtqueue_reinit_vring(vq); + + /* 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); @@ -388,45 +457,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 @@ -528,11 +561,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 @@ -547,14 +582,33 @@ 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) + if (vm_dev->version =3D=3D 1 && !vm_dev->vdev.noirq_restore_done) writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); =20 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); + + if (vm_dev->version =3D=3D 1) + writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); + + 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.43.0