[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] xen/gntdev: remove struct gntdev_copy_batch from stack


  • To: Stefano Stabellini <sstabellini@xxxxxxxxxx>
  • From: Jürgen Groß <jgross@xxxxxxxx>
  • Date: Wed, 9 Jul 2025 07:23:07 +0200
  • Autocrypt: addr=jgross@xxxxxxxx; keydata= xsBNBFOMcBYBCACgGjqjoGvbEouQZw/ToiBg9W98AlM2QHV+iNHsEs7kxWhKMjrioyspZKOB ycWxw3ie3j9uvg9EOB3aN4xiTv4qbnGiTr3oJhkB1gsb6ToJQZ8uxGq2kaV2KL9650I1SJve dYm8Of8Zd621lSmoKOwlNClALZNew72NjJLEzTalU1OdT7/i1TXkH09XSSI8mEQ/ouNcMvIJ NwQpd369y9bfIhWUiVXEK7MlRgUG6MvIj6Y3Am/BBLUVbDa4+gmzDC9ezlZkTZG2t14zWPvx XP3FAp2pkW0xqG7/377qptDmrk42GlSKN4z76ELnLxussxc7I2hx18NUcbP8+uty4bMxABEB AAHNH0p1ZXJnZW4gR3Jvc3MgPGpncm9zc0BzdXNlLmNvbT7CwHkEEwECACMFAlOMcK8CGwMH CwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRCw3p3WKL8TL8eZB/9G0juS/kDY9LhEXseh mE9U+iA1VsLhgDqVbsOtZ/S14LRFHczNd/Lqkn7souCSoyWsBs3/wO+OjPvxf7m+Ef+sMtr0 G5lCWEWa9wa0IXx5HRPW/ScL+e4AVUbL7rurYMfwCzco+7TfjhMEOkC+va5gzi1KrErgNRHH kg3PhlnRY0Udyqx++UYkAsN4TQuEhNN32MvN0Np3WlBJOgKcuXpIElmMM5f1BBzJSKBkW0Jc Wy3h2Wy912vHKpPV/Xv7ZwVJ27v7KcuZcErtptDevAljxJtE7aJG6WiBzm+v9EswyWxwMCIO RoVBYuiocc51872tRGywc03xaQydB+9R7BHPzsBNBFOMcBYBCADLMfoA44MwGOB9YT1V4KCy vAfd7E0BTfaAurbG+Olacciz3yd09QOmejFZC6AnoykydyvTFLAWYcSCdISMr88COmmCbJzn sHAogjexXiif6ANUUlHpjxlHCCcELmZUzomNDnEOTxZFeWMTFF9Rf2k2F0Tl4E5kmsNGgtSa aMO0rNZoOEiD/7UfPP3dfh8JCQ1VtUUsQtT1sxos8Eb/HmriJhnaTZ7Hp3jtgTVkV0ybpgFg w6WMaRkrBh17mV0z2ajjmabB7SJxcouSkR0hcpNl4oM74d2/VqoW4BxxxOD1FcNCObCELfIS auZx+XT6s+CE7Qi/c44ibBMR7hyjdzWbABEBAAHCwF8EGAECAAkFAlOMcBYCGwwACgkQsN6d 1ii/Ey9D+Af/WFr3q+bg/8v5tCknCtn92d5lyYTBNt7xgWzDZX8G6/pngzKyWfedArllp0Pn fgIXtMNV+3t8Li1Tg843EXkP7+2+CQ98MB8XvvPLYAfW8nNDV85TyVgWlldNcgdv7nn1Sq8g HwB2BHdIAkYce3hEoDQXt/mKlgEGsLpzJcnLKimtPXQQy9TxUaLBe9PInPd+Ohix0XOlY+Uk QFEx50Ki3rSDl2Zt2tnkNYKUCvTJq7jvOlaPd6d/W0tZqpyy7KVay+K4aMobDsodB3dvEAs6 ScCnh03dDAFgIq5nsB11j3KPKdVoPlfucX2c7kGNH+LUMbzqV6beIENfNexkOfxHfw==
  • Cc: linux-kernel@xxxxxxxxxxxxxxx, llvm@xxxxxxxxxxxxxxx, Oleksandr Tyshchenko <oleksandr_tyshchenko@xxxxxxxx>, Nathan Chancellor <nathan@xxxxxxxxxx>, Nick Desaulniers <nick.desaulniers+lkml@xxxxxxxxx>, Bill Wendling <morbo@xxxxxxxxxx>, Justin Stitt <justinstitt@xxxxxxxxxx>, xen-devel@xxxxxxxxxxxxxxxxxxxx, Abinash Singh <abinashsinghlalotra@xxxxxxxxx>
  • Delivery-date: Wed, 09 Jul 2025 05:23:30 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

