]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mei: pull kvfree out of spinlock
authorAlexander Usyskin <alexander.usyskin@intel.com>
Sun, 19 Jul 2026 09:57:55 +0000 (12:57 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 31 Jul 2026 11:28:05 +0000 (13:28 +0200)
The read buffer allocation was changed from kmalloc() to kvmalloc().

This buffer is part of mei_cl_cb structure that can be queued in
rd_complete queue protected by spinlock.
Releasing the structure leads to errors like below when freeing buffer
that allocated non-contiguous:

BUG: sleeping function called from invalid context at mm/vmalloc.c:3448

Separate mei_cl_cb structure dequeue and release to
perform only dequeue under spinlock and push release out of spinlock.

Cc: stable <stable@kernel.org>
Fixes: 4adf613e01bf ("mei: use kvmalloc for read buffer")
Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16359
Reviewed-by: Menachem Adin <menachem.adin@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Link: https://patch.msgid.link/20260719-kvfree_out_of_spinlock-v1-1-e07d6333bea7@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/mei/client.c

index 643b0039cc72c43de7589d2ae93c9240437cce83..26d2b2742d5094677e87cec2ec6988e21fe47d2e 100644 (file)
@@ -425,18 +425,24 @@ static void mei_io_tx_list_free_cl(struct list_head *head,
 }
 
 /**
- * mei_io_list_free_fp - free cb from a list that matches file pointer
+ * mei_io_rd_list_free_fp - free cb from a rd_completed list that matches file pointer
  *
- * @head: io list
+ * @cl: host client
  * @fp: file pointer (matching cb file object), may be NULL
  */
-static void mei_io_list_free_fp(struct list_head *head, const struct file *fp)
+static void mei_io_rd_list_free_fp(struct mei_cl *cl, const struct file *fp)
 {
        struct mei_cl_cb *cb, *next;
+       LIST_HEAD(cmpl_list);
 
-       list_for_each_entry_safe(cb, next, head, list)
+       spin_lock(&cl->rd_completed_lock);
+       list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
                if (!fp || fp == cb->fp)
-                       mei_io_cb_free(cb);
+                       list_move(&cb->list, &cmpl_list);
+       spin_unlock(&cl->rd_completed_lock);
+
+       list_for_each_entry_safe(cb, next, &cmpl_list, list)
+               mei_io_cb_free(cb);
 }
 
 /**
@@ -565,9 +571,7 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
                mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
                mei_cl_free_pending(cl);
        }
-       spin_lock(&cl->rd_completed_lock);
-       mei_io_list_free_fp(&cl->rd_completed, fp);
-       spin_unlock(&cl->rd_completed_lock);
+       mei_io_rd_list_free_fp(cl, fp);
 
        return 0;
 }
@@ -1401,7 +1405,7 @@ void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
 }
 
 /**
- * mei_cl_del_rd_completed - free read completed callback with lock
+ * mei_cl_del_rd_completed - unlink read completed callback with lock and free it
  *
  * @cl: host client
  * @cb: callback block
@@ -1410,8 +1414,9 @@ void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
 void mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
 {
        spin_lock(&cl->rd_completed_lock);
-       mei_io_cb_free(cb);
+       list_del_init(&cb->list);
        spin_unlock(&cl->rd_completed_lock);
+       mei_io_cb_free(cb);
 }
 
 /**