From nobody Sun Apr 28 18:05:22 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1574211216; cv=none; d=zoho.com; s=zohoarc; b=GItHYymFJhSZOUYr0b5qFkOloXwjyjwyu+o6hr7k9ri/finqK1WRpgpD9cqJo+Cm6fNoVq8MGdwRF3Fp3VSTpZizF47gE5wu90RwTQMs2sBvh/cnwwENwLNYd6LrmvpVbGraRdlnUhAzcnKifaNROuadYfEcIBkW4GCMAwKeUrQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1574211216; h=Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:Sender:Subject:To; bh=1ypBYdKEWZ9+Eboj2YyYBeWQOiZZI2gxQafgDmCCzCA=; b=dIht2LmAMlIHdvSfiZo/3yOX75pN1FbMy1rCVfNKn4Hmf4S6oyCm2QYwJtjM8ES1/O3SzZC8aVcuHtTiPszsmA5/QmxFOI4fMAjRmFhdsidNfBUELF/dALNKUeFgTKguRbEk+kWHrIS9iV9JYJKVFeZ8MMnsm/hcQIoHNtpJt5g= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1574211216286199.49253036106415; Tue, 19 Nov 2019 16:53:36 -0800 (PST) Received: from localhost ([::1]:52702 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iXEFM-0000c9-NI for importer@patchew.org; Tue, 19 Nov 2019 19:53:28 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:45865) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iXEES-0000Cu-60 for qemu-devel@nongnu.org; Tue, 19 Nov 2019 19:52:34 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iXEEP-0000u0-U1 for qemu-devel@nongnu.org; Tue, 19 Nov 2019 19:52:31 -0500 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:9224 helo=mx0a-001b2d01.pphosted.com) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iXEEP-0000t2-Od for qemu-devel@nongnu.org; Tue, 19 Nov 2019 19:52:29 -0500 Received: from pps.filterd (m0098414.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id xAK0qJYl024080; Tue, 19 Nov 2019 19:52:22 -0500 Received: from pps.reinject (localhost [127.0.0.1]) by mx0b-001b2d01.pphosted.com with ESMTP id 2way22af24-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 19 Nov 2019 19:52:21 -0500 Received: from m0098414.ppops.net (m0098414.ppops.net [127.0.0.1]) by pps.reinject (8.16.0.36/8.16.0.36) with SMTP id xAK0qLWr025309; Tue, 19 Nov 2019 19:52:21 -0500 Received: from ppma02dal.us.ibm.com (a.bd.3ea9.ip4.static.sl-reverse.com [169.62.189.10]) by mx0b-001b2d01.pphosted.com with ESMTP id 2way22aeyv-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 19 Nov 2019 19:52:21 -0500 Received: from pps.filterd (ppma02dal.us.ibm.com [127.0.0.1]) by ppma02dal.us.ibm.com (8.16.0.27/8.16.0.27) with SMTP id xAK0oViR020157; Wed, 20 Nov 2019 00:52:15 GMT Received: from b03cxnp07029.gho.boulder.ibm.com (b03cxnp07029.gho.boulder.ibm.com [9.17.130.16]) by ppma02dal.us.ibm.com with ESMTP id 2wa8r6garb-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 20 Nov 2019 00:52:15 +0000 Received: from b03ledav001.gho.boulder.ibm.com (b03ledav001.gho.boulder.ibm.com [9.17.130.232]) by b03cxnp07029.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id xAK0qEpo53543286 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 20 Nov 2019 00:52:14 GMT Received: from b03ledav001.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 240466E052; Wed, 20 Nov 2019 00:52:14 +0000 (GMT) Received: from b03ledav001.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 026E86E04E; Wed, 20 Nov 2019 00:52:13 +0000 (GMT) Received: from localhost (unknown [9.53.179.218]) by b03ledav001.gho.boulder.ibm.com (Postfix) with ESMTP; Wed, 20 Nov 2019 00:52:13 +0000 (GMT) From: Michael Roth To: qemu-devel@nongnu.org Subject: [PATCH v2] virtio-pci: disable vring processing when bus-mastering is disabled Date: Tue, 19 Nov 2019 18:50:03 -0600 Message-Id: <20191120005003.27035-1-mdroth@linux.vnet.ibm.com> X-Mailer: git-send-email 2.17.1 X-TM-AS-GCONF: 00 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.95,18.0.572 definitions=2019-11-19_08:2019-11-15,2019-11-19 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxlogscore=999 malwarescore=0 bulkscore=0 suspectscore=13 spamscore=0 adultscore=0 clxscore=1015 mlxscore=0 priorityscore=1501 impostorscore=0 phishscore=0 lowpriorityscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-1910280000 definitions=main-1911200007 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.158.5 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Alexey Kardashevskiy , "Michael S. Tsirkin" , David Gibson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Currently the SLOF firmware for pseries guests will disable/re-enable a PCI device multiple times via IO/MEM/MASTER bits of PCI_COMMAND register after the initial probe/feature negotiation, as it tends to work with a single device at a time at various stages like probing and running block/network bootloaders without doing a full reset in-between. In QEMU, when PCI_COMMAND_MASTER is disabled we disable the corresponding IOMMU memory region, so DMA accesses (including to vring fields like idx/flags) will no longer undergo the necessary translation. Normally we wouldn't expect this to happen since it would be misbehavior on the driver side to continue driving DMA requests. However, in the case of pseries, with iommu_platform=3Don, we trigger the following sequence when tearing down the virtio-blk dataplane ioeventfd in response to the guest unsetting PCI_COMMAND_MASTER: #2 0x0000555555922651 in virtqueue_map_desc (vdev=3Dvdev@entry=3D0x55555= 6dbcfb0, p_num_sg=3Dp_num_sg@entry=3D0x7fffe657e1a8, addr=3Daddr@entry=3D0x= 7fffe657e240, iov=3Diov@entry=3D0x7fffe6580240, max_num_sg=3Dmax_num_sg@ent= ry=3D1024, is_write=3Dis_write@entry=3Dfalse, pa=3D0, sz=3D0) at /home/mdroth/w/qemu.git/hw/virtio/virtio.c:757 #3 0x0000555555922a89 in virtqueue_pop (vq=3Dvq@entry=3D0x555556dc8660, = sz=3Dsz@entry=3D184) at /home/mdroth/w/qemu.git/hw/virtio/virtio.c:950 #4 0x00005555558d3eca in virtio_blk_get_request (vq=3D0x555556dc8660, s= =3D0x555556dbcfb0) at /home/mdroth/w/qemu.git/hw/block/virtio-blk.c:255 #5 0x00005555558d3eca in virtio_blk_handle_vq (s=3D0x555556dbcfb0, vq=3D= 0x555556dc8660) at /home/mdroth/w/qemu.git/hw/block/virtio-blk.c:776 #6 0x000055555591dd66 in virtio_queue_notify_aio_vq (vq=3Dvq@entry=3D0x5= 55556dc8660) at /home/mdroth/w/qemu.git/hw/virtio/virtio.c:1550 #7 0x000055555591ecef in virtio_queue_notify_aio_vq (vq=3D0x555556dc8660) at /home/mdroth/w/qemu.git/hw/virtio/virtio.c:1546 #8 0x000055555591ecef in virtio_queue_host_notifier_aio_poll (opaque=3D0= x555556dc86c8) at /home/mdroth/w/qemu.git/hw/virtio/virtio.c:2527 #9 0x0000555555d02164 in run_poll_handlers_once (ctx=3Dctx@entry=3D0x555= 55688bfc0, timeout=3Dtimeout@entry=3D0x7fffe65844a8) at /home/mdroth/w/qemu.git/util/aio-posix.c:520 #10 0x0000555555d02d1b in try_poll_mode (timeout=3D0x7fffe65844a8, ctx=3D= 0x55555688bfc0) at /home/mdroth/w/qemu.git/util/aio-posix.c:607 #11 0x0000555555d02d1b in aio_poll (ctx=3Dctx@entry=3D0x55555688bfc0, blo= cking=3Dblocking@entry=3Dtrue) at /home/mdroth/w/qemu.git/util/aio-posix.c:639 #12 0x0000555555d0004d in aio_wait_bh_oneshot (ctx=3D0x55555688bfc0, cb= =3Dcb@entry=3D0x5555558d5130 , opaque=3Dopaq= ue@entry=3D0x555556de86f0) at /home/mdroth/w/qemu.git/util/aio-wait.c:71 #13 0x00005555558d59bf in virtio_blk_data_plane_stop (vdev=3D) at /home/mdroth/w/qemu.git/hw/block/dataplane/virtio-blk.c:288 #14 0x0000555555b906a1 in virtio_bus_stop_ioeventfd (bus=3Dbus@entry=3D0x= 555556dbcf38) at /home/mdroth/w/qemu.git/hw/virtio/virtio-bus.c:245 #15 0x0000555555b90dbb in virtio_bus_stop_ioeventfd (bus=3Dbus@entry=3D0x= 555556dbcf38) at /home/mdroth/w/qemu.git/hw/virtio/virtio-bus.c:237 #16 0x0000555555b92a8e in virtio_pci_stop_ioeventfd (proxy=3D0x555556db4e= 40) at /home/mdroth/w/qemu.git/hw/virtio/virtio-pci.c:292 #17 0x0000555555b92a8e in virtio_write_config (pci_dev=3D0x555556db4e40, = address=3D, val=3D1048832, len=3D) at /home/mdroth/w/qemu.git/hw/virtio/virtio-pci.c:613 I.e. the calling code is only scheduling a one-shot BH for virtio_blk_data_plane_stop_bh, but somehow we end up trying to process an additional virtqueue entry before we get there. This is likely due to the following check in virtio_queue_host_notifier_aio_poll: static bool virtio_queue_host_notifier_aio_poll(void *opaque) { EventNotifier *n =3D opaque; VirtQueue *vq =3D container_of(n, VirtQueue, host_notifier); bool progress; if (!vq->vring.desc || virtio_queue_empty(vq)) { return false; } progress =3D virtio_queue_notify_aio_vq(vq); namely the call to virtio_queue_empty(). In this case, since no new requests have actually been issued, shadow_avail_idx =3D=3D last_avail_idx, so we actually try to access the vring via vring_avail_idx() to get the latest non-shadowed idx: int virtio_queue_empty(VirtQueue *vq) { bool empty; ... if (vq->shadow_avail_idx !=3D vq->last_avail_idx) { return 0; } rcu_read_lock(); empty =3D vring_avail_idx(vq) =3D=3D vq->last_avail_idx; rcu_read_unlock(); return empty; but since the IOMMU region has been disabled we get a bogus value (0 usually), which causes virtio_queue_empty() to falsely report that there are entries to be processed, which causes errors such as: "virtio: zero sized buffers are not allowed" or "virtio-blk missing headers" and puts the device in an error state. This patch works around the issue by introducing virtio_set_disabled(), which sets a 'disabled' flag to bypass checks like virtio_queue_empty() when bus-mastering is disabled. Since we'd check this flag at all the same sites as vdev->broken, we replace those checks with an inline function which checks for either vdev->broken or vdev->disabled. The 'disabled' flag is only migrated when set, which should be fairly rare, but to maintain migration compatibility we disable it's use for older machine types. Users requiring the use of the flag in conjunction with older machine types can set it explicitly as a virtio-device option. NOTES: - This leaves some other oddities in play, like the fact that DRIVER_OK also gets unset in response to bus-mastering being disabled, but not restored (however the device seems to continue working) - Similarly, we disable the host notifier via virtio_bus_stop_ioeventfd(), which seems to move the handling out of virtio-blk dataplane and back into the main IO thread, and it ends up staying there till a reset (but otherwise continues working normally) Cc: David Gibson , Cc: Alexey Kardashevskiy Cc: "Michael S. Tsirkin" Signed-off-by: Michael Roth --- v2: - add migration support and only default to using 'disabled' flag for newer machines via virtio-device compat option (MST) - use inline functions to decouple checks from specific fields (MST) - rebased on master --- hw/core/machine.c | 1 + hw/virtio/virtio-pci.c | 12 ++++++++---- hw/virtio/virtio.c | 35 ++++++++++++++++++++++++++++------- include/hw/virtio/virtio.h | 15 +++++++++++++++ 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 1689ad3bf8..9f3073b23b 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -29,6 +29,7 @@ =20 GlobalProperty hw_compat_4_1[] =3D { { "virtio-pci", "x-pcie-flr-init", "off" }, + { "virtio-device", "use-disabled-flag", "false" }, }; const size_t hw_compat_4_1_len =3D G_N_ELEMENTS(hw_compat_4_1); =20 diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index c6b47a9c73..394d409fb9 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -608,10 +608,14 @@ static void virtio_write_config(PCIDevice *pci_dev, u= int32_t address, pcie_cap_flr_write_config(pci_dev, address, val, len); } =20 - if (range_covers_byte(address, len, PCI_COMMAND) && - !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - virtio_pci_stop_ioeventfd(proxy); - virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); + if (range_covers_byte(address, len, PCI_COMMAND)) { + if (!(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { + virtio_set_disabled(vdev, true); + virtio_pci_stop_ioeventfd(proxy); + virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER= _OK); + } else { + virtio_set_disabled(vdev, false); + } } =20 if (proxy->config_cap && diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 04716b5f6c..3cb603a466 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -546,7 +546,7 @@ static inline bool is_desc_avail(uint16_t flags, bool w= rap_counter) * Called within rcu_read_lock(). */ static int virtio_queue_empty_rcu(VirtQueue *vq) { - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return 1; } =20 @@ -565,7 +565,7 @@ static int virtio_queue_split_empty(VirtQueue *vq) { bool empty; =20 - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return 1; } =20 @@ -783,7 +783,7 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueEleme= nt *elem, =20 virtqueue_unmap_sg(vq, elem, len); =20 - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return; } =20 @@ -839,7 +839,7 @@ static void virtqueue_packed_flush(VirtQueue *vq, unsig= ned int count) =20 void virtqueue_flush(VirtQueue *vq, unsigned int count) { - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { vq->inuse -=3D count; return; } @@ -1602,7 +1602,7 @@ err_undo_map: =20 void *virtqueue_pop(VirtQueue *vq, size_t sz) { - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return NULL; } =20 @@ -1698,7 +1698,7 @@ unsigned int virtqueue_drop_all(VirtQueue *vq) { struct VirtIODevice *vdev =3D vq->vdev; =20 - if (unlikely(vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return 0; } =20 @@ -1816,7 +1816,7 @@ static void virtio_notify_vector(VirtIODevice *vdev, = uint16_t vector) BusState *qbus =3D qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k =3D VIRTIO_BUS_GET_CLASS(qbus); =20 - if (unlikely(vdev->broken)) { + if (virtio_device_disabled(vdev)) { return; } =20 @@ -1920,6 +1920,7 @@ void virtio_reset(void *opaque) vdev->guest_features =3D 0; vdev->queue_sel =3D 0; vdev->status =3D 0; + vdev->disabled =3D false; atomic_set(&vdev->isr, 0); vdev->config_vector =3D VIRTIO_NO_VECTOR; virtio_notify_vector(vdev, vdev->config_vector); @@ -2553,6 +2554,13 @@ static bool virtio_started_needed(void *opaque) return vdev->started; } =20 +static bool virtio_disabled_needed(void *opaque) +{ + VirtIODevice *vdev =3D opaque; + + return vdev->disabled; +} + static const VMStateDescription vmstate_virtqueue =3D { .name =3D "virtqueue_state", .version_id =3D 1, @@ -2718,6 +2726,17 @@ static const VMStateDescription vmstate_virtio_start= ed =3D { } }; =20 +static const VMStateDescription vmstate_virtio_disabled =3D { + .name =3D "virtio/disabled", + .version_id =3D 1, + .minimum_version_id =3D 1, + .needed =3D &virtio_disabled_needed, + .fields =3D (VMStateField[]) { + VMSTATE_BOOL(disabled, VirtIODevice), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_virtio =3D { .name =3D "virtio", .version_id =3D 1, @@ -2735,6 +2754,7 @@ static const VMStateDescription vmstate_virtio =3D { &vmstate_virtio_extra_state, &vmstate_virtio_started, &vmstate_virtio_packed_virtqueues, + &vmstate_virtio_disabled, NULL } }; @@ -3569,6 +3589,7 @@ static void virtio_device_instance_finalize(Object *o= bj) static Property virtio_properties[] =3D { DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features), DEFINE_PROP_BOOL("use-started", VirtIODevice, use_started, true), + DEFINE_PROP_BOOL("use-disabled-flag", VirtIODevice, use_disabled_flag,= true), DEFINE_PROP_END_OF_LIST(), }; =20 diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index c32a815303..f23d2efbc0 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -100,6 +100,8 @@ struct VirtIODevice uint16_t device_id; bool vm_running; bool broken; /* device in invalid state, needs reset */ + bool use_disabled_flag; /* allow use of 'disable' flag when needed */ + bool disabled; /* device in temporarily disabled state */ bool use_started; bool started; bool start_on_kick; /* when virtio 1.0 feature has not been negotiated= */ @@ -378,4 +380,17 @@ static inline void virtio_set_started(VirtIODevice *vd= ev, bool started) vdev->started =3D started; } } + +static inline void virtio_set_disabled(VirtIODevice *vdev, bool disable) +{ + if (vdev->use_disabled_flag) { + vdev->disabled =3D disable; + } +} + +static inline bool virtio_device_disabled(VirtIODevice *vdev) +{ + return unlikely(vdev->disabled || vdev->broken); +} + #endif --=20 2.17.1