On 08.07.25 21:01, Stefano Stabellini wrote:
On Thu, 3 Jul 2025, Juergen Gross wrote:
When compiling the kernel with LLVM, the following warning was issued:

   drivers/xen/gntdev.c:991: warning: stack frame size (1160) exceeds
   limit (1024) in function 'gntdev_ioctl'

The main reason is struct gntdev_copy_batch which is located on the
stack and has a size of nearly 1kb.

For performance reasons it shouldn't by just dynamically allocated
instead, so allocate a new instance when needed and instead of freeing
it put it into a list of free structs anchored in struct gntdev_priv.

Fixes: a4cdb556cae0 ("xen/gntdev: add ioctl for grant copy")
Reported-by: Abinash Singh <abinashsinghlalotra@xxxxxxxxx>
Signed-off-by: Juergen Gross <jgross@xxxxxxxx>
---
  drivers/xen/gntdev-common.h |  4 +++
  drivers/xen/gntdev.c        | 71 ++++++++++++++++++++++++++-----------
  2 files changed, 54 insertions(+), 21 deletions(-)

diff --git a/drivers/xen/gntdev-common.h b/drivers/xen/gntdev-common.h
index 9c286b2a1900..ac8ce3179ba2 100644
--- a/drivers/xen/gntdev-common.h
+++ b/drivers/xen/gntdev-common.h
@@ -26,6 +26,10 @@ struct gntdev_priv {
        /* lock protects maps and freeable_maps. */
        struct mutex lock;
+ /* Free instances of struct gntdev_copy_batch. */
+       struct gntdev_copy_batch *batch;
+       struct mutex batch_lock;
+
  #ifdef CONFIG_XEN_GRANT_DMA_ALLOC
        /* Device for which DMA memory is allocated. */
        struct device *dma_dev;
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 61faea1f0663..1f2160765618 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -56,6 +56,18 @@ MODULE_AUTHOR("Derek G. Murray <Derek.Murray@xxxxxxxxxxxx>, "
              "Gerd Hoffmann <kraxel@xxxxxxxxxx>");
  MODULE_DESCRIPTION("User-space granted page access driver");
+#define GNTDEV_COPY_BATCH 16
+
+struct gntdev_copy_batch {
+       struct gnttab_copy ops[GNTDEV_COPY_BATCH];
+       struct page *pages[GNTDEV_COPY_BATCH];
+       s16 __user *status[GNTDEV_COPY_BATCH];
+       unsigned int nr_ops;
+       unsigned int nr_pages;
+       bool writeable;
+       struct gntdev_copy_batch *next;
+};
+
  static unsigned int limit = 64*1024;
  module_param(limit, uint, 0644);
  MODULE_PARM_DESC(limit,
@@ -584,6 +596,8 @@ static int gntdev_open(struct inode *inode, struct file 
*flip)
        INIT_LIST_HEAD(&priv->maps);
        mutex_init(&priv->lock);
+ mutex_init(&priv->batch_lock);
+
  #ifdef CONFIG_XEN_GNTDEV_DMABUF
        priv->dmabuf_priv = gntdev_dmabuf_init(flip);
        if (IS_ERR(priv->dmabuf_priv)) {
@@ -608,6 +622,7 @@ static int gntdev_release(struct inode *inode, struct file 
*flip)
  {
        struct gntdev_priv *priv = flip->private_data;
        struct gntdev_grant_map *map;
+       struct gntdev_copy_batch *batch;
pr_debug("priv %p\n", priv); @@ -620,6 +635,14 @@ static int gntdev_release(struct inode *inode, struct file *flip)
        }
        mutex_unlock(&priv->lock);
+ mutex_lock(&priv->batch_lock);
+       while (priv->batch) {
+               batch = priv->batch;
+               priv->batch = batch->next;
+               kfree(batch);
+       }
+       mutex_unlock(&priv->batch_lock);
+
  #ifdef CONFIG_XEN_GNTDEV_DMABUF
        gntdev_dmabuf_fini(priv->dmabuf_priv);
  #endif
@@ -785,17 +808,6 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, 
void __user *u)
        return rc;
  }
-#define GNTDEV_COPY_BATCH 16
-
-struct gntdev_copy_batch {
-       struct gnttab_copy ops[GNTDEV_COPY_BATCH];
-       struct page *pages[GNTDEV_COPY_BATCH];
-       s16 __user *status[GNTDEV_COPY_BATCH];
-       unsigned int nr_ops;
-       unsigned int nr_pages;
-       bool writeable;
-};
-
  static int gntdev_get_page(struct gntdev_copy_batch *batch, void __user *virt,
                                unsigned long *gfn)
  {
@@ -953,36 +965,53 @@ static int gntdev_grant_copy_seg(struct gntdev_copy_batch 
*batch,
  static long gntdev_ioctl_grant_copy(struct gntdev_priv *priv, void __user *u)
  {
        struct ioctl_gntdev_grant_copy copy;
-       struct gntdev_copy_batch batch;
+       struct gntdev_copy_batch *batch;
        unsigned int i;
        int ret = 0;
if (copy_from_user(&copy, u, sizeof(copy)))
                return -EFAULT;
- batch.nr_ops = 0;
-       batch.nr_pages = 0;
+       mutex_lock(&priv->batch_lock);
+       if (!priv->batch) {
+               batch = kmalloc(sizeof(*batch), GFP_KERNEL);
+       } else {
+               batch = priv->batch;
+               priv->batch = batch->next;
+       }
+       mutex_unlock(&priv->batch_lock);

I am concerned about the potentially unbounded amount of memory that
could be allocated this way.

Unbounded? It can be at most the number of threads using the interface
concurrently.

The mutex is already a potentially very slow operation. Could we instead
allocate a single batch, and if it is currently in use, use the mutex to
wait until it becomes available?

As this interface is e.g. used by the qemu based qdisk backend, the chances
are very high that there are concurrent users. This would hurt multi-ring
qdisk quite badly!

It would be possible to replace the mutex with a spinlock and do the kmalloc()
outside the locked region.


I am also OK with the current approach but I thought I would ask.




+       if (!batch)
+               return -ENOMEM;
+
+       batch->nr_ops = 0;
+       batch->nr_pages = 0;
for (i = 0; i < copy.count; i++) {
                struct gntdev_grant_copy_segment seg;
if (copy_from_user(&seg, &copy.segments[i], sizeof(seg))) {
                        ret = -EFAULT;
+                       gntdev_put_pages(batch);
                        goto out;
                }
- ret = gntdev_grant_copy_seg(&batch, &seg, &copy.segments[i].status);
-               if (ret < 0)
+               ret = gntdev_grant_copy_seg(batch, &seg, 
&copy.segments[i].status);
+               if (ret < 0) {
+                       gntdev_put_pages(batch);
                        goto out;
+               }
cond_resched();
        }
-       if (batch.nr_ops)
-               ret = gntdev_copy(&batch);
-       return ret;
+       if (batch->nr_ops)
+               ret = gntdev_copy(batch);
+
+ out:
+       mutex_lock(&priv->batch_lock);
+       batch->next = priv->batch;
+       priv->batch = batch;
+       mutex_unlock(&priv->batch_lock);
- out:
-       gntdev_put_pages(&batch);

One change from before is that in case of no errors, gntdev_put_pages is
not called anymore. Do we want that? Specifically, we are missing the
call to unpin_user_pages_dirty_lock

I don't think you are right. There was a "return ret" before the "out:"
label before my patch.


Juergen

Attachment: OpenPGP_0xB0DE9DD628BF132F.asc
Description: OpenPGP public key

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.