]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/vfio_ccw: Move cp cleanup out of not operational
authorEric Farman <farman@linux.ibm.com>
Tue, 28 Jul 2026 03:30:20 +0000 (05:30 +0200)
committerChristian Borntraeger <borntraeger@linux.ibm.com>
Thu, 30 Jul 2026 17:50:39 +0000 (19:50 +0200)
The fsm_notoper() routine is called when the device has been
lost, and is (by definition) no longer operational. Since this
can happen asynchronously from the normal behavior of the
driver, the cleanup may happen when holding other locks
in the calling sequence (notably, the cio subchannel lock).

Push the cleanup of the private->cp resources to a workqueue,
where it can be done out from under that lock sequence and
a future patch can safely manage the locking requirements.

Fixes: 204b394a23ad ("vfio/ccw: Move FSM open/close to MDEV open/close")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Farman <farman@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
drivers/s390/cio/vfio_ccw_drv.c
drivers/s390/cio/vfio_ccw_fsm.c
drivers/s390/cio/vfio_ccw_ops.c
drivers/s390/cio/vfio_ccw_private.h

index 1a095085bc7281a12c49a1caa1b53683696d28e0..c197ad5ab5805674bc124323eaafbc3e5854a0a1 100644 (file)
@@ -125,6 +125,15 @@ void vfio_ccw_crw_todo(struct work_struct *work)
                eventfd_signal(private->crw_trigger);
 }
 
+void vfio_ccw_notoper_todo(struct work_struct *work)
+{
+       struct vfio_ccw_private *private;
+
+       private = container_of(work, struct vfio_ccw_private, notoper_work);
+
+       cp_free(&private->cp);
+}
+
 /*
  * Css driver callbacks
  */
index 4d7988ea47ef0f3e23304a5b1547c2bb29ce50ee..4d47a3c7b9a069d907fcaf1ee0284769cfe15bc6 100644 (file)
@@ -170,8 +170,7 @@ static void fsm_notoper(struct vfio_ccw_private *private,
        css_sched_sch_todo(sch, SCH_TODO_UNREG);
        private->state = VFIO_CCW_STATE_NOT_OPER;
 
-       /* This is usually handled during CLOSE event */
-       cp_free(&private->cp);
+       queue_work(vfio_ccw_work_q, &private->notoper_work);
 }
 
 /*
index d361d1fde3a0d319ab4529c9f2e807d3e41b58ae..1df6d649565b80a7390255b74ffce83d64d9f05f 100644 (file)
@@ -54,6 +54,7 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev)
        INIT_LIST_HEAD(&private->crw);
        INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
        INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
+       INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo);
 
        private->cp.guest_cp = kzalloc_objs(struct ccw1, CCWCHAIN_LEN_MAX);
        if (!private->cp.guest_cp)
@@ -134,9 +135,16 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
        /*
         * Ensure these work items are fully drained, so none can
         * fire after being released.
+        *
+        * notoper_work should have nothing to do here, because only
+        * open devices could have channel_program resources in use
+        * and those would be released during close. Nevertheless,
+        * call flush here as well to be certain anything that was
+        * allocated is freed.
         */
        cancel_work_sync(&private->io_work);
        cancel_work_sync(&private->crw_work);
+       flush_work(&private->notoper_work);
 
        list_for_each_entry_safe(crw, temp, &private->crw, next) {
                list_del(&crw->next);
@@ -213,9 +221,14 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev)
        /*
         * Ensure these work items are drained, in the event the
         * device is re-opened instead of released.
+        *
+        * notoper_work needs to be given a chance to run if it
+        * is queued, so any memory associated with the channel
+        * program can be returned.
         */
        cancel_work_sync(&private->io_work);
        cancel_work_sync(&private->crw_work);
+       flush_work(&private->notoper_work);
 
        vfio_ccw_unregister_dev_regions(private);
 }
index 0501d4bbcdbd63e77e49804c66b7cb67149e08ac..e2256402b089409fd4acf1d4a80966d9e4efe08c 100644 (file)
@@ -102,6 +102,7 @@ struct vfio_ccw_parent {
  * @req_trigger: eventfd ctx for signaling userspace to return device
  * @io_work: work for deferral process of I/O handling
  * @crw_work: work for deferral process of CRW handling
+ * @notoper_work: work for deferred processing in not-operational state
  */
 struct vfio_ccw_private {
        struct vfio_device vdev;
@@ -125,11 +126,13 @@ struct vfio_ccw_private {
        struct eventfd_ctx      *req_trigger;
        struct work_struct      io_work;
        struct work_struct      crw_work;
+       struct work_struct      notoper_work;
 } __aligned(8);
 
 int vfio_ccw_sch_quiesce(struct subchannel *sch);
 void vfio_ccw_sch_io_todo(struct work_struct *work);
 void vfio_ccw_crw_todo(struct work_struct *work);
+void vfio_ccw_notoper_todo(struct work_struct *work);
 
 extern struct mdev_driver vfio_ccw_mdev_driver;