Re: [PATCH v3 3/5] PCI: Allow IOV resources to be resized in pci_resize_resource

2024-10-10 Thread Michał Winiarski
On Thu, Oct 10, 2024 at 02:17:11PM +0300, Ilpo Järvinen wrote:
> On Thu, 10 Oct 2024, Michał Winiarski wrote:
> 
> > Similar to regular resizable BAR, VF BAR can also be resized.
> > The structures are very similar, which means we can reuse most of the
> > implementation. See PCIe r4.0, sec 9.3.7.4.
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  drivers/pci/iov.c   | 20 
> >  drivers/pci/pci.c   |  9 -
> >  drivers/pci/pci.h   |  8 
> >  drivers/pci/setup-res.c | 33 -
> >  4 files changed, 64 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
> > index fd5c059b29c13..591a3eae1618a 100644
> > --- a/drivers/pci/iov.c
> > +++ b/drivers/pci/iov.c
> > @@ -154,6 +154,26 @@ resource_size_t pci_iov_resource_size(struct pci_dev 
> > *dev, int resno)
> > return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
> >  }
> >  
> > +void pci_iov_resource_set_size(struct pci_dev *dev, int resno, 
> > resource_size_t size)
> > +{
> > +   if (!pci_resource_is_iov(resno)) {
> > +   pci_warn(dev, "%s is not an IOV resource\n",
> > +pci_resource_name(dev, resno));
> > +   return;
> > +   }
> > +
> > +   dev->sriov->barsz[resno - PCI_IOV_RESOURCES] = size;
> > +}
> > +
> > +bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
> > +{
> > +   u16 cmd;
> > +
> > +   pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_CTRL, &cmd);
> > +
> > +   return cmd & PCI_SRIOV_CTRL_MSE;
> > +}
> > +
> >  static void pci_read_vf_config_common(struct pci_dev *virtfn)
> >  {
> > struct pci_dev *physfn = virtfn->physfn;
> > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> > index 7d85c04fbba2a..788ae61731213 100644
> > --- a/drivers/pci/pci.c
> > +++ b/drivers/pci/pci.c
> > @@ -3718,10 +3718,17 @@ void pci_acs_init(struct pci_dev *dev)
> >   */
> >  static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
> >  {
> > +   int cap = PCI_EXT_CAP_ID_REBAR;
> > unsigned int pos, nbars, i;
> > u32 ctrl;
> >  
> > -   pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
> > +#ifdef CONFIG_PCI_IOV
> > +   if (pci_resource_is_iov(bar)) {
> > +   cap = PCI_EXT_CAP_ID_VF_REBAR;
> > +   bar -= PCI_IOV_RESOURCES;
> > +   }
> > +#endif
> 
> Perhaps abstracting bar -= PCI_IOV_RESOURCES too into some static inline 
> function would be useful so you could drop the ifdefs. That calculation 
> seems to be done in few places besides this one.

I have a version of this series with helpers for conversion in both
ways:
pci_iov_resource_to_vf_bar (which is this one)
and pci_vf_bar_to_iov_resource (+= PCI_IOV_RESOURCES)

But decided to leave it out for now.
I can include it in v4 (or send it separately, depending on the
direction in which this revision goes).

> 
> > +   pos = pci_find_ext_capability(pdev, cap);
> > if (!pos)
> > return -ENOTSUPP;
> >  
> > diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> > index c55f2d7a4f37e..e15fd8fe0f81f 100644
> > --- a/drivers/pci/pci.h
> > +++ b/drivers/pci/pci.h
> > @@ -584,6 +584,8 @@ static inline bool pci_resource_is_iov(int resno)
> >  {
> > return resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END;
> >  }
> > +void pci_iov_resource_set_size(struct pci_dev *dev, int resno, 
> > resource_size_t size);
> > +bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev);
> >  extern const struct attribute_group sriov_pf_dev_attr_group;
> >  extern const struct attribute_group sriov_vf_dev_attr_group;
> >  #else
> > @@ -607,6 +609,12 @@ static inline bool pci_resource_is_iov(int resno)
> >  {
> > return false;
> >  }
> > +static inline void pci_iov_resource_set_size(struct pci_dev *dev, int 
> > resno,
> > +resource_size_t size) { }
> > +static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
> > +{
> > +   return false;
> > +}
> >  #endif /* CONFIG_PCI_IOV */
> >  
> >  #ifdef CONFIG_PCIE_PTM
> > diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
> > index e2cf79253ebda..95a13a5fa379c 100644
> > --- a/drivers/pci/setup-res.c
> > +++ b/drivers/pci/setup-res.c
> > @@ -425,13 +425,37 @@ void pci_release_resource(struct pci_dev *dev, i

[PATCH v3 5/5] drm/xe/pf: Extend the VF LMEM BAR

2024-10-10 Thread Michał Winiarski
Opt into extending the VF BAR.
LMEM is partitioned between multiple VFs, and we expect that the more
VFs we have, the less LMEM is assigned to each VF.
This means that we can achieve full LMEM BAR access without the need to
attempt full VF LMEM BAR resize via pci_resize_resource().

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/xe/regs/xe_bars.h | 1 +
 drivers/gpu/drm/xe/xe_sriov_pf.c  | 8 
 2 files changed, 9 insertions(+)

diff --git a/drivers/gpu/drm/xe/regs/xe_bars.h 
b/drivers/gpu/drm/xe/regs/xe_bars.h
index ce05b6ae832f1..880140d6ccdca 100644
--- a/drivers/gpu/drm/xe/regs/xe_bars.h
+++ b/drivers/gpu/drm/xe/regs/xe_bars.h
@@ -7,5 +7,6 @@
 
 #define GTTMMADR_BAR   0 /* MMIO + GTT */
 #define LMEM_BAR   2 /* VRAM */
+#define VF_LMEM_BAR9 /* VF VRAM */
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.c b/drivers/gpu/drm/xe/xe_sriov_pf.c
index 0f721ae17b266..a26719b87ac1e 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf.c
@@ -4,7 +4,9 @@
  */
 
 #include 
+#include 
 
+#include "regs/xe_bars.h"
 #include "xe_assert.h"
 #include "xe_device.h"
 #include "xe_module.h"
@@ -80,8 +82,14 @@ bool xe_sriov_pf_readiness(struct xe_device *xe)
  */
 int xe_sriov_pf_init_early(struct xe_device *xe)
 {
+   int err;
+
xe_assert(xe, IS_SRIOV_PF(xe));
 
+   err = pci_iov_resource_extend(to_pci_dev(xe->drm.dev), VF_LMEM_BAR, 
true);
+   if (err)
+   xe_sriov_info(xe, "Failed to extend VF LMEM BAR: %d", err);
+
return drmm_mutex_init(&xe->drm, &xe->sriov.pf.master_lock);
 }
 
-- 
2.47.0



[PATCH v3 4/5] PCI/IOV: Allow extending VF BAR within original resource boundary

2024-10-10 Thread Michał Winiarski
VF MMIO resource reservation, either created by system firmware and
inherited by Linux PCI subsystem or created by the subsystem itself,
contains enough space to fit the BAR of all SR-IOV Virtual Functions
that can potentially be created (total VFs supported by the device).
This can be leveraged when the device is exposing lower than optimal BAR
size as a default, allowing access to the entire resource when lower
number of VFs are created.
It is achieved by dynamically resizing the BAR to largest possible value
that allows to fit all newly created VFs within the original resource
boundary.

Signed-off-by: Michał Winiarski 
---
 drivers/pci/iov.c   | 92 -
 drivers/pci/pci.h   |  1 +
 include/linux/pci.h |  3 ++
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 591a3eae1618a..f9071c1cfe9ee 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -174,6 +174,86 @@ bool pci_iov_is_memory_decoding_enabled(struct pci_dev 
*dev)
return cmd & PCI_SRIOV_CTRL_MSE;
 }
 
+static void pci_iov_resource_do_extend(struct pci_dev *dev, int resno, u16 
num_vfs)
+{
+   resource_size_t size;
+   int ret, old, i;
+   u32 sizes;
+
+   pci_config_pm_runtime_get(dev);
+
+   if (pci_iov_is_memory_decoding_enabled(dev)) {
+   ret = -EBUSY;
+   goto err;
+   }
+
+   sizes = pci_rebar_get_possible_sizes(dev, resno);
+   if (!sizes) {
+   ret = -ENOTSUPP;
+   goto err;
+   }
+
+   old = pci_rebar_get_current_size(dev, resno);
+   if (old < 0) {
+   ret = old;
+   goto err;
+   }
+
+   while (sizes > 0) {
+   i = __fls(sizes);
+   size = pci_rebar_size_to_bytes(i);
+   if (size * num_vfs <= pci_resource_len(dev, resno)) {
+   if (i != old) {
+   ret = pci_rebar_set_size(dev, resno, size);
+   if (ret)
+   goto err;
+
+   pci_iov_resource_set_size(dev, resno, size);
+   pci_iov_update_resource(dev, resno);
+   }
+   break;
+   }
+   sizes &= ~BIT(i);
+   }
+
+   pci_config_pm_runtime_put(dev);
+
+   return;
+
+err:
+   pci_warn(dev, "Failed to extend %s: %d\n",
+pci_resource_name(dev, resno), ret);
+
+   pci_config_pm_runtime_put(dev);
+}
+
+static void pci_iov_resource_do_restore(struct pci_dev *dev, int resno)
+{
+   if (dev->sriov->rebar_extend[resno - PCI_IOV_RESOURCES])
+   pci_iov_resource_do_extend(dev, resno, dev->sriov->total_VFs);
+}
+
+int pci_iov_resource_extend(struct pci_dev *dev, int resno, bool enable)
+{
+   if (!pci_resource_is_iov(resno)) {
+   pci_warn(dev, "%s is not an IOV resource\n",
+pci_resource_name(dev, resno));
+
+   return -ENODEV;
+   }
+
+   if (!pci_rebar_get_possible_sizes(dev, resno))
+   return -ENOTSUPP;
+
+   if (!enable)
+   pci_iov_resource_do_restore(dev, resno);
+
+   dev->sriov->rebar_extend[resno - PCI_IOV_RESOURCES] = enable;
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(pci_iov_resource_extend);
+
 static void pci_read_vf_config_common(struct pci_dev *virtfn)
 {
struct pci_dev *physfn = virtfn->physfn;
@@ -438,7 +518,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
  const char *buf, size_t count)
 {
struct pci_dev *pdev = to_pci_dev(dev);
-   int ret = 0;
+   int i, ret = 0;
u16 num_vfs;
 
if (kstrtou16(buf, 0, &num_vfs) < 0)
@@ -480,6 +560,11 @@ static ssize_t sriov_numvfs_store(struct device *dev,
goto exit;
}
 
+   for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+   if (pdev->sriov->rebar_extend[i])
+   pci_iov_resource_do_extend(pdev, i + PCI_IOV_RESOURCES, 
num_vfs);
+   }
+
ret = pdev->driver->sriov_configure(pdev, num_vfs);
if (ret < 0)
goto exit;
@@ -874,8 +959,13 @@ static int sriov_init(struct pci_dev *dev, int pos)
 
 static void sriov_release(struct pci_dev *dev)
 {
+   int i;
+
BUG_ON(dev->sriov->num_VFs);
 
+   for (i = 0; i < PCI_SRIOV_NUM_BARS; i++)
+   pci_iov_resource_do_restore(dev, i + PCI_IOV_RESOURCES);
+
if (dev != dev->sriov->dev)
pci_dev_put(dev->sriov->dev);
 
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index e15fd8fe0f81f..57e79f75e4c8f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -403,6 +403,7 @@ struct pci_sriov {
u16 subsystem_vendor; /* VF subsystem ven

[PATCH v3 3/5] PCI: Allow IOV resources to be resized in pci_resize_resource

2024-10-10 Thread Michał Winiarski
Similar to regular resizable BAR, VF BAR can also be resized.
The structures are very similar, which means we can reuse most of the
implementation. See PCIe r4.0, sec 9.3.7.4.

Signed-off-by: Michał Winiarski 
---
 drivers/pci/iov.c   | 20 
 drivers/pci/pci.c   |  9 -
 drivers/pci/pci.h   |  8 
 drivers/pci/setup-res.c | 33 -
 4 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index fd5c059b29c13..591a3eae1618a 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -154,6 +154,26 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, 
int resno)
return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
 }
 
+void pci_iov_resource_set_size(struct pci_dev *dev, int resno, resource_size_t 
size)
+{
+   if (!pci_resource_is_iov(resno)) {
+   pci_warn(dev, "%s is not an IOV resource\n",
+pci_resource_name(dev, resno));
+   return;
+   }
+
+   dev->sriov->barsz[resno - PCI_IOV_RESOURCES] = size;
+}
+
+bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
+{
+   u16 cmd;
+
+   pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_CTRL, &cmd);
+
+   return cmd & PCI_SRIOV_CTRL_MSE;
+}
+
 static void pci_read_vf_config_common(struct pci_dev *virtfn)
 {
struct pci_dev *physfn = virtfn->physfn;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 7d85c04fbba2a..788ae61731213 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3718,10 +3718,17 @@ void pci_acs_init(struct pci_dev *dev)
  */
 static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
 {
+   int cap = PCI_EXT_CAP_ID_REBAR;
unsigned int pos, nbars, i;
u32 ctrl;
 
-   pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+#ifdef CONFIG_PCI_IOV
+   if (pci_resource_is_iov(bar)) {
+   cap = PCI_EXT_CAP_ID_VF_REBAR;
+   bar -= PCI_IOV_RESOURCES;
+   }
+#endif
+   pos = pci_find_ext_capability(pdev, cap);
if (!pos)
return -ENOTSUPP;
 
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index c55f2d7a4f37e..e15fd8fe0f81f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -584,6 +584,8 @@ static inline bool pci_resource_is_iov(int resno)
 {
return resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END;
 }
+void pci_iov_resource_set_size(struct pci_dev *dev, int resno, resource_size_t 
size);
+bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev);
 extern const struct attribute_group sriov_pf_dev_attr_group;
 extern const struct attribute_group sriov_vf_dev_attr_group;
 #else
@@ -607,6 +609,12 @@ static inline bool pci_resource_is_iov(int resno)
 {
return false;
 }
+static inline void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
+resource_size_t size) { }
+static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
+{
+   return false;
+}
 #endif /* CONFIG_PCI_IOV */
 
 #ifdef CONFIG_PCIE_PTM
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index e2cf79253ebda..95a13a5fa379c 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -425,13 +425,37 @@ void pci_release_resource(struct pci_dev *dev, int resno)
 }
 EXPORT_SYMBOL(pci_release_resource);
 
+static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev, int 
resno)
+{
+   u16 cmd;
+
+   if (pci_resource_is_iov(resno))
+   return pci_iov_is_memory_decoding_enabled(dev);
+
+   pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+   return cmd & PCI_COMMAND_MEMORY;
+}
+
+static void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int 
size)
+{
+   resource_size_t res_size = pci_rebar_size_to_bytes(size);
+   struct resource *res = dev->resource + resno;
+
+   if (!pci_resource_is_iov(resno)) {
+   res->end = res->start + res_size - 1;
+   } else {
+   res->end = res->start + res_size * pci_sriov_get_totalvfs(dev) 
- 1;
+   pci_iov_resource_set_size(dev, resno, res_size);
+   }
+}
+
 int pci_resize_resource(struct pci_dev *dev, int resno, int size)
 {
struct resource *res = dev->resource + resno;
struct pci_host_bridge *host;
int old, ret;
u32 sizes;
-   u16 cmd;
 
/* Check if we must preserve the firmware's resource assignment */
host = pci_find_host_bridge(dev->bus);
@@ -442,8 +466,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int 
size)
if (!(res->flags & IORESOURCE_UNSET))
return -EBUSY;
 
-   pci_read_config_word(dev, PCI_COMMAND, &cmd);
-   if (cmd & PCI_COMMAND_MEMORY)
+   if (pci_resize_is_memory_decoding_enabled(dev, resno))

[PATCH v3 2/5] PCI: Add a helper to identify IOV resources

2024-10-10 Thread Michał Winiarski
There are multiple places where special handling is required for IOV
resources.
Extract it to a helper and drop a few ifdefs.

Signed-off-by: Michał Winiarski 
---
 drivers/pci/pci.h   | 18 ++
 drivers/pci/setup-bus.c |  5 +
 drivers/pci/setup-res.c |  4 +---
 3 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 14d00ce45bfa9..c55f2d7a4f37e 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -580,6 +580,10 @@ void pci_iov_update_resource(struct pci_dev *dev, int 
resno);
 resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
 void pci_restore_iov_state(struct pci_dev *dev);
 int pci_iov_bus_range(struct pci_bus *bus);
+static inline bool pci_resource_is_iov(int resno)
+{
+   return resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END;
+}
 extern const struct attribute_group sriov_pf_dev_attr_group;
 extern const struct attribute_group sriov_vf_dev_attr_group;
 #else
@@ -589,12 +593,20 @@ static inline int pci_iov_init(struct pci_dev *dev)
 }
 static inline void pci_iov_release(struct pci_dev *dev) { }
 static inline void pci_iov_remove(struct pci_dev *dev) { }
+static inline void pci_iov_update_resource(struct pci_dev *dev, int resno) { }
+static inline resource_size_t pci_sriov_resource_alignment(struct pci_dev 
*dev, int resno)
+{
+   return 0;
+}
 static inline void pci_restore_iov_state(struct pci_dev *dev) { }
 static inline int pci_iov_bus_range(struct pci_bus *bus)
 {
return 0;
 }
-
+static inline bool pci_resource_is_iov(int resno)
+{
+   return false;
+}
 #endif /* CONFIG_PCI_IOV */
 
 #ifdef CONFIG_PCIE_PTM
@@ -616,12 +628,10 @@ unsigned long pci_cardbus_resource_alignment(struct 
resource *);
 static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
 struct resource *res)
 {
-#ifdef CONFIG_PCI_IOV
int resno = res - dev->resource;
 
-   if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
+   if (pci_resource_is_iov(resno))
return pci_sriov_resource_alignment(dev, resno);
-#endif
if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS)
return pci_cardbus_resource_alignment(res);
return resource_alignment(res);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 23082bc0ca37a..8909948bc9a9f 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -1093,17 +1093,14 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned 
long mask,
 (r->flags & mask) != type3))
continue;
r_size = resource_size(r);
-#ifdef CONFIG_PCI_IOV
/* Put SRIOV requested res to the optional list */
-   if (realloc_head && i >= PCI_IOV_RESOURCES &&
-   i <= PCI_IOV_RESOURCE_END) {
+   if (realloc_head && pci_resource_is_iov(i)) {
add_align = max(pci_resource_alignment(dev, r), 
add_align);
r->end = r->start - 1;
add_to_list(realloc_head, dev, r, r_size, 0 /* 
Don't care */);
children_add_size += r_size;
continue;
}
-#endif
/*
 * aligns[0] is for 1MB (since bridge memory
 * windows are always at least 1MB aligned), so
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index c6d933ddfd464..e2cf79253ebda 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -127,10 +127,8 @@ void pci_update_resource(struct pci_dev *dev, int resno)
 {
if (resno <= PCI_ROM_RESOURCE)
pci_std_update_resource(dev, resno);
-#ifdef CONFIG_PCI_IOV
-   else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
+   else if (pci_resource_is_iov(resno))
pci_iov_update_resource(dev, resno);
-#endif
 }
 
 int pci_claim_resource(struct pci_dev *dev, int resource)
-- 
2.47.0



[PATCH v3 1/5] PCI/IOV: Restore VF resizable BAR state after reset

2024-10-10 Thread Michał Winiarski
Similar to regular resizable BAR, VF BAR can also be resized, e.g. by
the system firmware, or the PCI subsystem itself.
Add the capability ID and restore it as a part of IOV state.
See PCIe r4.0, sec 9.3.7.4.

Signed-off-by: Michał Winiarski 
---
 drivers/pci/iov.c | 29 -
 include/uapi/linux/pci_regs.h |  1 +
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index aaa33e8dc4c97..fd5c059b29c13 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -7,6 +7,7 @@
  * Copyright (C) 2009 Intel Corporation, Yu Zhao 
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -862,6 +863,30 @@ static void sriov_release(struct pci_dev *dev)
dev->sriov = NULL;
 }
 
+static void sriov_restore_vf_rebar_state(struct pci_dev *dev)
+{
+   unsigned int pos, nbars, i;
+   u32 ctrl;
+
+   pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VF_REBAR);
+   if (!pos)
+   return;
+
+   pci_read_config_dword(dev, pos + PCI_REBAR_CTRL, &ctrl);
+   nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+
+   for (i = 0; i < nbars; i++, pos += 8) {
+   int bar_idx, size;
+
+   pci_read_config_dword(dev, pos + PCI_REBAR_CTRL, &ctrl);
+   bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+   size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]);
+   ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+   ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
+   pci_write_config_dword(dev, pos + PCI_REBAR_CTRL, ctrl);
+   }
+}
+
 static void sriov_restore_state(struct pci_dev *dev)
 {
int i;
@@ -1021,8 +1046,10 @@ resource_size_t pci_sriov_resource_alignment(struct 
pci_dev *dev, int resno)
  */
 void pci_restore_iov_state(struct pci_dev *dev)
 {
-   if (dev->is_physfn)
+   if (dev->is_physfn) {
+   sriov_restore_vf_rebar_state(dev);
sriov_restore_state(dev);
+   }
 }
 
 /**
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 12323b3334a9c..a0cf701c4c3af 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -740,6 +740,7 @@
 #define PCI_EXT_CAP_ID_L1SS0x1E/* L1 PM Substates */
 #define PCI_EXT_CAP_ID_PTM 0x1F/* Precision Time Measurement */
 #define PCI_EXT_CAP_ID_DVSEC   0x23/* Designated Vendor-Specific */
+#define PCI_EXT_CAP_ID_VF_REBAR 0x24   /* VF Resizable BAR */
 #define PCI_EXT_CAP_ID_DLF 0x25/* Data Link Feature */
 #define PCI_EXT_CAP_ID_PL_16GT 0x26/* Physical Layer 16.0 GT/s */
 #define PCI_EXT_CAP_ID_NPEM0x29/* Native PCIe Enclosure Management */
-- 
2.47.0



[PATCH v3 0/5] PCI: VF resizable BAR

2024-10-10 Thread Michał Winiarski
Hi,

Resurrecting an old series [1], now that we have an in-tree user for it.

For regular BAR, drivers can use pci_resize_resource to resize it to the
desired size provided that it is supported by the hardware, which the
driver can query using pci_rebar_get_possible_sizes.
This series expands the API to work with IOV BAR as well.
It also adds the additional API to allow extending the VF BAR within the
original resource boundary.

Thanks,
-Michał

[1] 
https://lore.kernel.org/all/20211215141626.3090807-1-michal.winiar...@intel.com/

v1 -> v2:
- Add pci_iov_resource_extend() and usage in Xe driver
- Reduce the number of ifdefs (Christian)
- Drop patch 2/2 from v1 (Christian)
- Add a helper to avoid upsetting static analysis tools (Krzysztof)

v2 -> v3:
- Extract introducing pci_resource_is_iov to separate commit and
  use it elsewhere in PCI subsystem (Christian)
- Extract restoring VF rebar state to separate commit (Christian)
- Reorganize memory decoding check (Christian)
- Don't use dev_WARN (Ilpo)
- Fix build without CONFIG_PCI_IOV (CI)

Michał Winiarski (5):
  PCI/IOV: Restore VF resizable BAR state after reset
  PCI: Add a helper to identify IOV resources
  PCI: Allow IOV resources to be resized in pci_resize_resource
  PCI/IOV: Allow extending VF BAR within original resource boundary
  drm/xe/pf: Extend the VF LMEM BAR

 drivers/gpu/drm/xe/regs/xe_bars.h |   1 +
 drivers/gpu/drm/xe/xe_sriov_pf.c  |   8 ++
 drivers/pci/iov.c | 141 +-
 drivers/pci/pci.c |   9 +-
 drivers/pci/pci.h |  27 +-
 drivers/pci/setup-bus.c   |   5 +-
 drivers/pci/setup-res.c   |  37 ++--
 include/linux/pci.h   |   3 +
 include/uapi/linux/pci_regs.h |   1 +
 9 files changed, 213 insertions(+), 19 deletions(-)

-- 
2.47.0



Re: [PATCH v2 2/3] PCI: Allow extending VF BAR within original resource boundary

2024-10-10 Thread Michał Winiarski
On Fri, Sep 20, 2024 at 12:07:34PM +0200, Christian König wrote:
> Am 20.09.24 um 00:35 schrieb Michał Winiarski:
> > VF MMIO resource reservation, either created by system firmware and
> > inherited by Linux PCI subsystem or created by the subsystem itself,
> > contains enough space to fit the BAR of all SR-IOV Virtual Functions
> > that can potentially be created (total VFs supported by the device).
> > This can be leveraged when the device is exposing lower than optimal BAR
> > size as a default, allowing access to the entire resource when lower
> > number of VFs are created.
> > It is achieved by dynamically resizing the BAR to largest possible value
> > that allows to fit all newly created VFs within the original resource
> > boundary.
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >   drivers/pci/iov.c   | 92 -
> >   drivers/pci/pci.h   |  1 +
> >   include/linux/pci.h |  3 ++
> >   3 files changed, 95 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
> > index e8ccd2ae0f024..d88efbfa70e42 100644
> > --- a/drivers/pci/iov.c
> > +++ b/drivers/pci/iov.c
> > @@ -181,6 +181,86 @@ bool pci_iov_memory_decoding_enabled(struct pci_dev 
> > *dev)
> > return cmd & PCI_SRIOV_CTRL_MSE;
> >   }
> > +static void pci_iov_resource_do_extend(struct pci_dev *dev, int resno, u16 
> > num_vfs)
> > +{
> > +   resource_size_t size;
> > +   int ret, old, i;
> > +   u32 sizes;
> > +
> > +   pci_config_pm_runtime_get(dev);
> > +
> > +   if (pci_iov_memory_decoding_enabled(dev)) {
> > +   ret = -EBUSY;
> > +   goto err;
> > +   }
> > +
> > +   sizes = pci_rebar_get_possible_sizes(dev, resno);
> > +   if (!sizes) {
> > +   ret = -ENOTSUPP;
> > +   goto err;
> > +   }
> > +
> > +   old = pci_rebar_get_current_size(dev, resno);
> > +   if (old < 0) {
> > +   ret = old;
> > +   goto err;
> > +   }
> > +
> > +   while (sizes > 0) {
> > +   i = __fls(sizes);
> > +   size = pci_rebar_size_to_bytes(i);
> > +   if (size * num_vfs <= pci_resource_len(dev, resno)) {
> > +   if (i != old) {
> > +   ret = pci_rebar_set_size(dev, resno, size);
> > +   if (ret)
> > +   goto err;
> > +
> > +   pci_iov_resource_set_size(dev, resno, size);
> > +   pci_iov_update_resource(dev, resno);
> > +   }
> > +   break;
> > +   }
> > +   sizes &= ~BIT(i);
> > +   }
> > +
> > +   pci_config_pm_runtime_put(dev);
> > +
> > +   return;
> > +
> > +err:
> > +   dev_WARN(&dev->dev, "Failed to extend %s: %d\n",
> > +pci_resource_name(dev, resno), ret);
> > +
> > +   pci_config_pm_runtime_put(dev);
> > +}
> > +
> > +static void pci_iov_resource_do_restore(struct pci_dev *dev, int resno)
> > +{
> > +   if (dev->sriov->rebar_extend[resno - PCI_IOV_RESOURCES])
> > +   pci_iov_resource_do_extend(dev, resno, dev->sriov->total_VFs);
> > +}
> > +
> > +int pci_iov_resource_extend(struct pci_dev *dev, int resno, bool enable)
> > +{
> > +   if (!pci_resource_is_iov(dev, resno)) {
> > +   dev_WARN(&dev->dev, "%s is not an IOV resource\n",
> > +pci_resource_name(dev, resno));
> > +
> > +   return -ENODEV;
> > +   }
> > +
> > +   if (!pci_rebar_get_possible_sizes(dev, resno))
> > +   return -ENOTSUPP;
> > +
> > +   if (!enable)
> > +   pci_iov_resource_do_restore(dev, resno);
> > +
> > +   dev->sriov->rebar_extend[resno - PCI_IOV_RESOURCES] = enable;
> > +
> > +   return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_iov_resource_extend);
> > +
> >   static void pci_read_vf_config_common(struct pci_dev *virtfn)
> >   {
> > struct pci_dev *physfn = virtfn->physfn;
> > @@ -445,7 +525,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
> >   const char *buf, size_t count)
> >   {
> > struct pci_dev *pdev = to_pci_dev(dev);
> > -   int ret = 0;
> > +   int i, ret = 0;
> > u16 num_vfs;
> > if (kstrtou16(buf, 0, &num_vfs) < 0)
> > @@ -487,6 

Re: [PATCH v2 1/3] PCI: Add support for VF Resizable Bar extended cap

2024-10-10 Thread Michał Winiarski
On Fri, Sep 20, 2024 at 11:57:34AM +0200, Christian König wrote:
> Am 20.09.24 um 00:35 schrieb Michał Winiarski:
> > Similar to regular resizable BAR, VF BAR can also be resized.
> > The structures are very similar, which means we can reuse most of the
> > implementation. See PCIe r4.0, sec 9.3.7.4.
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >   drivers/pci/iov.c | 28 ++
> >   drivers/pci/pci.c | 40 ++-
> >   drivers/pci/pci.h | 14 ++-
> >   drivers/pci/setup-res.c   | 44 ++-
> >   include/uapi/linux/pci_regs.h |  1 +
> >   5 files changed, 119 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
> > index aaa33e8dc4c97..e8ccd2ae0f024 100644
> > --- a/drivers/pci/iov.c
> > +++ b/drivers/pci/iov.c
> > @@ -153,6 +153,34 @@ resource_size_t pci_iov_resource_size(struct pci_dev 
> > *dev, int resno)
> > return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
> >   }
> > +bool pci_resource_is_iov(struct pci_dev *dev, int resno)
> > +{
> > +   if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
> > +   return true;
> > +
> > +   return false;
> > +}
> 
> When you want to generalize that check you should probably but it in a
> header and change the existing checks in pci.h and setup-res.c as well.
> Otherwise I don't really see the value in having a separate function.

I'll split it into separate patch that's changing the existing checks.

> 
> Additional to that please code that something like "return resno >=" the
> extra if just increases the number of lines without adding any value.

Ok.

> 
> > +
> > +void pci_iov_resource_set_size(struct pci_dev *dev, int resno, 
> > resource_size_t size)
> > +{
> > +   if (!pci_resource_is_iov(dev, resno)) {
> > +   dev_WARN(&dev->dev, "%s is not an IOV resource\n",
> > +pci_resource_name(dev, resno));
> > +   return;
> > +   }
> > +
> > +   dev->sriov->barsz[resno - PCI_IOV_RESOURCES] = size;
> > +}
> > +
> > +bool pci_iov_memory_decoding_enabled(struct pci_dev *dev)
> > +{
> > +   u16 cmd;
> > +
> > +   pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_CTRL, &cmd);
> > +
> > +   return cmd & PCI_SRIOV_CTRL_MSE;
> > +}
> > +
> >   static void pci_read_vf_config_common(struct pci_dev *virtfn)
> >   {
> > struct pci_dev *physfn = virtfn->physfn;
> > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> > index ffaaca0978cbc..d4522e365e7ba 100644
> > --- a/drivers/pci/pci.c
> > +++ b/drivers/pci/pci.c
> > @@ -1901,6 +1901,35 @@ static void pci_restore_rebar_state(struct pci_dev 
> > *pdev)
> > }
> >   }
> > +static void pci_restore_vf_rebar_state(struct pci_dev *pdev)
> > +{
> > +   unsigned int pos, nbars, i;
> > +   u32 ctrl;
> > +
> > +   if (!pdev->is_physfn)
> > +   return;
> > +
> > +   pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_VF_REBAR);
> > +   if (!pos)
> > +   return;
> > +
> > +   pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
> > +   nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
> > +
> > +   for (i = 0; i < nbars; i++, pos += 8) {
> > +   struct resource *res;
> > +   int bar_idx, size;
> > +
> > +   pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
> > +   bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
> > +   res = pdev->resource + bar_idx;
> 
> The variable res seems to be unused.
> 
> In general I think you should split up the patch into restoring the VF rebar
> state on resume and implementing the new resize API.

I'll split it into separate patch.

> 
> > +   size = pci_rebar_bytes_to_size(pdev->sriov->barsz[bar_idx]);
> > +   ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
> > +   ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
> > +   pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
> > +   }
> > +}
> > +
> >   /**
> >* pci_restore_state - Restore the saved state of a PCI device
> >* @dev: PCI device that we're dealing with
> > @@ -1916,6 +1945,7 @@ void pci_restore_state(struct pci_dev *dev)
> > pci_restore_ats_state(dev);
> > pci_restore_vc_state(

Re: [PATCH v2 2/3] PCI: Allow extending VF BAR within original resource boundary

2024-10-10 Thread Michał Winiarski
On Fri, Sep 20, 2024 at 02:30:00PM +0300, Ilpo Järvinen wrote:
> On Fri, 20 Sep 2024, Michał Winiarski wrote:
> 
> > VF MMIO resource reservation, either created by system firmware and
> > inherited by Linux PCI subsystem or created by the subsystem itself,
> > contains enough space to fit the BAR of all SR-IOV Virtual Functions
> > that can potentially be created (total VFs supported by the device).
> > This can be leveraged when the device is exposing lower than optimal BAR
> > size as a default, allowing access to the entire resource when lower
> > number of VFs are created.
> > It is achieved by dynamically resizing the BAR to largest possible value
> > that allows to fit all newly created VFs within the original resource
> > boundary.
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  drivers/pci/iov.c   | 92 -
> >  drivers/pci/pci.h   |  1 +
> >  include/linux/pci.h |  3 ++
> >  3 files changed, 95 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
> > index e8ccd2ae0f024..d88efbfa70e42 100644
> > --- a/drivers/pci/iov.c
> > +++ b/drivers/pci/iov.c
> > @@ -181,6 +181,86 @@ bool pci_iov_memory_decoding_enabled(struct pci_dev 
> > *dev)
> > return cmd & PCI_SRIOV_CTRL_MSE;
> >  }
> >  
> > +static void pci_iov_resource_do_extend(struct pci_dev *dev, int resno, u16 
> > num_vfs)
> > +{
> > +   resource_size_t size;
> > +   int ret, old, i;
> > +   u32 sizes;
> > +
> > +   pci_config_pm_runtime_get(dev);
> > +
> > +   if (pci_iov_memory_decoding_enabled(dev)) {
> > +   ret = -EBUSY;
> > +   goto err;
> > +   }
> > +
> > +   sizes = pci_rebar_get_possible_sizes(dev, resno);
> > +   if (!sizes) {
> > +   ret = -ENOTSUPP;
> > +   goto err;
> > +   }
> > +
> > +   old = pci_rebar_get_current_size(dev, resno);
> > +   if (old < 0) {
> > +   ret = old;
> > +   goto err;
> > +   }
> > +
> > +   while (sizes > 0) {
> > +   i = __fls(sizes);
> > +   size = pci_rebar_size_to_bytes(i);
> > +   if (size * num_vfs <= pci_resource_len(dev, resno)) {
> > +   if (i != old) {
> > +   ret = pci_rebar_set_size(dev, resno, size);
> > +   if (ret)
> > +   goto err;
> > +
> > +   pci_iov_resource_set_size(dev, resno, size);
> > +   pci_iov_update_resource(dev, resno);
> > +   }
> > +   break;
> > +   }
> > +   sizes &= ~BIT(i);
> > +   }
> > +
> > +   pci_config_pm_runtime_put(dev);
> > +
> > +   return;
> > +
> > +err:
> > +   dev_WARN(&dev->dev, "Failed to extend %s: %d\n",
> > +pci_resource_name(dev, resno), ret);
> 
> Why do you use dev_WARN()? (analoguous to WARN_ON() / friends).
> 
> I suppose you'd want to use pci_warn() instead.

This should never happen - but fair point, with something like
panic_on_warn it would have more negative consequences. Let's downgrade
it to pci_warn().

> 
> > +   pci_config_pm_runtime_put(dev);
> > +}
> > +
> > +static void pci_iov_resource_do_restore(struct pci_dev *dev, int resno)
> > +{
> > +   if (dev->sriov->rebar_extend[resno - PCI_IOV_RESOURCES])
> > +   pci_iov_resource_do_extend(dev, resno, dev->sriov->total_VFs);
> > +}
> > +
> > +int pci_iov_resource_extend(struct pci_dev *dev, int resno, bool enable)
> > +{
> > +   if (!pci_resource_is_iov(dev, resno)) {
> > +   dev_WARN(&dev->dev, "%s is not an IOV resource\n",
> > +pci_resource_name(dev, resno));
> 
> pci_warn() ?

And this one is programmer error. But same as above, let's use
pci_warn().

Thanks
-Michał


[PATCH v2 3/3] drm/xe/pf: Extend the VF LMEM BAR

2024-09-19 Thread Michał Winiarski
Opt into extending the VF BAR.
LMEM is partitioned between multiple VFs, and we expect that the more
VFs we have, the less LMEM is assigned to each VF.
This means that we can achieve full LMEM BAR access without the need to
attempt full VF LMEM BAR resize via pci_resize_resource().

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/xe/regs/xe_bars.h | 1 +
 drivers/gpu/drm/xe/xe_sriov_pf.c  | 8 
 2 files changed, 9 insertions(+)

diff --git a/drivers/gpu/drm/xe/regs/xe_bars.h 
b/drivers/gpu/drm/xe/regs/xe_bars.h
index ce05b6ae832f1..880140d6ccdca 100644
--- a/drivers/gpu/drm/xe/regs/xe_bars.h
+++ b/drivers/gpu/drm/xe/regs/xe_bars.h
@@ -7,5 +7,6 @@
 
 #define GTTMMADR_BAR   0 /* MMIO + GTT */
 #define LMEM_BAR   2 /* VRAM */
+#define VF_LMEM_BAR9 /* VF VRAM */
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.c b/drivers/gpu/drm/xe/xe_sriov_pf.c
index 0f721ae17b266..a26719b87ac1e 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf.c
@@ -4,7 +4,9 @@
  */
 
 #include 
+#include 
 
+#include "regs/xe_bars.h"
 #include "xe_assert.h"
 #include "xe_device.h"
 #include "xe_module.h"
@@ -80,8 +82,14 @@ bool xe_sriov_pf_readiness(struct xe_device *xe)
  */
 int xe_sriov_pf_init_early(struct xe_device *xe)
 {
+   int err;
+
xe_assert(xe, IS_SRIOV_PF(xe));
 
+   err = pci_iov_resource_extend(to_pci_dev(xe->drm.dev), VF_LMEM_BAR, 
true);
+   if (err)
+   xe_sriov_info(xe, "Failed to extend VF LMEM BAR: %d", err);
+
return drmm_mutex_init(&xe->drm, &xe->sriov.pf.master_lock);
 }
 
-- 
2.46.0



[PATCH v2 2/3] PCI: Allow extending VF BAR within original resource boundary

2024-09-19 Thread Michał Winiarski
VF MMIO resource reservation, either created by system firmware and
inherited by Linux PCI subsystem or created by the subsystem itself,
contains enough space to fit the BAR of all SR-IOV Virtual Functions
that can potentially be created (total VFs supported by the device).
This can be leveraged when the device is exposing lower than optimal BAR
size as a default, allowing access to the entire resource when lower
number of VFs are created.
It is achieved by dynamically resizing the BAR to largest possible value
that allows to fit all newly created VFs within the original resource
boundary.

Signed-off-by: Michał Winiarski 
---
 drivers/pci/iov.c   | 92 -
 drivers/pci/pci.h   |  1 +
 include/linux/pci.h |  3 ++
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index e8ccd2ae0f024..d88efbfa70e42 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -181,6 +181,86 @@ bool pci_iov_memory_decoding_enabled(struct pci_dev *dev)
return cmd & PCI_SRIOV_CTRL_MSE;
 }
 
+static void pci_iov_resource_do_extend(struct pci_dev *dev, int resno, u16 
num_vfs)
+{
+   resource_size_t size;
+   int ret, old, i;
+   u32 sizes;
+
+   pci_config_pm_runtime_get(dev);
+
+   if (pci_iov_memory_decoding_enabled(dev)) {
+   ret = -EBUSY;
+   goto err;
+   }
+
+   sizes = pci_rebar_get_possible_sizes(dev, resno);
+   if (!sizes) {
+   ret = -ENOTSUPP;
+   goto err;
+   }
+
+   old = pci_rebar_get_current_size(dev, resno);
+   if (old < 0) {
+   ret = old;
+   goto err;
+   }
+
+   while (sizes > 0) {
+   i = __fls(sizes);
+   size = pci_rebar_size_to_bytes(i);
+   if (size * num_vfs <= pci_resource_len(dev, resno)) {
+   if (i != old) {
+   ret = pci_rebar_set_size(dev, resno, size);
+   if (ret)
+   goto err;
+
+   pci_iov_resource_set_size(dev, resno, size);
+   pci_iov_update_resource(dev, resno);
+   }
+   break;
+   }
+   sizes &= ~BIT(i);
+   }
+
+   pci_config_pm_runtime_put(dev);
+
+   return;
+
+err:
+   dev_WARN(&dev->dev, "Failed to extend %s: %d\n",
+pci_resource_name(dev, resno), ret);
+
+   pci_config_pm_runtime_put(dev);
+}
+
+static void pci_iov_resource_do_restore(struct pci_dev *dev, int resno)
+{
+   if (dev->sriov->rebar_extend[resno - PCI_IOV_RESOURCES])
+   pci_iov_resource_do_extend(dev, resno, dev->sriov->total_VFs);
+}
+
+int pci_iov_resource_extend(struct pci_dev *dev, int resno, bool enable)
+{
+   if (!pci_resource_is_iov(dev, resno)) {
+   dev_WARN(&dev->dev, "%s is not an IOV resource\n",
+pci_resource_name(dev, resno));
+
+   return -ENODEV;
+   }
+
+   if (!pci_rebar_get_possible_sizes(dev, resno))
+   return -ENOTSUPP;
+
+   if (!enable)
+   pci_iov_resource_do_restore(dev, resno);
+
+   dev->sriov->rebar_extend[resno - PCI_IOV_RESOURCES] = enable;
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(pci_iov_resource_extend);
+
 static void pci_read_vf_config_common(struct pci_dev *virtfn)
 {
struct pci_dev *physfn = virtfn->physfn;
@@ -445,7 +525,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
  const char *buf, size_t count)
 {
struct pci_dev *pdev = to_pci_dev(dev);
-   int ret = 0;
+   int i, ret = 0;
u16 num_vfs;
 
if (kstrtou16(buf, 0, &num_vfs) < 0)
@@ -487,6 +567,11 @@ static ssize_t sriov_numvfs_store(struct device *dev,
goto exit;
}
 
+   for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+   if (pdev->sriov->rebar_extend[i])
+   pci_iov_resource_do_extend(pdev, i + PCI_IOV_RESOURCES, 
num_vfs);
+   }
+
ret = pdev->driver->sriov_configure(pdev, num_vfs);
if (ret < 0)
goto exit;
@@ -881,8 +966,13 @@ static int sriov_init(struct pci_dev *dev, int pos)
 
 static void sriov_release(struct pci_dev *dev)
 {
+   int i;
+
BUG_ON(dev->sriov->num_VFs);
 
+   for (i = 0; i < PCI_SRIOV_NUM_BARS; i++)
+   pci_iov_resource_do_restore(dev, i + PCI_IOV_RESOURCES);
+
if (dev != dev->sriov->dev)
pci_dev_put(dev->sriov->dev);
 
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index e763b3fd4c7a2..47ed2633232aa 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -385,6 +385,7 @@ struct pci_sriov {
u16 subsystem_vendor; /* VF

[PATCH v2 1/3] PCI: Add support for VF Resizable Bar extended cap

2024-09-19 Thread Michał Winiarski
Similar to regular resizable BAR, VF BAR can also be resized.
The structures are very similar, which means we can reuse most of the
implementation. See PCIe r4.0, sec 9.3.7.4.

Signed-off-by: Michał Winiarski 
---
 drivers/pci/iov.c | 28 ++
 drivers/pci/pci.c | 40 ++-
 drivers/pci/pci.h | 14 ++-
 drivers/pci/setup-res.c   | 44 ++-
 include/uapi/linux/pci_regs.h |  1 +
 5 files changed, 119 insertions(+), 8 deletions(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index aaa33e8dc4c97..e8ccd2ae0f024 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -153,6 +153,34 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, 
int resno)
return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
 }
 
+bool pci_resource_is_iov(struct pci_dev *dev, int resno)
+{
+   if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
+   return true;
+
+   return false;
+}
+
+void pci_iov_resource_set_size(struct pci_dev *dev, int resno, resource_size_t 
size)
+{
+   if (!pci_resource_is_iov(dev, resno)) {
+   dev_WARN(&dev->dev, "%s is not an IOV resource\n",
+pci_resource_name(dev, resno));
+   return;
+   }
+
+   dev->sriov->barsz[resno - PCI_IOV_RESOURCES] = size;
+}
+
+bool pci_iov_memory_decoding_enabled(struct pci_dev *dev)
+{
+   u16 cmd;
+
+   pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_CTRL, &cmd);
+
+   return cmd & PCI_SRIOV_CTRL_MSE;
+}
+
 static void pci_read_vf_config_common(struct pci_dev *virtfn)
 {
struct pci_dev *physfn = virtfn->physfn;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ffaaca0978cbc..d4522e365e7ba 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1901,6 +1901,35 @@ static void pci_restore_rebar_state(struct pci_dev *pdev)
}
 }
 
+static void pci_restore_vf_rebar_state(struct pci_dev *pdev)
+{
+   unsigned int pos, nbars, i;
+   u32 ctrl;
+
+   if (!pdev->is_physfn)
+   return;
+
+   pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_VF_REBAR);
+   if (!pos)
+   return;
+
+   pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+   nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+
+   for (i = 0; i < nbars; i++, pos += 8) {
+   struct resource *res;
+   int bar_idx, size;
+
+   pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+   bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+   res = pdev->resource + bar_idx;
+   size = pci_rebar_bytes_to_size(pdev->sriov->barsz[bar_idx]);
+   ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+   ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
+   pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+   }
+}
+
 /**
  * pci_restore_state - Restore the saved state of a PCI device
  * @dev: PCI device that we're dealing with
@@ -1916,6 +1945,7 @@ void pci_restore_state(struct pci_dev *dev)
pci_restore_ats_state(dev);
pci_restore_vc_state(dev);
pci_restore_rebar_state(dev);
+   pci_restore_vf_rebar_state(dev);
pci_restore_dpc_state(dev);
pci_restore_ptm_state(dev);
 
@@ -3703,10 +3733,18 @@ void pci_acs_init(struct pci_dev *dev)
  */
 static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
 {
+   int cap = PCI_EXT_CAP_ID_REBAR;
unsigned int pos, nbars, i;
u32 ctrl;
 
-   pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+#ifdef CONFIG_PCI_IOV
+   if (pci_resource_is_iov(pdev, bar)) {
+   cap = PCI_EXT_CAP_ID_VF_REBAR;
+   bar -= PCI_IOV_RESOURCES;
+   }
+#endif
+
+   pos = pci_find_ext_capability(pdev, cap);
if (!pos)
return -ENOTSUPP;
 
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 79c8398f39384..e763b3fd4c7a2 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -556,6 +556,9 @@ void pci_restore_iov_state(struct pci_dev *dev);
 int pci_iov_bus_range(struct pci_bus *bus);
 extern const struct attribute_group sriov_pf_dev_attr_group;
 extern const struct attribute_group sriov_vf_dev_attr_group;
+bool pci_resource_is_iov(struct pci_dev *dev, int resno);
+bool pci_iov_memory_decoding_enabled(struct pci_dev *dev);
+void pci_iov_resource_set_size(struct pci_dev *dev, int resno, resource_size_t 
size);
 #else
 static inline int pci_iov_init(struct pci_dev *dev)
 {
@@ -568,7 +571,16 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
 {
return 0;
 }
-
+static inline bool pci_iov_memory_decoding_enabled(struct pci_dev *dev)
+{
+   return false;
+}
+static inline bool pci_resource_is_iov(struct pci_dev *dev, int resno)
+{
+

[PATCH v2 0/3] PCI: VF resizable BAR

2024-09-19 Thread Michał Winiarski
Hi,

Resurrecting an old series [1], now that we have an in-tree user for it.

For regular BAR, drivers can use pci_resize_resource to resize it to the
desired size provided that it is supported by the hardware, which the
driver can query using pci_rebar_get_possible_sizes.
This series expands the API to work with IOV BAR as well.
It also adds the additional API to allow extending the VF BAR within the
original resource boundary.

Thanks,
-Michał

[1] 
https://lore.kernel.org/all/20211215141626.3090807-1-michal.winiar...@intel.com/

v1 -> v2:
- Add pci_iov_resource_extend() and usage in Xe driver
- Reduce the number of ifdefs (Christian)
- Drop patch 2/2 from v1 (Christian)
- Add a helper to avoid upsetting static analysis tools (Krzysztof)

Michał Winiarski (3):
  PCI: Add support for VF Resizable Bar extended cap
  PCI: Allow extending VF BAR within original resource boundary
  drm/xe/pf: Extend the VF LMEM BAR

 drivers/gpu/drm/xe/regs/xe_bars.h |   1 +
 drivers/gpu/drm/xe/xe_sriov_pf.c  |   8 ++
 drivers/pci/iov.c | 120 +-
 drivers/pci/pci.c |  40 +-
 drivers/pci/pci.h |  15 +++-
 drivers/pci/setup-res.c   |  44 +--
 include/linux/pci.h   |   3 +
 include/uapi/linux/pci_regs.h |   1 +
 8 files changed, 223 insertions(+), 9 deletions(-)

-- 
2.46.0



[PATCH v7 3/3] drm: Expand max DRM device number to full MINORBITS

2024-08-23 Thread Michał Winiarski
Having a limit of 64 DRM devices is not good enough for modern world
where we have multi-GPU servers, SR-IOV virtual functions and virtual
devices used for testing.
Let's utilize full minor range for DRM devices.
To avoid regressing the existing userspace, we're still maintaining the
numbering scheme where 0-63 is used for primary, 64-127 is reserved
(formerly for control) and 128-191 is used for render.
For minors >= 192, we're allocating minors dynamically on a first-come,
first-served basis.

Acked-by: James Zhu 
Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 7e0fa5a2a3182..c734e6a1c4ce2 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -121,10 +121,19 @@ static void drm_minor_alloc_release(struct drm_device 
*dev, void *data)
xa_erase(drm_minor_get_xa(minor->type), minor->index);
 }
 
+/*
+ * DRM used to support 64 devices, for backwards compatibility we need to 
maintain the
+ * minor allocation scheme where minors 0-63 are primary nodes, 64-127 are 
control nodes,
+ * and 128-191 are render nodes.
+ * After reaching the limit, we're allocating minors dynamically - first-come, 
first-serve.
+ * Accel nodes are using a distinct major, so the minors are allocated in 
continuous 0-MAX
+ * range.
+ */
 #define DRM_MINOR_LIMIT(t) ({ \
typeof(t) _t = (t); \
_t == DRM_MINOR_ACCEL ? XA_LIMIT(0, ACCEL_MAX_MINORS) : XA_LIMIT(64 * 
_t, 64 * _t + 63); \
 })
+#define DRM_EXTENDED_MINOR_LIMIT XA_LIMIT(192, (1 << MINORBITS) - 1)
 
 static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
 {
@@ -140,6 +149,9 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
drm_minor_type type)
 
r = xa_alloc(drm_minor_get_xa(type), &minor->index,
 NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL);
+   if (r == -EBUSY && (type == DRM_MINOR_PRIMARY || type == 
DRM_MINOR_RENDER))
+   r = xa_alloc(&drm_minors_xa, &minor->index,
+NULL, DRM_EXTENDED_MINOR_LIMIT, GFP_KERNEL);
if (r < 0)
return r;
 
-- 
2.46.0



[PATCH v7 2/3] accel: Use XArray instead of IDR for minors

2024-08-23 Thread Michał Winiarski
Accel minor management is based on DRM (and is also using struct
drm_minor internally), since DRM is using XArray for minors, it makes
sense to also convert accel.
As the two implementations are identical (only difference being the
underlying xarray), move the accel_minor_* functionality to DRM.

Acked-by: James Zhu 
Signed-off-by: Michał Winiarski 
---
 drivers/accel/drm_accel.c  | 110 +++--
 drivers/gpu/drm/drm_drv.c  |  66 ++--
 drivers/gpu/drm/drm_file.c |   2 +-
 drivers/gpu/drm/drm_internal.h |   4 --
 include/drm/drm_accel.h|  18 +-
 include/drm/drm_file.h |   5 ++
 6 files changed, 47 insertions(+), 158 deletions(-)

diff --git a/drivers/accel/drm_accel.c b/drivers/accel/drm_accel.c
index 16c3edb8c46ee..aa826033b0ceb 100644
--- a/drivers/accel/drm_accel.c
+++ b/drivers/accel/drm_accel.c
@@ -8,7 +8,7 @@
 
 #include 
 #include 
-#include 
+#include 
 
 #include 
 #include 
@@ -18,8 +18,7 @@
 #include 
 #include 
 
-static DEFINE_SPINLOCK(accel_minor_lock);
-static struct idr accel_minors_idr;
+DEFINE_XARRAY_ALLOC(accel_minors_xa);
 
 static struct dentry *accel_debugfs_root;
 
@@ -117,99 +116,6 @@ void accel_set_device_instance_params(struct device *kdev, 
int index)
kdev->type = &accel_sysfs_device_minor;
 }
 
-/**
- * accel_minor_alloc() - Allocates a new accel minor
- *
- * This function access the accel minors idr and allocates from it
- * a new id to represent a new accel minor
- *
- * Return: A new id on success or error code in case idr_alloc failed
- */
-int accel_minor_alloc(void)
-{
-   unsigned long flags;
-   int r;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-
-   return r;
-}
-
-/**
- * accel_minor_remove() - Remove an accel minor
- * @index: The minor id to remove.
- *
- * This function access the accel minors idr and removes from
- * it the member with the id that is passed to this function.
- */
-void accel_minor_remove(int index)
-{
-   unsigned long flags;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   idr_remove(&accel_minors_idr, index);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-}
-
-/**
- * accel_minor_replace() - Replace minor pointer in accel minors idr.
- * @minor: Pointer to the new minor.
- * @index: The minor id to replace.
- *
- * This function access the accel minors idr structure and replaces the pointer
- * that is associated with an existing id. Because the minor pointer can be
- * NULL, we need to explicitly pass the index.
- *
- * Return: 0 for success, negative value for error
- */
-void accel_minor_replace(struct drm_minor *minor, int index)
-{
-   unsigned long flags;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   idr_replace(&accel_minors_idr, minor, index);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-}
-
-/*
- * Looks up the given minor-ID and returns the respective DRM-minor object. The
- * refence-count of the underlying device is increased so you must release this
- * object with accel_minor_release().
- *
- * The object can be only a drm_minor that represents an accel device.
- *
- * As long as you hold this minor, it is guaranteed that the object and the
- * minor->dev pointer will stay valid! However, the device may get unplugged 
and
- * unregistered while you hold the minor.
- */
-static struct drm_minor *accel_minor_acquire(unsigned int minor_id)
-{
-   struct drm_minor *minor;
-   unsigned long flags;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   minor = idr_find(&accel_minors_idr, minor_id);
-   if (minor)
-   drm_dev_get(minor->dev);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-
-   if (!minor) {
-   return ERR_PTR(-ENODEV);
-   } else if (drm_dev_is_unplugged(minor->dev)) {
-   drm_dev_put(minor->dev);
-   return ERR_PTR(-ENODEV);
-   }
-
-   return minor;
-}
-
-static void accel_minor_release(struct drm_minor *minor)
-{
-   drm_dev_put(minor->dev);
-}
-
 /**
  * accel_open - open method for ACCEL file
  * @inode: device inode
@@ -227,7 +133,7 @@ int accel_open(struct inode *inode, struct file *filp)
struct drm_minor *minor;
int retcode;
 
-   minor = accel_minor_acquire(iminor(inode));
+   minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
 
@@ -246,7 +152,7 @@ int accel_open(struct inode *inode, struct file *filp)
 
 err_undo:
atomic_dec(&dev->open_count);
-   accel_minor_release(minor);
+   drm_minor_release(minor);
return retcode;
 }
 EXPORT_SYMBOL_GPL(accel_open);
@@ -257,7 +163,7 @@ static int accel_stub_open(struct i

[PATCH v7 1/3] drm: Use XArray instead of IDR for minors

2024-08-23 Thread Michał Winiarski
IDR is deprecated, and since XArray manages its own state with internal
locking, it simplifies the locking on DRM side.
Additionally, don't use the IRQ-safe variant, since operating on drm
minor is not done in IRQ context.

Suggested-by: Matthew Wilcox 
Acked-by: James Zhu 
Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 63 ---
 1 file changed, 25 insertions(+), 38 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 93543071a5008..ae675a588a1df 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -54,8 +55,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon 
Smirl");
 MODULE_DESCRIPTION("DRM shared core routines");
 MODULE_LICENSE("GPL and additional rights");
 
-static DEFINE_SPINLOCK(drm_minor_lock);
-static struct idr drm_minors_idr;
+static DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
 /*
  * If the drm core fails to init for whatever reason,
@@ -101,26 +101,23 @@ static struct drm_minor **drm_minor_get_slot(struct 
drm_device *dev,
 static void drm_minor_alloc_release(struct drm_device *dev, void *data)
 {
struct drm_minor *minor = data;
-   unsigned long flags;
 
WARN_ON(dev != minor->dev);
 
put_device(minor->kdev);
 
-   if (minor->type == DRM_MINOR_ACCEL) {
+   if (minor->type == DRM_MINOR_ACCEL)
accel_minor_remove(minor->index);
-   } else {
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_remove(&drm_minors_idr, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
-   }
+   else
+   xa_erase(&drm_minors_xa, minor->index);
 }
 
+#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 
63); })
+
 static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
 {
struct drm_minor *minor;
-   unsigned long flags;
-   int r;
+   int index, r;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
if (!minor)
@@ -129,24 +126,17 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
drm_minor_type type)
minor->type = type;
minor->dev = dev;
 
-   idr_preload(GFP_KERNEL);
if (type == DRM_MINOR_ACCEL) {
r = accel_minor_alloc();
+   index = r;
} else {
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   r = idr_alloc(&drm_minors_idr,
-   NULL,
-   64 * type,
-   64 * (type + 1),
-   GFP_NOWAIT);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   r = xa_alloc(&drm_minors_xa, &index, NULL, 
DRM_MINOR_LIMIT(type), GFP_KERNEL);
}
-   idr_preload_end();
 
if (r < 0)
return r;
 
-   minor->index = r;
+   minor->index = index;
 
r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
if (r)
@@ -163,7 +153,7 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
drm_minor_type type)
 static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type)
 {
struct drm_minor *minor;
-   unsigned long flags;
+   void *entry;
int ret;
 
DRM_DEBUG("\n");
@@ -189,9 +179,12 @@ static int drm_minor_register(struct drm_device *dev, enum 
drm_minor_type type)
if (minor->type == DRM_MINOR_ACCEL) {
accel_minor_replace(minor, minor->index);
} else {
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, minor, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   entry = xa_store(&drm_minors_xa, minor->index, minor, 
GFP_KERNEL);
+   if (xa_is_err(entry)) {
+   ret = xa_err(entry);
+   goto err_debugfs;
+   }
+   WARN_ON(entry);
}
 
DRM_DEBUG("new minor registered %d\n", minor->index);
@@ -205,20 +198,16 @@ static int drm_minor_register(struct drm_device *dev, 
enum drm_minor_type type)
 static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type 
type)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
minor = *drm_minor_get_slot(dev, type);
if (!minor || !device_is_registered(minor->kdev))
return;
 
/* replace @minor with NULL so lookups will fail from now on */
-   if (minor->type == DRM_MINOR_ACCEL) {
+   if (minor->type == DRM_MINOR_ACCEL)
accel_minor_replace(NULL, minor->index);
-   } else {
-   

[PATCH v7 0/3] drm: Use full allocated minor range for DRM

2024-08-23 Thread Michał Winiarski
64 DRM device nodes is not enough for everyone.
Upgrade it to ~512K (which definitely is more than enough).

Additionally, convert minors to use XArray instead of IDR to simplify
the locking.

Corresponding libdrm changes were merged in:
https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/305

v1 -> v2:
Don't touch DRM_MINOR_CONTROL and its range (Simon Ser)

v2 -> v3:
Don't use legacy scheme for >=192 minor range (Dave Airlie)
Add modparam for testing (Dave Airlie)
Add lockdep annotation for IDR (Daniel Vetter)

v3 -> v4:
Convert from IDR to XArray (Matthew Wilcox)

v4 -> v5:
Fixup IDR to XArray conversion (Matthew Wilcox)

v5 -> v6:
Also convert Accel to XArray
Rename skip_legacy_minors to force_extended_minors

v6 -> v7:
Drop the force_extended_minors patch intended for debug
Rebase on latest drm-tip
Update the cover letter, pointing out libdrm changes

Michał Winiarski (3):
  drm: Use XArray instead of IDR for minors
  accel: Use XArray instead of IDR for minors
  drm: Expand max DRM device number to full MINORBITS

 drivers/accel/drm_accel.c  | 110 +++--
 drivers/gpu/drm/drm_drv.c  |  97 ++---
 drivers/gpu/drm/drm_file.c |   2 +-
 drivers/gpu/drm/drm_internal.h |   4 --
 include/drm/drm_accel.h|  18 +-
 include/drm/drm_file.h |   5 ++
 6 files changed, 62 insertions(+), 174 deletions(-)

-- 
2.46.0



Re: [PATCH v6 0/4] drm: Use full allocated minor range for DRM

2024-08-13 Thread Michał Winiarski
On Mon, Aug 12, 2024 at 01:38:38PM GMT, Alex Deucher wrote:
> Are there any objections to this series?  We have been running into
> this limit as a problem for a while now on big servers.

I don't think there were any objections, just a general lack of
interest - so there are no R-b / Acks.
If you're interested to have a go at it - I can resend it.
It should still apply on latest drm-tip.

-Michał

> 
> Alex
> 
> On Mon, Jul 24, 2023 at 5:15 PM Michał Winiarski
>  wrote:
> >
> > 64 DRM device nodes is not enough for everyone.
> > Upgrade it to ~512K (which definitely is more than enough).
> >
> > To allow testing userspace support for >64 devices, add additional DRM
> > modparam (force_extended_minors) which causes DRM to skip allocating minors
> > in 0-192 range.
> > Additionally - convert minors to use XArray instead of IDR to simplify the
> > locking.
> >
> > v1 -> v2:
> > Don't touch DRM_MINOR_CONTROL and its range (Simon Ser)
> >
> > v2 -> v3:
> > Don't use legacy scheme for >=192 minor range (Dave Airlie)
> > Add modparam for testing (Dave Airlie)
> > Add lockdep annotation for IDR (Daniel Vetter)
> >
> > v3 -> v4:
> > Convert from IDR to XArray (Matthew Wilcox)
> >
> > v4 -> v5:
> > Fixup IDR to XArray conversion (Matthew Wilcox)
> >
> > v5 -> v6:
> > Also convert Accel to XArray
> > Rename skip_legacy_minors to force_extended_minors
> >
> > Michał Winiarski (4):
> >   drm: Use XArray instead of IDR for minors
> >   accel: Use XArray instead of IDR for minors
> >   drm: Expand max DRM device number to full MINORBITS
> >   drm: Introduce force_extended_minors modparam
> >
> >  drivers/accel/drm_accel.c  | 110 +++--
> >  drivers/gpu/drm/drm_drv.c  | 105 ---
> >  drivers/gpu/drm/drm_file.c |   2 +-
> >  drivers/gpu/drm/drm_internal.h |   4 --
> >  include/drm/drm_accel.h|  18 +-
> >  include/drm/drm_file.h |   5 ++
> >  6 files changed, 69 insertions(+), 175 deletions(-)
> >
> > --
> > 2.41.0
> >


Re: [PATCH v3 4/4] drm/xe/FLR: Support PCIe FLR

2024-04-25 Thread Michał Winiarski
On Thu, Apr 25, 2024 at 11:47:46AM +0530, Aravind Iddamsetty wrote:
> 
> On 25/04/24 04:59, Michał Winiarski wrote:
> > On Wed, Apr 24, 2024 at 10:42:59AM +0530, Aravind Iddamsetty wrote:
> >> On 24/04/24 05:19, Michał Winiarski wrote:
> >>> On Mon, Apr 22, 2024 at 12:27:56PM +0530, Aravind Iddamsetty wrote:
> >>>> PCI subsystem provides callbacks to inform the driver about a request to
> >>>> do function level reset by user, initiated by writing to sysfs entry
> >>>> /sys/bus/pci/devices/.../reset. This will allow the driver to handle FLR
> >>>> without the need to do unbind and rebind as the driver needs to
> >>>> reinitialize the device afresh post FLR.
> >>>>
> >>>> v2:
> >>>> 1. separate out gt idle and pci save/restore to a separate patch (Lucas)
> >>>> 2. Fixed the warnings seen around xe_guc_submit_stop, xe_guc_puc_fini
> >>>>
> >>>> v3: declare xe_pci_err_handlers as static(Michal)
> >>>>
> >>>> Cc: Rodrigo Vivi 
> >>>> Cc: Lucas De Marchi 
> >>>> Cc: Michal Wajdeczko 
> >>>>
> >>>> Reviewed-by: Rodrigo Vivi 
> >>>> Signed-off-by: Aravind Iddamsetty 
> >>>> ---
> >>>>  drivers/gpu/drm/xe/Makefile  |  1 +
> >>>>  drivers/gpu/drm/xe/xe_device_types.h |  3 +
> >>>>  drivers/gpu/drm/xe/xe_guc_pc.c   |  4 ++
> >>>>  drivers/gpu/drm/xe/xe_pci.c  |  9 ++-
> >>>>  drivers/gpu/drm/xe/xe_pci.h  |  2 +
> >>>>  drivers/gpu/drm/xe/xe_pci_err.c  | 88 
> >>>>  drivers/gpu/drm/xe/xe_pci_err.h  | 13 
> >>>>  7 files changed, 119 insertions(+), 1 deletion(-)
> >>>>  create mode 100644 drivers/gpu/drm/xe/xe_pci_err.c
> >>>>  create mode 100644 drivers/gpu/drm/xe/xe_pci_err.h
> >>>>
> >>>> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> >>>> index 8bc62bfbc679..693971a1fac0 100644
> >>>> --- a/drivers/gpu/drm/xe/Makefile
> >>>> +++ b/drivers/gpu/drm/xe/Makefile
> >>>> @@ -117,6 +117,7 @@ xe-y += xe_bb.o \
> >>>>  xe_module.o \
> >>>>  xe_pat.o \
> >>>>  xe_pci.o \
> >>>> +xe_pci_err.o \
> >>>>  xe_pcode.o \
> >>>>  xe_pm.o \
> >>>>  xe_preempt_fence.o \
> >>>> diff --git a/drivers/gpu/drm/xe/xe_device_types.h 
> >>>> b/drivers/gpu/drm/xe/xe_device_types.h
> >>>> index 0a66555229e9..8c749b378a92 100644
> >>>> --- a/drivers/gpu/drm/xe/xe_device_types.h
> >>>> +++ b/drivers/gpu/drm/xe/xe_device_types.h
> >>>> @@ -465,6 +465,9 @@ struct xe_device {
> >>>>  /** @pci_state: PCI state of device */
> >>>>  struct pci_saved_state *pci_state;
> >>>>  
> >>>> +/** @pci_device_is_reset: device went through PCIe FLR */
> >>>> +bool pci_device_is_reset;
> >>>> +
> >>>>  /* private: */
> >>>>  
> >>>>  #if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
> >>>> diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c 
> >>>> b/drivers/gpu/drm/xe/xe_guc_pc.c
> >>>> index 509649d0e65e..efba0fbe2f5c 100644
> >>>> --- a/drivers/gpu/drm/xe/xe_guc_pc.c
> >>>> +++ b/drivers/gpu/drm/xe/xe_guc_pc.c
> >>>> @@ -902,6 +902,10 @@ static void xe_guc_pc_fini(struct drm_device *drm, 
> >>>> void *arg)
> >>>>  return;
> >>>>  }
> >>>>  
> >>>> +/* We already have done this before going through a reset, so 
> >>>> skip here */
> >>>> +if (xe->pci_device_is_reset)
> >>>> +return;
> >>>> +
> >>>>  XE_WARN_ON(xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), 
> >>>> XE_FORCEWAKE_ALL));
> >>>>  XE_WARN_ON(xe_guc_pc_gucrc_disable(pc));
> >>>>  XE_WARN_ON(xe_guc_pc_stop(pc));
> >>>> diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
> >>>> index a62300990e19..b5a582afc9e7 100644
> >>>> --- a/drivers/gpu/drm/xe/xe_pci.c
> >>>> +++ b/drivers/gpu/drm/xe/xe_pci.c
> >>>&

Re: [PATCH v3 4/4] drm/xe/FLR: Support PCIe FLR

2024-04-24 Thread Michał Winiarski
On Wed, Apr 24, 2024 at 10:42:59AM +0530, Aravind Iddamsetty wrote:
> 
> On 24/04/24 05:19, Michał Winiarski wrote:
> > On Mon, Apr 22, 2024 at 12:27:56PM +0530, Aravind Iddamsetty wrote:
> >> PCI subsystem provides callbacks to inform the driver about a request to
> >> do function level reset by user, initiated by writing to sysfs entry
> >> /sys/bus/pci/devices/.../reset. This will allow the driver to handle FLR
> >> without the need to do unbind and rebind as the driver needs to
> >> reinitialize the device afresh post FLR.
> >>
> >> v2:
> >> 1. separate out gt idle and pci save/restore to a separate patch (Lucas)
> >> 2. Fixed the warnings seen around xe_guc_submit_stop, xe_guc_puc_fini
> >>
> >> v3: declare xe_pci_err_handlers as static(Michal)
> >>
> >> Cc: Rodrigo Vivi 
> >> Cc: Lucas De Marchi 
> >> Cc: Michal Wajdeczko 
> >>
> >> Reviewed-by: Rodrigo Vivi 
> >> Signed-off-by: Aravind Iddamsetty 
> >> ---
> >>  drivers/gpu/drm/xe/Makefile  |  1 +
> >>  drivers/gpu/drm/xe/xe_device_types.h |  3 +
> >>  drivers/gpu/drm/xe/xe_guc_pc.c   |  4 ++
> >>  drivers/gpu/drm/xe/xe_pci.c  |  9 ++-
> >>  drivers/gpu/drm/xe/xe_pci.h  |  2 +
> >>  drivers/gpu/drm/xe/xe_pci_err.c  | 88 
> >>  drivers/gpu/drm/xe/xe_pci_err.h  | 13 
> >>  7 files changed, 119 insertions(+), 1 deletion(-)
> >>  create mode 100644 drivers/gpu/drm/xe/xe_pci_err.c
> >>  create mode 100644 drivers/gpu/drm/xe/xe_pci_err.h
> >>
> >> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> >> index 8bc62bfbc679..693971a1fac0 100644
> >> --- a/drivers/gpu/drm/xe/Makefile
> >> +++ b/drivers/gpu/drm/xe/Makefile
> >> @@ -117,6 +117,7 @@ xe-y += xe_bb.o \
> >>xe_module.o \
> >>xe_pat.o \
> >>xe_pci.o \
> >> +  xe_pci_err.o \
> >>xe_pcode.o \
> >>xe_pm.o \
> >>xe_preempt_fence.o \
> >> diff --git a/drivers/gpu/drm/xe/xe_device_types.h 
> >> b/drivers/gpu/drm/xe/xe_device_types.h
> >> index 0a66555229e9..8c749b378a92 100644
> >> --- a/drivers/gpu/drm/xe/xe_device_types.h
> >> +++ b/drivers/gpu/drm/xe/xe_device_types.h
> >> @@ -465,6 +465,9 @@ struct xe_device {
> >>/** @pci_state: PCI state of device */
> >>struct pci_saved_state *pci_state;
> >>  
> >> +  /** @pci_device_is_reset: device went through PCIe FLR */
> >> +  bool pci_device_is_reset;
> >> +
> >>/* private: */
> >>  
> >>  #if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
> >> diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c 
> >> b/drivers/gpu/drm/xe/xe_guc_pc.c
> >> index 509649d0e65e..efba0fbe2f5c 100644
> >> --- a/drivers/gpu/drm/xe/xe_guc_pc.c
> >> +++ b/drivers/gpu/drm/xe/xe_guc_pc.c
> >> @@ -902,6 +902,10 @@ static void xe_guc_pc_fini(struct drm_device *drm, 
> >> void *arg)
> >>return;
> >>}
> >>  
> >> +  /* We already have done this before going through a reset, so skip here 
> >> */
> >> +  if (xe->pci_device_is_reset)
> >> +  return;
> >> +
> >>XE_WARN_ON(xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL));
> >>XE_WARN_ON(xe_guc_pc_gucrc_disable(pc));
> >>XE_WARN_ON(xe_guc_pc_stop(pc));
> >> diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
> >> index a62300990e19..b5a582afc9e7 100644
> >> --- a/drivers/gpu/drm/xe/xe_pci.c
> >> +++ b/drivers/gpu/drm/xe/xe_pci.c
> >> @@ -23,6 +23,7 @@
> >>  #include "xe_macros.h"
> >>  #include "xe_mmio.h"
> >>  #include "xe_module.h"
> >> +#include "xe_pci_err.h"
> >>  #include "xe_pci_types.h"
> >>  #include "xe_pm.h"
> >>  #include "xe_sriov.h"
> >> @@ -738,7 +739,7 @@ static void xe_pci_remove(struct pci_dev *pdev)
> >>pci_set_drvdata(pdev, NULL);
> >>  }
> >>  
> >> -static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id 
> >> *ent)
> >> +int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> >>  {
> >>const struct xe_device_desc *desc = (const void *)ent->driver_data;
> >>const struct xe_subplatform_desc *subplatform_desc;
> >> @@ -986,6 +987,11 @@ static const struct 

Re: [PATCH v3 4/4] drm/xe/FLR: Support PCIe FLR

2024-04-23 Thread Michał Winiarski
On Mon, Apr 22, 2024 at 12:27:56PM +0530, Aravind Iddamsetty wrote:
> PCI subsystem provides callbacks to inform the driver about a request to
> do function level reset by user, initiated by writing to sysfs entry
> /sys/bus/pci/devices/.../reset. This will allow the driver to handle FLR
> without the need to do unbind and rebind as the driver needs to
> reinitialize the device afresh post FLR.
> 
> v2:
> 1. separate out gt idle and pci save/restore to a separate patch (Lucas)
> 2. Fixed the warnings seen around xe_guc_submit_stop, xe_guc_puc_fini
> 
> v3: declare xe_pci_err_handlers as static(Michal)
> 
> Cc: Rodrigo Vivi 
> Cc: Lucas De Marchi 
> Cc: Michal Wajdeczko 
> 
> Reviewed-by: Rodrigo Vivi 
> Signed-off-by: Aravind Iddamsetty 
> ---
>  drivers/gpu/drm/xe/Makefile  |  1 +
>  drivers/gpu/drm/xe/xe_device_types.h |  3 +
>  drivers/gpu/drm/xe/xe_guc_pc.c   |  4 ++
>  drivers/gpu/drm/xe/xe_pci.c  |  9 ++-
>  drivers/gpu/drm/xe/xe_pci.h  |  2 +
>  drivers/gpu/drm/xe/xe_pci_err.c  | 88 
>  drivers/gpu/drm/xe/xe_pci_err.h  | 13 
>  7 files changed, 119 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/xe/xe_pci_err.c
>  create mode 100644 drivers/gpu/drm/xe/xe_pci_err.h
> 
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> index 8bc62bfbc679..693971a1fac0 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -117,6 +117,7 @@ xe-y += xe_bb.o \
>   xe_module.o \
>   xe_pat.o \
>   xe_pci.o \
> + xe_pci_err.o \
>   xe_pcode.o \
>   xe_pm.o \
>   xe_preempt_fence.o \
> diff --git a/drivers/gpu/drm/xe/xe_device_types.h 
> b/drivers/gpu/drm/xe/xe_device_types.h
> index 0a66555229e9..8c749b378a92 100644
> --- a/drivers/gpu/drm/xe/xe_device_types.h
> +++ b/drivers/gpu/drm/xe/xe_device_types.h
> @@ -465,6 +465,9 @@ struct xe_device {
>   /** @pci_state: PCI state of device */
>   struct pci_saved_state *pci_state;
>  
> + /** @pci_device_is_reset: device went through PCIe FLR */
> + bool pci_device_is_reset;
> +
>   /* private: */
>  
>  #if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
> diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c
> index 509649d0e65e..efba0fbe2f5c 100644
> --- a/drivers/gpu/drm/xe/xe_guc_pc.c
> +++ b/drivers/gpu/drm/xe/xe_guc_pc.c
> @@ -902,6 +902,10 @@ static void xe_guc_pc_fini(struct drm_device *drm, void 
> *arg)
>   return;
>   }
>  
> + /* We already have done this before going through a reset, so skip here 
> */
> + if (xe->pci_device_is_reset)
> + return;
> +
>   XE_WARN_ON(xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL));
>   XE_WARN_ON(xe_guc_pc_gucrc_disable(pc));
>   XE_WARN_ON(xe_guc_pc_stop(pc));
> diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
> index a62300990e19..b5a582afc9e7 100644
> --- a/drivers/gpu/drm/xe/xe_pci.c
> +++ b/drivers/gpu/drm/xe/xe_pci.c
> @@ -23,6 +23,7 @@
>  #include "xe_macros.h"
>  #include "xe_mmio.h"
>  #include "xe_module.h"
> +#include "xe_pci_err.h"
>  #include "xe_pci_types.h"
>  #include "xe_pm.h"
>  #include "xe_sriov.h"
> @@ -738,7 +739,7 @@ static void xe_pci_remove(struct pci_dev *pdev)
>   pci_set_drvdata(pdev, NULL);
>  }
>  
> -static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id 
> *ent)
> +int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>  {
>   const struct xe_device_desc *desc = (const void *)ent->driver_data;
>   const struct xe_subplatform_desc *subplatform_desc;
> @@ -986,6 +987,11 @@ static const struct dev_pm_ops xe_pm_ops = {
>  };
>  #endif
>  
> +static const struct pci_error_handlers xe_pci_err_handlers = {
> + .reset_prepare = xe_pci_reset_prepare,
> + .reset_done = xe_pci_reset_done,
> +};
> +
>  static struct pci_driver xe_pci_driver = {
>   .name = DRIVER_NAME,
>   .id_table = pciidlist,
> @@ -995,6 +1001,7 @@ static struct pci_driver xe_pci_driver = {
>  #ifdef CONFIG_PM_SLEEP
>   .driver.pm = &xe_pm_ops,
>  #endif
> + .err_handler = &xe_pci_err_handlers,
>  };
>  
>  int xe_register_pci_driver(void)
> diff --git a/drivers/gpu/drm/xe/xe_pci.h b/drivers/gpu/drm/xe/xe_pci.h
> index 73b90a430d1f..9faf5380a09e 100644
> --- a/drivers/gpu/drm/xe/xe_pci.h
> +++ b/drivers/gpu/drm/xe/xe_pci.h
> @@ -7,8 +7,10 @@
>  #define _XE_PCI_H_
>  
>  struct pci_dev;
> +struct pci_device_id;
>  
>  int xe_register_pci_driver(void);
>  void xe_unregister_pci_driver(void);
>  void xe_load_pci_state(struct pci_dev *pdev);
> +int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
>  #endif
> diff --git a/drivers/gpu/drm/xe/xe_pci_err.c b/drivers/gpu/drm/xe/xe_pci_err.c
> new file mode 100644
> index ..5306925ea2fa
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_pci_err.c
> @@ -0,0 +1,88 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2024 Intel

[PATCH] drm/tests: mm: Convert to drm_dbg_printer

2024-02-09 Thread Michał Winiarski
Fix one of the tests in drm_mm that was not converted prior to
drm_debug_printer removal, causing tests build failure.

Fixes: e154c4fc7bf2d ("drm: remove drm_debug_printer in favor of 
drm_dbg_printer")
Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_mm_test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tests/drm_mm_test.c 
b/drivers/gpu/drm/tests/drm_mm_test.c
index 1eb0c304f9607..3488d930e3a38 100644
--- a/drivers/gpu/drm/tests/drm_mm_test.c
+++ b/drivers/gpu/drm/tests/drm_mm_test.c
@@ -188,7 +188,7 @@ static void drm_test_mm_init(struct kunit *test)
 
 static void drm_test_mm_debug(struct kunit *test)
 {
-   struct drm_printer p = drm_debug_printer(test->name);
+   struct drm_printer p = drm_dbg_printer(NULL, DRM_UT_CORE, test->name);
struct drm_mm mm;
struct drm_mm_node nodes[2];
 
-- 
2.43.0



[PATCH] drm/tests: mm: Call drm_mm_print in drm_test_mm_debug

2024-01-16 Thread Michał Winiarski
The original intent behind the test was to sanity check whether calling
the debug iterator (drm_mm_print) doesn't cause any problems.
Unfortunately - this call got accidentally removed during KUnit
transition. Restore it.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_mm_test.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tests/drm_mm_test.c 
b/drivers/gpu/drm/tests/drm_mm_test.c
index 4e9247cf9977f..1eb0c304f9607 100644
--- a/drivers/gpu/drm/tests/drm_mm_test.c
+++ b/drivers/gpu/drm/tests/drm_mm_test.c
@@ -188,13 +188,13 @@ static void drm_test_mm_init(struct kunit *test)
 
 static void drm_test_mm_debug(struct kunit *test)
 {
+   struct drm_printer p = drm_debug_printer(test->name);
struct drm_mm mm;
struct drm_mm_node nodes[2];
 
/* Create a small drm_mm with a couple of nodes and a few holes, and
 * check that the debug iterator doesn't explode over a trivial drm_mm.
 */
-
drm_mm_init(&mm, 0, 4096);
 
memset(nodes, 0, sizeof(nodes));
@@ -209,6 +209,9 @@ static void drm_test_mm_debug(struct kunit *test)
KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]),
   "failed to reserve node[0] {start=%lld, 
size=%lld)\n",
   nodes[0].start, nodes[0].size);
+
+   drm_mm_print(&mm, &p);
+   KUNIT_SUCCEED(test);
 }
 
 static bool expect_insert(struct kunit *test, struct drm_mm *mm,
-- 
2.43.0



[PATCH v5 4/5] drm/tests: managed: Extract device initialization into test init

2024-01-15 Thread Michał Winiarski
It simplifies the process of extending the test suite with additional
test cases without unnecessary duplication.

Signed-off-by: Michał Winiarski 
Acked-by: Maxime Ripard 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 41 +++-
 1 file changed, 26 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index b5cf46d9f5cf8..d936c879a4a30 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -12,6 +12,7 @@
 #define TEST_TIMEOUT_MS100
 
 struct managed_test_priv {
+   struct drm_device *drm;
bool action_done;
wait_queue_head_t action_wq;
 };
@@ -29,11 +30,28 @@ static void drm_action(struct drm_device *drm, void *ptr)
  * device is released.
  */
 static void drm_test_managed_run_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+
+   ret = drm_dev_register(priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm->dev);
+
+   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
+  
msecs_to_jiffies(TEST_TIMEOUT_MS));
+   KUNIT_EXPECT_GT(test, ret, 0);
+}
+
+static int drm_managed_test_init(struct kunit *test)
 {
struct managed_test_priv *priv;
-   struct drm_device *drm;
struct device *dev;
-   int ret;
 
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
@@ -47,21 +65,13 @@ static void drm_test_managed_run_action(struct kunit *test)
 * to remain allocated beyond both parent device and drm_device
 * lifetime.
 */
-   drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
DRIVER_MODESET);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+   priv->drm = __drm_kunit_helper_alloc_drm_device(test, dev, 
sizeof(*priv->drm), 0,
+   DRIVER_MODESET);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
 
-   ret = drmm_add_action_or_reset(drm, drm_action, priv);
-   KUNIT_EXPECT_EQ(test, ret, 0);
+   test->priv = priv;
 
-   ret = drm_dev_register(drm, 0);
-   KUNIT_ASSERT_EQ(test, ret, 0);
-
-   drm_dev_unregister(drm);
-   drm_kunit_helper_free_device(test, dev);
-
-   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
-  
msecs_to_jiffies(TEST_TIMEOUT_MS));
-   KUNIT_EXPECT_GT(test, ret, 0);
+   return 0;
 }
 
 static struct kunit_case drm_managed_tests[] = {
@@ -71,6 +81,7 @@ static struct kunit_case drm_managed_tests[] = {
 
 static struct kunit_suite drm_managed_test_suite = {
.name = "drm_managed",
+   .init = drm_managed_test_init,
.test_cases = drm_managed_tests
 };
 
-- 
2.43.0



[PATCH v5 5/5] drm/tests: managed: Add a simple test for drmm_managed_release

2024-01-15 Thread Michał Winiarski
Add a simple test that checks whether the action is called when
drmm_managed_release is called.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 25 
 1 file changed, 25 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index d936c879a4a30..76eb273c9b364 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -25,6 +25,30 @@ static void drm_action(struct drm_device *drm, void *ptr)
wake_up_interruptible(&priv->action_wq);
 }
 
+/*
+ * The test verifies that the release action is called when
+ * drmm_release_action is called.
+ */
+static void drm_test_managed_release_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+
+   ret = drm_dev_register(priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drmm_release_action(priv->drm, drm_action, priv);
+   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
+  
msecs_to_jiffies(TEST_TIMEOUT_MS));
+   KUNIT_EXPECT_GT(test, ret, 0);
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm->dev);
+}
+
 /*
  * The test verifies that the release action is called automatically when the
  * device is released.
@@ -75,6 +99,7 @@ static int drm_managed_test_init(struct kunit *test)
 }
 
 static struct kunit_case drm_managed_tests[] = {
+   KUNIT_CASE(drm_test_managed_release_action),
KUNIT_CASE(drm_test_managed_run_action),
{}
 };
-- 
2.43.0



[PATCH v5 2/5] drm/tests: managed: Rename the suite name to match other DRM tests

2024-01-15 Thread Michał Winiarski
DRM tests use "_" rather than "-" as word separator. Rename the test
suite to match other tests.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 1652dca11d30c..f85dada4de0a9 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -61,7 +61,7 @@ static struct kunit_case drm_managed_tests[] = {
 };
 
 static struct kunit_suite drm_managed_test_suite = {
-   .name = "drm-test-managed",
+   .name = "drm_managed",
.test_cases = drm_managed_tests
 };
 
-- 
2.43.0



[PATCH v5 3/5] drm/tests: managed: Add comments about test intent

2024-01-15 Thread Michał Winiarski
Add comments explaining the intention behind the test and certain
implementation details related to device lifetime.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 9 +
 1 file changed, 9 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index f85dada4de0a9..b5cf46d9f5cf8 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -24,6 +24,10 @@ static void drm_action(struct drm_device *drm, void *ptr)
wake_up_interruptible(&priv->action_wq);
 }
 
+/*
+ * The test verifies that the release action is called automatically when the
+ * device is released.
+ */
 static void drm_test_managed_run_action(struct kunit *test)
 {
struct managed_test_priv *priv;
@@ -38,6 +42,11 @@ static void drm_test_managed_run_action(struct kunit *test)
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
 
+   /*
+* DRM device can't be embedded in priv, since priv->action_done needs
+* to remain allocated beyond both parent device and drm_device
+* lifetime.
+*/
drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
DRIVER_MODESET);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
 
-- 
2.43.0



[PATCH v5 1/5] drm/managed: Add drmm_release_action

2024-01-15 Thread Michał Winiarski
Similar to devres equivalent, it allows to call the "release" action
directly and remove the resource from the managed resources list.

Signed-off-by: Michał Winiarski 
Reviewed-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_managed.c | 39 +++
 include/drm/drm_managed.h |  4 
 2 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c
index bcd111404b128..7646f67bda4e4 100644
--- a/drivers/gpu/drm/drm_managed.c
+++ b/drivers/gpu/drm/drm_managed.c
@@ -176,6 +176,45 @@ int __drmm_add_action_or_reset(struct drm_device *dev,
 }
 EXPORT_SYMBOL(__drmm_add_action_or_reset);
 
+/**
+ * drmm_release_action - release a managed action from a &drm_device
+ * @dev: DRM device
+ * @action: function which would be called when @dev is released
+ * @data: opaque pointer, passed to @action
+ *
+ * This function calls the @action previously added by drmm_add_action()
+ * immediately.
+ * The @action is removed from the list of cleanup actions for @dev,
+ * which means that it won't be called in the final drm_dev_put().
+ */
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data)
+{
+   struct drmres *dr_match = NULL, *dr;
+   unsigned long flags;
+
+   spin_lock_irqsave(&dev->managed.lock, flags);
+   list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) {
+   if (dr->node.release == action) {
+   if (!data || (data && *(void **)dr->data == data)) {
+   dr_match = dr;
+   del_dr(dev, dr_match);
+   break;
+   }
+   }
+   }
+   spin_unlock_irqrestore(&dev->managed.lock, flags);
+
+   if (WARN_ON(!dr_match))
+   return;
+
+   action(dev, data);
+
+   free_dr(dr_match);
+}
+EXPORT_SYMBOL(drmm_release_action);
+
 /**
  * drmm_kmalloc - &drm_device managed kmalloc()
  * @dev: DRM device
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h
index ad08f834af408..f547b09ca0239 100644
--- a/include/drm/drm_managed.h
+++ b/include/drm/drm_managed.h
@@ -45,6 +45,10 @@ int __must_check __drmm_add_action_or_reset(struct 
drm_device *dev,
drmres_release_t action,
void *data, const char *name);
 
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data);
+
 void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc;
 
 /**
-- 
2.43.0



[PATCH v5 0/5] drm/managed: Add drmm_release_action

2024-01-15 Thread Michał Winiarski
Upcoming Intel Xe driver will need to have a more fine-grained control
over DRM managed actions - namely, the ability to release a given
action, triggering it manually at a different point in time than the
final drm_dev_put().
This series adds a drmm_release_action function (which is similar to
devres devm_release_action) and a simple test that uses it.

v1 -> v2:
- Split the test changes (Maxime)
- Simplify priv lifetime management (Maxime)

v2 -> v3:
- Order tests alphabetically (Maxime)
- Add comments explaining the intention behind the tests and the reason
  why DRM device can't be embedded inside test priv (Maxime)
- Bring back priv lifetime management from v1 to avoid use-after-free

v3 -> v4:
- Split test changes into smaller patches (Maxime)
- Remove the waitqueue usage in tests
- Rename the test suite to match other DRM tests

v4 -> v5:
- Simplify the release test (Maxime)
- Rename suite to "drm_managed" (Maxime)
- Drop redundant messages from asserts (Maxime)

Michał Winiarski (5):
  drm/managed: Add drmm_release_action
  drm/tests: managed: Rename the suite name to match other DRM tests
  drm/tests: managed: Add comments about test intent
  drm/tests: managed: Extract device initialization into test init
  drm/tests: managed: Add a simple test for drmm_managed_release

 drivers/gpu/drm/drm_managed.c| 39 
 drivers/gpu/drm/tests/drm_managed_test.c | 77 +++-
 include/drm/drm_managed.h|  4 ++
 3 files changed, 104 insertions(+), 16 deletions(-)

-- 
2.43.0



Re: Re: [PATCH v4 6/6] drm/tests: managed: Add a simple test for drmm_managed_release

2024-01-15 Thread Michał Winiarski
On Wed, Jan 10, 2024 at 04:56:27PM +0100, Maxime Ripard wrote:
> On Fri, Jan 05, 2024 at 11:13:24AM +0100, Michał Winiarski wrote:
> > Add a simple test that checks whether the action is indeed called right
> > away and that it is not called on the final drm_dev_put().
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  drivers/gpu/drm/tests/drm_managed_test.c | 28 
> >  1 file changed, 28 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
> > b/drivers/gpu/drm/tests/drm_managed_test.c
> > index c1fc1f0aac9b2..91863642efc13 100644
> > --- a/drivers/gpu/drm/tests/drm_managed_test.c
> > +++ b/drivers/gpu/drm/tests/drm_managed_test.c
> > @@ -41,6 +41,33 @@ static void drm_test_managed_run_action(struct kunit 
> > *test)
> > KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
> > called");
> >  }
> >  
> > +/*
> > + * The test verifies that the release action is called immediately when
> > + * drmm_release_action is called and that it is not called for a second 
> > time
> > + * when the device is released.
> > + */
> > +static void drm_test_managed_release_action(struct kunit *test)
> > +{
> > +   struct managed_test_priv *priv = test->priv;
> > +   int ret;
> > +
> > +   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_EQ(test, ret, 0);
> > +
> > +   ret = drm_dev_register(priv->drm, 0);
> > +   KUNIT_ASSERT_EQ(test, ret, 0);
> > +
> > +   drmm_release_action(priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
> > called");
> > +   priv->action_done = false;
> > +
> > +   drm_dev_unregister(priv->drm);
> > +   drm_kunit_helper_free_device(test, priv->drm->dev);
> > +
> > +   KUNIT_EXPECT_FALSE_MSG(test, priv->action_done,
> > +  "Unexpected release action call during cleanup");
> > +}
> > +
> 
> I guess we can have something simpler if we switch action_done to a
> counter and just check that the counter didn't increase.
> 
> And I think the custom messages should be removed there too.
> 
> Maxime

I'll drop the custom messages here and in the previous patch.

I'll also simplify this test in the way you suggested in previous
revision, to not check for release action call on cleanup.

Thanks,
-Michał


[PATCH v4 6/6] drm/tests: managed: Add a simple test for drmm_managed_release

2024-01-05 Thread Michał Winiarski
Add a simple test that checks whether the action is indeed called right
away and that it is not called on the final drm_dev_put().

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 28 
 1 file changed, 28 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index c1fc1f0aac9b2..91863642efc13 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -41,6 +41,33 @@ static void drm_test_managed_run_action(struct kunit *test)
KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
called");
 }
 
+/*
+ * The test verifies that the release action is called immediately when
+ * drmm_release_action is called and that it is not called for a second time
+ * when the device is released.
+ */
+static void drm_test_managed_release_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+
+   ret = drm_dev_register(priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drmm_release_action(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
called");
+   priv->action_done = false;
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm->dev);
+
+   KUNIT_EXPECT_FALSE_MSG(test, priv->action_done,
+  "Unexpected release action call during cleanup");
+}
+
 static int drm_managed_test_init(struct kunit *test)
 {
struct managed_test_priv *priv;
@@ -67,6 +94,7 @@ static int drm_managed_test_init(struct kunit *test)
 }
 
 static struct kunit_case drm_managed_tests[] = {
+   KUNIT_CASE(drm_test_managed_release_action),
KUNIT_CASE(drm_test_managed_run_action),
{}
 };
-- 
2.43.0



[PATCH v4 5/6] drm/tests: managed: Extract device initialization into test init

2024-01-05 Thread Michał Winiarski
It simplifies the process of extending the test suite with additional
test cases without unnecessary duplication.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 37 +++-
 1 file changed, 24 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 986a38c9144a5..c1fc1f0aac9b2 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -9,6 +9,7 @@
 #include 
 
 struct managed_test_priv {
+   struct drm_device *drm;
bool action_done;
 };
 
@@ -24,11 +25,26 @@ static void drm_action(struct drm_device *drm, void *ptr)
  * device is released.
  */
 static void drm_test_managed_run_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+
+   ret = drm_dev_register(priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm->dev);
+
+   KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
called");
+}
+
+static int drm_managed_test_init(struct kunit *test)
 {
struct managed_test_priv *priv;
-   struct drm_device *drm;
struct device *dev;
-   int ret;
 
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
@@ -41,19 +57,13 @@ static void drm_test_managed_run_action(struct kunit *test)
 * to remain allocated beyond both parent device and drm_device
 * lifetime.
 */
-   drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
DRIVER_MODESET);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+   priv->drm = __drm_kunit_helper_alloc_drm_device(test, dev, 
sizeof(*priv->drm), 0,
+   DRIVER_MODESET);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
 
-   ret = drmm_add_action_or_reset(drm, drm_action, priv);
-   KUNIT_EXPECT_EQ(test, ret, 0);
+   test->priv = priv;
 
-   ret = drm_dev_register(drm, 0);
-   KUNIT_ASSERT_EQ(test, ret, 0);
-
-   drm_dev_unregister(drm);
-   drm_kunit_helper_free_device(test, dev);
-
-   KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
called");
+   return 0;
 }
 
 static struct kunit_case drm_managed_tests[] = {
@@ -63,6 +73,7 @@ static struct kunit_case drm_managed_tests[] = {
 
 static struct kunit_suite drm_managed_test_suite = {
.name = "drm_test_managed",
+   .init = drm_managed_test_init,
.test_cases = drm_managed_tests
 };
 
-- 
2.43.0



[PATCH v4 4/6] drm/tests: managed: Add comments and expect fail messages

2024-01-05 Thread Michał Winiarski
Add comments explaining the intention behind the test and certain
implementation details related to device lifetime.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 11 ++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index e4790ae838ba7..986a38c9144a5 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -19,6 +19,10 @@ static void drm_action(struct drm_device *drm, void *ptr)
priv->action_done = true;
 }
 
+/*
+ * The test verifies that the release action is called automatically when the
+ * device is released.
+ */
 static void drm_test_managed_run_action(struct kunit *test)
 {
struct managed_test_priv *priv;
@@ -32,6 +36,11 @@ static void drm_test_managed_run_action(struct kunit *test)
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
 
+   /*
+* DRM device can't be embedded in priv, since priv->action_done needs
+* to remain allocated beyond both parent device and drm_device
+* lifetime.
+*/
drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
DRIVER_MODESET);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
 
@@ -44,7 +53,7 @@ static void drm_test_managed_run_action(struct kunit *test)
drm_dev_unregister(drm);
drm_kunit_helper_free_device(test, dev);
 
-   KUNIT_EXPECT_TRUE(test, priv->action_done);
+   KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
called");
 }
 
 static struct kunit_case drm_managed_tests[] = {
-- 
2.43.0



[PATCH v4 3/6] drm/tests: managed: Remove the waitqueue usage

2024-01-05 Thread Michał Winiarski
DRM managed release (drm_managed_release) is called as part of devres
release (devres_release_all), which is not async.
The release action should have already been executed once
drm_kunit_helper_free_device exits, meaning that there's no need to use
a waitqueue - we can just inspect the "action_done" state directly.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 10 +-
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 659af5abb8014..e4790ae838ba7 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -8,12 +8,8 @@
 
 #include 
 
-/* Ought to be enough for anybody */
-#define TEST_TIMEOUT_MS100
-
 struct managed_test_priv {
bool action_done;
-   wait_queue_head_t action_wq;
 };
 
 static void drm_action(struct drm_device *drm, void *ptr)
@@ -21,7 +17,6 @@ static void drm_action(struct drm_device *drm, void *ptr)
struct managed_test_priv *priv = ptr;
 
priv->action_done = true;
-   wake_up_interruptible(&priv->action_wq);
 }
 
 static void drm_test_managed_run_action(struct kunit *test)
@@ -33,7 +28,6 @@ static void drm_test_managed_run_action(struct kunit *test)
 
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
-   init_waitqueue_head(&priv->action_wq);
 
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
@@ -50,9 +44,7 @@ static void drm_test_managed_run_action(struct kunit *test)
drm_dev_unregister(drm);
drm_kunit_helper_free_device(test, dev);
 
-   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
-  
msecs_to_jiffies(TEST_TIMEOUT_MS));
-   KUNIT_EXPECT_GT(test, ret, 0);
+   KUNIT_EXPECT_TRUE(test, priv->action_done);
 }
 
 static struct kunit_case drm_managed_tests[] = {
-- 
2.43.0



[PATCH v4 2/6] drm/tests: managed: Rename the suite name to match other DRM tests

2024-01-05 Thread Michał Winiarski
DRM tests use "_" rather than "-" as word separator. Rename the test
suite to match other tests.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 1652dca11d30c..659af5abb8014 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -61,7 +61,7 @@ static struct kunit_case drm_managed_tests[] = {
 };
 
 static struct kunit_suite drm_managed_test_suite = {
-   .name = "drm-test-managed",
+   .name = "drm_test_managed",
.test_cases = drm_managed_tests
 };
 
-- 
2.43.0



[PATCH v4 1/6] drm/managed: Add drmm_release_action

2024-01-05 Thread Michał Winiarski
Similar to devres equivalent, it allows to call the "release" action
directly and remove the resource from the managed resources list.

Signed-off-by: Michał Winiarski 
Reviewed-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_managed.c | 39 +++
 include/drm/drm_managed.h |  4 
 2 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c
index bcd111404b128..7646f67bda4e4 100644
--- a/drivers/gpu/drm/drm_managed.c
+++ b/drivers/gpu/drm/drm_managed.c
@@ -176,6 +176,45 @@ int __drmm_add_action_or_reset(struct drm_device *dev,
 }
 EXPORT_SYMBOL(__drmm_add_action_or_reset);
 
+/**
+ * drmm_release_action - release a managed action from a &drm_device
+ * @dev: DRM device
+ * @action: function which would be called when @dev is released
+ * @data: opaque pointer, passed to @action
+ *
+ * This function calls the @action previously added by drmm_add_action()
+ * immediately.
+ * The @action is removed from the list of cleanup actions for @dev,
+ * which means that it won't be called in the final drm_dev_put().
+ */
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data)
+{
+   struct drmres *dr_match = NULL, *dr;
+   unsigned long flags;
+
+   spin_lock_irqsave(&dev->managed.lock, flags);
+   list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) {
+   if (dr->node.release == action) {
+   if (!data || (data && *(void **)dr->data == data)) {
+   dr_match = dr;
+   del_dr(dev, dr_match);
+   break;
+   }
+   }
+   }
+   spin_unlock_irqrestore(&dev->managed.lock, flags);
+
+   if (WARN_ON(!dr_match))
+   return;
+
+   action(dev, data);
+
+   free_dr(dr_match);
+}
+EXPORT_SYMBOL(drmm_release_action);
+
 /**
  * drmm_kmalloc - &drm_device managed kmalloc()
  * @dev: DRM device
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h
index ad08f834af408..f547b09ca0239 100644
--- a/include/drm/drm_managed.h
+++ b/include/drm/drm_managed.h
@@ -45,6 +45,10 @@ int __must_check __drmm_add_action_or_reset(struct 
drm_device *dev,
drmres_release_t action,
void *data, const char *name);
 
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data);
+
 void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc;
 
 /**
-- 
2.43.0



[PATCH v4 0/6] drm/managed: Add drmm_release_action

2024-01-05 Thread Michał Winiarski
Upcoming Intel Xe driver will need to have a more fine-grained control
over DRM managed actions - namely, the ability to release a given
action, triggering it manually at a different point in time than the
final drm_dev_put().
This series adds a drmm_release_action function (which is similar to
devres devm_release_action) and a simple test that uses it.

v1 -> v2:
- Split the test changes (Maxime)
- Simplify priv lifetime management (Maxime)

v2 -> v3:
- Order tests alphabetically (Maxime)
- Add comments explaining the intention behind the tests and the reason
  why DRM device can't be embedded inside test priv (Maxime)
- Bring back priv lifetime management from v1 to avoid use-after-free

v3 -> v4:
- Split test changes into smaller patches (Maxime)
- Remove the waitqueue usage in tests
- Rename the test suite to match other DRM tests

Michał Winiarski (6):
  drm/managed: Add drmm_release_action
  drm/tests: managed: Rename the suite name to match other DRM tests
  drm/tests: managed: Remove the waitqueue usage
  drm/tests: managed: Add comments and expect fail messages
  drm/tests: managed: Extract device initialization into test init
  drm/tests: managed: Add a simple test for drmm_managed_release

 drivers/gpu/drm/drm_managed.c| 39 +++
 drivers/gpu/drm/tests/drm_managed_test.c | 84 +---
 include/drm/drm_managed.h|  4 ++
 3 files changed, 105 insertions(+), 22 deletions(-)

-- 
2.43.0



Re: [PATCH v3 3/3] drm/tests: managed: Add a simple test for drmm_managed_release

2024-01-05 Thread Michał Winiarski
On Fri, Dec 15, 2023 at 05:31:38PM +0100, Maxime Ripard wrote:
> Hi,
> 
> On Mon, Dec 11, 2023 at 11:09:39PM +0100, Michał Winiarski wrote:
> > Add a simple test that checks whether the action is indeed called right
> > away and that it is not called on the final drm_dev_put().
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  drivers/gpu/drm/tests/drm_managed_test.c | 29 
> >  1 file changed, 29 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
> > b/drivers/gpu/drm/tests/drm_managed_test.c
> > index 15bd2474440b5..ef5e784afbc6d 100644
> > --- a/drivers/gpu/drm/tests/drm_managed_test.c
> > +++ b/drivers/gpu/drm/tests/drm_managed_test.c
> > @@ -48,6 +48,34 @@ static void drm_test_managed_run_action(struct kunit 
> > *test)
> > KUNIT_EXPECT_GT_MSG(test, ret, 0, "Release action was not called");
> >  }
> >  
> > +/*
> > + * The test verifies that the release action is called immediately when
> > + * drmm_release_action is called and that it is not called for a second 
> > time
> > + * when the device is released.
> > + */
> 
> Thanks, it's much clearer now.
> 
> > +static void drm_test_managed_release_action(struct kunit *test)
> > +{
> > +   struct managed_test_priv *priv = test->priv;
> > +   int ret;
> > +
> > +   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_EQ(test, ret, 0);
> > +
> > +   ret = drm_dev_register(priv->drm, 0);
> > +   KUNIT_ASSERT_EQ(test, ret, 0);
> > +
> > +   drmm_release_action(priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
> > called");
> > +   priv->action_done = false;
> 
> We should call wait_event_* here.
> 
> > +
> > +   drm_dev_unregister(priv->drm);
> > +   drm_kunit_helper_free_device(test, priv->drm->dev);
> > +
> > +   ret = wait_event_interruptible_timeout(priv->action_wq, 
> > priv->action_done,
> > +  
> > msecs_to_jiffies(TEST_TIMEOUT_MS));
> > +   KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Unexpected release action call 
> > during cleanup");
> > +}
> > +
> 
> Tests should in general be as fast as possible. Waiting for 100ms for
> the success case is not ok. We have ~500 tests at the moment, if every
> test was doing that it would take at least 50s to run all our unit
> tests, while it takes less than a second at the moment on a capable
> machine.
> 
> And also, I'm not sure we actually need to make sure it never happened.
> If only because nothing actually guarantees it wouldn't have happened
> after the timeout anyway, so the test isn't definitive.

There's no difference in that regard (test being definitive) between
drm_test_managed_release_action and pre-existing
drm_test_managed_run_action.

If the action is executed after the timeout, with run_action we're going
to get a false-negative result, and with release_action we're going to
get a false-positive result.

> I guess what we could test is whether the action is still in the actions
> list through a function only exported to tests. If it's no longer in the
> action list, then it won't be run.

That would require digging into implementation details rather than
focusing on the interface, which, in my opinion, is not a very good
approach.

In the next revision I'll drop the waitqueue completely. If that won't
work, I also have a variant that uses bus notifier to make both tests
more definitive.

Thanks,
-Michał

> But unless we ever have a bug, I'm not sure it's worth testing for that.
> 
> Maxime




[PATCH v3 3/3] drm/tests: managed: Add a simple test for drmm_managed_release

2023-12-11 Thread Michał Winiarski
Add a simple test that checks whether the action is indeed called right
away and that it is not called on the final drm_dev_put().

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 29 
 1 file changed, 29 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 15bd2474440b5..ef5e784afbc6d 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -48,6 +48,34 @@ static void drm_test_managed_run_action(struct kunit *test)
KUNIT_EXPECT_GT_MSG(test, ret, 0, "Release action was not called");
 }
 
+/*
+ * The test verifies that the release action is called immediately when
+ * drmm_release_action is called and that it is not called for a second time
+ * when the device is released.
+ */
+static void drm_test_managed_release_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+
+   ret = drm_dev_register(priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drmm_release_action(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_TRUE_MSG(test, priv->action_done, "Release action was not 
called");
+   priv->action_done = false;
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm->dev);
+
+   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
+  
msecs_to_jiffies(TEST_TIMEOUT_MS));
+   KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Unexpected release action call 
during cleanup");
+}
+
 static int drm_managed_test_init(struct kunit *test)
 {
struct managed_test_priv *priv;
@@ -76,6 +104,7 @@ static int drm_managed_test_init(struct kunit *test)
 }
 
 static struct kunit_case drm_managed_tests[] = {
+   KUNIT_CASE(drm_test_managed_release_action),
KUNIT_CASE(drm_test_managed_run_action),
{}
 };
-- 
2.43.0



[PATCH v3 2/3] drm/tests: managed: Extract device initialization into test init

2023-12-11 Thread Michał Winiarski
It simplifies the process of extending the test suite with additional
test cases without unnecessary duplication.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 51 +---
 1 file changed, 36 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 1652dca11d30c..15bd2474440b5 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -12,6 +12,7 @@
 #define TEST_TIMEOUT_MS100
 
 struct managed_test_priv {
+   struct drm_device *drm;
bool action_done;
wait_queue_head_t action_wq;
 };
@@ -24,35 +25,54 @@ static void drm_action(struct drm_device *drm, void *ptr)
wake_up_interruptible(&priv->action_wq);
 }
 
+/*
+ * The test verifies that the release action is called automatically when the
+ * device is released.
+ */
 static void drm_test_managed_run_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+
+   ret = drm_dev_register(priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm->dev);
+
+   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
+  
msecs_to_jiffies(TEST_TIMEOUT_MS));
+   KUNIT_EXPECT_GT_MSG(test, ret, 0, "Release action was not called");
+}
+
+static int drm_managed_test_init(struct kunit *test)
 {
struct managed_test_priv *priv;
-   struct drm_device *drm;
struct device *dev;
-   int ret;
 
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
-   init_waitqueue_head(&priv->action_wq);
 
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
 
-   drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
DRIVER_MODESET);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+   /*
+* DRM device can't be embedded in priv, since priv->action_done needs
+* to remain allocated beyond both parent device and drm_device
+* lifetime.
+*/
+   priv->drm = __drm_kunit_helper_alloc_drm_device(test, dev, 
sizeof(*priv->drm), 0,
+   DRIVER_MODESET);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
 
-   ret = drmm_add_action_or_reset(drm, drm_action, priv);
-   KUNIT_EXPECT_EQ(test, ret, 0);
-
-   ret = drm_dev_register(drm, 0);
-   KUNIT_ASSERT_EQ(test, ret, 0);
+   init_waitqueue_head(&priv->action_wq);
 
-   drm_dev_unregister(drm);
-   drm_kunit_helper_free_device(test, dev);
+   test->priv = priv;
 
-   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
-  
msecs_to_jiffies(TEST_TIMEOUT_MS));
-   KUNIT_EXPECT_GT(test, ret, 0);
+   return 0;
 }
 
 static struct kunit_case drm_managed_tests[] = {
@@ -62,6 +82,7 @@ static struct kunit_case drm_managed_tests[] = {
 
 static struct kunit_suite drm_managed_test_suite = {
.name = "drm-test-managed",
+   .init = drm_managed_test_init,
.test_cases = drm_managed_tests
 };
 
-- 
2.43.0



[PATCH v3 1/3] drm/managed: Add drmm_release_action

2023-12-11 Thread Michał Winiarski
Similar to devres equivalent, it allows to call the "release" action
directly and remove the resource from the managed resources list.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_managed.c | 39 +++
 include/drm/drm_managed.h |  4 
 2 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c
index bcd111404b128..7646f67bda4e4 100644
--- a/drivers/gpu/drm/drm_managed.c
+++ b/drivers/gpu/drm/drm_managed.c
@@ -176,6 +176,45 @@ int __drmm_add_action_or_reset(struct drm_device *dev,
 }
 EXPORT_SYMBOL(__drmm_add_action_or_reset);
 
+/**
+ * drmm_release_action - release a managed action from a &drm_device
+ * @dev: DRM device
+ * @action: function which would be called when @dev is released
+ * @data: opaque pointer, passed to @action
+ *
+ * This function calls the @action previously added by drmm_add_action()
+ * immediately.
+ * The @action is removed from the list of cleanup actions for @dev,
+ * which means that it won't be called in the final drm_dev_put().
+ */
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data)
+{
+   struct drmres *dr_match = NULL, *dr;
+   unsigned long flags;
+
+   spin_lock_irqsave(&dev->managed.lock, flags);
+   list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) {
+   if (dr->node.release == action) {
+   if (!data || (data && *(void **)dr->data == data)) {
+   dr_match = dr;
+   del_dr(dev, dr_match);
+   break;
+   }
+   }
+   }
+   spin_unlock_irqrestore(&dev->managed.lock, flags);
+
+   if (WARN_ON(!dr_match))
+   return;
+
+   action(dev, data);
+
+   free_dr(dr_match);
+}
+EXPORT_SYMBOL(drmm_release_action);
+
 /**
  * drmm_kmalloc - &drm_device managed kmalloc()
  * @dev: DRM device
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h
index ad08f834af408..f547b09ca0239 100644
--- a/include/drm/drm_managed.h
+++ b/include/drm/drm_managed.h
@@ -45,6 +45,10 @@ int __must_check __drmm_add_action_or_reset(struct 
drm_device *dev,
drmres_release_t action,
void *data, const char *name);
 
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data);
+
 void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc;
 
 /**
-- 
2.43.0



[PATCH v3 0/3] drm/managed: Add drmm_release_action

2023-12-11 Thread Michał Winiarski
Upcoming Intel Xe driver will need to have a more fine-grained control
over DRM managed actions - namely, the ability to release a given
action, triggering it manually at a different point in time than the
final drm_dev_put().
This series adds a drmm_release_action function (which is similar to
devres devm_release_action) and a simple test that uses it.

v1 -> v2:
- Split the test changes (Maxime)
- Simplify priv lifetime management (Maxime)

v2 -> v3:
- Order tests alphabetically (Maxime)
- Add comments explaining the intention behind the tests and the reason
  why DRM device can't be embedded inside test priv (Maxime)
- Bring back priv lifetime management from v1 to avoid use-after-free

Michał Winiarski (3):
  drm/managed: Add drmm_release_action
  drm/tests: managed: Extract device initialization into test init
  drm/tests: managed: Add a simple test for drmm_managed_release

 drivers/gpu/drm/drm_managed.c| 39 
 drivers/gpu/drm/tests/drm_managed_test.c | 80 +++-
 include/drm/drm_managed.h|  4 ++
 3 files changed, 108 insertions(+), 15 deletions(-)

-- 
2.43.0



Re: [PATCH v2 3/3] drm/tests: managed: Add a simple test for drmm_managed_release

2023-12-05 Thread Michał Winiarski
On Tue, Dec 05, 2023 at 02:50:22PM +0100, Maxime Ripard wrote:
> Hi,
> 
> Thanks for the rework
> 
> On Tue, Dec 05, 2023 at 02:22:10AM +0100, Michał Winiarski wrote:
> > Add a simple test that checks whether the action is indeed called right
> > away and that it is not called on the final drm_dev_put().
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  drivers/gpu/drm/tests/drm_managed_test.c | 24 
> >  1 file changed, 24 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
> > b/drivers/gpu/drm/tests/drm_managed_test.c
> > index cabe6360aef71..8dfbea21c35c5 100644
> > --- a/drivers/gpu/drm/tests/drm_managed_test.c
> > +++ b/drivers/gpu/drm/tests/drm_managed_test.c
> > @@ -44,6 +44,29 @@ static void drm_test_managed_run_action(struct kunit 
> > *test)
> > KUNIT_EXPECT_GT(test, ret, 0);
> >  }
> >  
> 
> We should have a description of the intent of the test here.

The test checks that the release action is called immediately after
calling drmm_release_action, and that it is successfully removed from
the list of resources managed by DRMM (by verifying that release action
is not called upon device cleanup).
Would it be enough to expand the messages in KUNIT_EXPECT?

> 
> > +static void drm_test_managed_release_action(struct kunit *test)
> > +{
> > +   struct managed_test_priv *priv = test->priv;
> > +   int ret;
> > +
> > +   ret = drmm_add_action_or_reset(&priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_EQ(test, ret, 0);
> > +
> > +   ret = drm_dev_register(&priv->drm, 0);
> > +   KUNIT_ASSERT_EQ(test, ret, 0);
> > +
> > +   drmm_release_action(&priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_TRUE(test, priv->action_done);

KUNIT_EXPECT_TRUE_MSG(test, priv->action, "Release action was not called");

> > +   priv->action_done = false;
> > +
> > +   drm_dev_unregister(&priv->drm);
> > +   drm_kunit_helper_free_device(test, priv->drm.dev);
> > +
> > +   ret = wait_event_interruptible_timeout(priv->action_wq, 
> > priv->action_done,
> > +  
> > msecs_to_jiffies(TEST_TIMEOUT_MS));
> > +   KUNIT_EXPECT_EQ(test, ret, 0);

KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Unexpected release action call during 
cleanup");

Or a comment on top? Kerneldoc? Not sure what the recommendation is, as
most (all?) tests in DRM don't have a description.

> 
> This tests that we have reached the timeout, thus the action never ran.

That's the intent, yes.

> It's not clear to me what your intent is here.
> 
> This test is:
> 
>   - Registering an action
>   - Registering the DRM device
>   - Calling drmm_release_action on the previously registered action
>   - Checking that the action has been run
>   - Clearing the flag saying the action has been run
>   - Unregistering the DRM device
>   - Freeing the DRM device
>   - Waiting for the action to run, but expecting it to never do?
> 
> I guess something like
> 
> static void drm_test_managed_release_action(struct kunit *test)
> {
>   struct managed_test_priv *priv = test->priv;
>   int ret;
> 
>   ret = drmm_add_action_or_reset(&priv->drm, drm_action, priv);
>   KUNIT_ASSERT_EQ(test, ret, 0);
> 
>   KUNIT_ASSERT_FALSE(test, priv->action_done);
> 
>   drmm_release_action(&priv->drm, drm_action, priv);
>   ret = wait_event_interruptible_timeout(priv->action_wq, 
> priv->action_done,
>  
> msecs_to_jiffies(TEST_TIMEOUT_MS));
>   KUNIT_EXPECT_GT(test, ret, 0);
>   KUNIT_EXPECT_TRUE(test, priv->action_done);
> }
> 
> would be enough?

It would only check that the action is called immediately, but not that
it was removed from the managed resources list. And we don't need to
wait for that - it should be called immediately.

We do need to wait when we expect it to be called (or not to be called -
in which case we expect a timeout) as part of device cleanup, otherwise
we would get a false positive when delayed release
(CONFIG_DEBUG_KOBJECT_RELEASE) is used.
I assumed that this was the intention behind introducing waitqueue in
drm_test_managed_run_action, is my understanding correct?

And BTW, now that I'm thinking about resource lifetimes. We can't really
tie priv lifetime with the device. It introduces use-after-free in both
tests, when checking if the action was called after
drm_kunit_helper_free_device() has a chance to invoke device cleanup.
Priv should be gone at that point, so I think we should go back to test
in

[PATCH v2 3/3] drm/tests: managed: Add a simple test for drmm_managed_release

2023-12-04 Thread Michał Winiarski
Add a simple test that checks whether the action is indeed called right
away and that it is not called on the final drm_dev_put().

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 24 
 1 file changed, 24 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index cabe6360aef71..8dfbea21c35c5 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -44,6 +44,29 @@ static void drm_test_managed_run_action(struct kunit *test)
KUNIT_EXPECT_GT(test, ret, 0);
 }
 
+static void drm_test_managed_release_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(&priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+
+   ret = drm_dev_register(&priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drmm_release_action(&priv->drm, drm_action, priv);
+   KUNIT_EXPECT_TRUE(test, priv->action_done);
+   priv->action_done = false;
+
+   drm_dev_unregister(&priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm.dev);
+
+   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
+  
msecs_to_jiffies(TEST_TIMEOUT_MS));
+   KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
 static int drm_managed_test_init(struct kunit *test)
 {
struct managed_test_priv *priv;
@@ -65,6 +88,7 @@ static int drm_managed_test_init(struct kunit *test)
 
 static struct kunit_case drm_managed_tests[] = {
KUNIT_CASE(drm_test_managed_run_action),
+   KUNIT_CASE(drm_test_managed_release_action),
{}
 };
 
-- 
2.43.0



[PATCH v2 2/3] drm/tests: managed: Extract device initialization into test init

2023-12-04 Thread Michał Winiarski
It simplifies the process of extending the test suite with additional
test cases without unnecessary duplication.
Additionally, store drm_device inside priv to simplify the lifetime
management by tying priv lifetime with parent struct device.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 43 ++--
 1 file changed, 26 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 1652dca11d30c..cabe6360aef71 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -12,6 +12,7 @@
 #define TEST_TIMEOUT_MS100
 
 struct managed_test_priv {
+   struct drm_device drm;
bool action_done;
wait_queue_head_t action_wq;
 };
@@ -26,35 +27,42 @@ static void drm_action(struct drm_device *drm, void *ptr)
 
 static void drm_test_managed_run_action(struct kunit *test)
 {
-   struct managed_test_priv *priv;
-   struct drm_device *drm;
-   struct device *dev;
+   struct managed_test_priv *priv = test->priv;
int ret;
 
-   priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
-   init_waitqueue_head(&priv->action_wq);
-
-   dev = drm_kunit_helper_alloc_device(test);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
-
-   drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
DRIVER_MODESET);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
-
-   ret = drmm_add_action_or_reset(drm, drm_action, priv);
+   ret = drmm_add_action_or_reset(&priv->drm, drm_action, priv);
KUNIT_EXPECT_EQ(test, ret, 0);
 
-   ret = drm_dev_register(drm, 0);
+   ret = drm_dev_register(&priv->drm, 0);
KUNIT_ASSERT_EQ(test, ret, 0);
 
-   drm_dev_unregister(drm);
-   drm_kunit_helper_free_device(test, dev);
+   drm_dev_unregister(&priv->drm);
+   drm_kunit_helper_free_device(test, priv->drm.dev);
 
ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
   
msecs_to_jiffies(TEST_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
 }
 
+static int drm_managed_test_init(struct kunit *test)
+{
+   struct managed_test_priv *priv;
+   struct device *dev;
+
+   dev = drm_kunit_helper_alloc_device(test);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+   priv = drm_kunit_helper_alloc_drm_device(test, dev, struct 
managed_test_priv, drm,
+DRIVER_MODESET);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+   init_waitqueue_head(&priv->action_wq);
+
+   test->priv = priv;
+
+   return 0;
+}
+
 static struct kunit_case drm_managed_tests[] = {
KUNIT_CASE(drm_test_managed_run_action),
{}
@@ -62,6 +70,7 @@ static struct kunit_case drm_managed_tests[] = {
 
 static struct kunit_suite drm_managed_test_suite = {
.name = "drm-test-managed",
+   .init = drm_managed_test_init,
.test_cases = drm_managed_tests
 };
 
-- 
2.43.0



[PATCH v2 1/3] drm/managed: Add drmm_release_action

2023-12-04 Thread Michał Winiarski
Similar to devres equivalent, it allows to call the "release" action
directly and remove the resource from the managed resources list.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_managed.c | 39 +++
 include/drm/drm_managed.h |  4 
 2 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c
index bcd111404b128..7646f67bda4e4 100644
--- a/drivers/gpu/drm/drm_managed.c
+++ b/drivers/gpu/drm/drm_managed.c
@@ -176,6 +176,45 @@ int __drmm_add_action_or_reset(struct drm_device *dev,
 }
 EXPORT_SYMBOL(__drmm_add_action_or_reset);
 
+/**
+ * drmm_release_action - release a managed action from a &drm_device
+ * @dev: DRM device
+ * @action: function which would be called when @dev is released
+ * @data: opaque pointer, passed to @action
+ *
+ * This function calls the @action previously added by drmm_add_action()
+ * immediately.
+ * The @action is removed from the list of cleanup actions for @dev,
+ * which means that it won't be called in the final drm_dev_put().
+ */
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data)
+{
+   struct drmres *dr_match = NULL, *dr;
+   unsigned long flags;
+
+   spin_lock_irqsave(&dev->managed.lock, flags);
+   list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) {
+   if (dr->node.release == action) {
+   if (!data || (data && *(void **)dr->data == data)) {
+   dr_match = dr;
+   del_dr(dev, dr_match);
+   break;
+   }
+   }
+   }
+   spin_unlock_irqrestore(&dev->managed.lock, flags);
+
+   if (WARN_ON(!dr_match))
+   return;
+
+   action(dev, data);
+
+   free_dr(dr_match);
+}
+EXPORT_SYMBOL(drmm_release_action);
+
 /**
  * drmm_kmalloc - &drm_device managed kmalloc()
  * @dev: DRM device
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h
index ad08f834af408..f547b09ca0239 100644
--- a/include/drm/drm_managed.h
+++ b/include/drm/drm_managed.h
@@ -45,6 +45,10 @@ int __must_check __drmm_add_action_or_reset(struct 
drm_device *dev,
drmres_release_t action,
void *data, const char *name);
 
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data);
+
 void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc;
 
 /**
-- 
2.43.0



[PATCH v2 0/3] drm/managed: Add drmm_release_action

2023-12-04 Thread Michał Winiarski
Upcoming Intel Xe driver will need to have a more fine-grained control
over DRM managed actions - namely, the ability to release a given
action, triggering it manually at a different point in time than the
final drm_dev_put().
This series adds a drmm_release_action function (which is similar to
devres devm_release_action) and a simple test that uses it.

v1 -> v2:
- Split the test changes (Maxime)
- Simplify priv lifetime management (Maxime)

Michał Winiarski (3):
  drm/managed: Add drmm_release_action
  drm/tests: managed: Extract device initialization into test init
  drm/tests: managed: Add a simple test for drmm_managed_release

 drivers/gpu/drm/drm_managed.c| 39 +++
 drivers/gpu/drm/tests/drm_managed_test.c | 63 ++--
 include/drm/drm_managed.h|  4 ++
 3 files changed, 91 insertions(+), 15 deletions(-)

-- 
2.43.0



Re: [PATCH 2/2] drm/tests: managed: Add a simple test for drmm_managed_release

2023-12-04 Thread Michał Winiarski
On Thu, Nov 30, 2023 at 09:38:35AM +0100, Maxime Ripard wrote:
> Hi,
> 
> Thanks for creating a test for that, that's really appreciated :)
> 
> On Wed, Nov 29, 2023 at 11:14:12PM +0100, Michał Winiarski wrote:
> > Add a simple test that checks whether the action is indeed called right
> > away and that it is not called on the final drm_dev_put().
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  drivers/gpu/drm/tests/drm_managed_test.c | 65 ++--
> >  1 file changed, 50 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
> > b/drivers/gpu/drm/tests/drm_managed_test.c
> > index 1652dca11d30c..a645ea42aee56 100644
> > --- a/drivers/gpu/drm/tests/drm_managed_test.c
> > +++ b/drivers/gpu/drm/tests/drm_managed_test.c
> > @@ -12,6 +12,8 @@
> >  #define TEST_TIMEOUT_MS100
> >  
> >  struct managed_test_priv {
> > +   struct drm_device *drm;
> > +   struct device *dev;
> > bool action_done;
> > wait_queue_head_t action_wq;
> >  };
> > @@ -26,42 +28,75 @@ static void drm_action(struct drm_device *drm, void 
> > *ptr)
> >  
> >  static void drm_test_managed_run_action(struct kunit *test)
> >  {
> > -   struct managed_test_priv *priv;
> > -   struct drm_device *drm;
> > -   struct device *dev;
> > +   struct managed_test_priv *priv = test->priv;
> > int ret;
> >  
> > -   priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
> > -   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
> > -   init_waitqueue_head(&priv->action_wq);
> > +   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_EQ(test, ret, 0);
> >  
> > -   dev = drm_kunit_helper_alloc_device(test);
> > -   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
> > +   ret = drm_dev_register(priv->drm, 0);
> > +   KUNIT_ASSERT_EQ(test, ret, 0);
> > +
> > +   drm_dev_unregister(priv->drm);
> > +   drm_kunit_helper_free_device(test, priv->dev);
> 
> I think we'll need two patches here, one to convert to having an init
> function, and one to actually add the new test, it's pretty confusing as
> it is.
> 
> >  
> > -   drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
> > DRIVER_MODESET);
> > -   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
> > +   ret = wait_event_interruptible_timeout(priv->action_wq, 
> > priv->action_done,
> > +  
> > msecs_to_jiffies(TEST_TIMEOUT_MS));
> > +   KUNIT_EXPECT_GT(test, ret, 0);
> > +}
> >  
> > -   ret = drmm_add_action_or_reset(drm, drm_action, priv);
> > +static void drm_test_managed_release_action(struct kunit *test)
> > +{
> > +   struct managed_test_priv *priv = test->priv;
> > +   int ret;
> > +
> > +   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
> > KUNIT_EXPECT_EQ(test, ret, 0);
> >  
> > -   ret = drm_dev_register(drm, 0);
> > +   ret = drm_dev_register(priv->drm, 0);
> > KUNIT_ASSERT_EQ(test, ret, 0);
> >  
> > -   drm_dev_unregister(drm);
> > -   drm_kunit_helper_free_device(test, dev);
> > +   drmm_release_action(priv->drm, drm_action, priv);
> > +   KUNIT_EXPECT_TRUE(test, priv->action_done);
> > +   priv->action_done = false;
> > +
> > +   drm_dev_unregister(priv->drm);
> > +   drm_kunit_helper_free_device(test, priv->dev);
> >  
> > ret = wait_event_interruptible_timeout(priv->action_wq, 
> > priv->action_done,
> >
> > msecs_to_jiffies(TEST_TIMEOUT_MS));
> > -   KUNIT_EXPECT_GT(test, ret, 0);
> > +   KUNIT_EXPECT_EQ(test, ret, 0);
> > +}
> > +
> > +static int drm_managed_test_init(struct kunit *test)
> > +{
> > +   struct managed_test_priv *priv;
> > +
> > +   priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
> > +   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
> > +   init_waitqueue_head(&priv->action_wq);
> 
> Also, I know that it was there before, but I'm not sure it's valid from
> a lifetime point of view. Or at least, we have to think hard enough
> about it to just remove that construct
> 
> > +   priv->dev = drm_kunit_helper_alloc_device(test);
> > +   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
> > +
> > +   priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, 
> > sizeof(*priv->drm),
> > +   0, DRIVER_MODESET);
> > +   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
> 
> For example by storing the drm_device struct in the priv structure
> directly, and thus everything will just work out.

Sure, makes sense, I'll include it in the patch that moves device alloc
to .init().

Thanks,
-Michał

> 
> Maxime




[PATCH 1/2] drm/managed: Add drmm_release_action

2023-11-29 Thread Michał Winiarski
Similar to devres equivalent, it allows to call the "release" action
directly and remove the resource from the managed resources list.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_managed.c | 39 +++
 include/drm/drm_managed.h |  4 
 2 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c
index bcd111404b128..7646f67bda4e4 100644
--- a/drivers/gpu/drm/drm_managed.c
+++ b/drivers/gpu/drm/drm_managed.c
@@ -176,6 +176,45 @@ int __drmm_add_action_or_reset(struct drm_device *dev,
 }
 EXPORT_SYMBOL(__drmm_add_action_or_reset);
 
+/**
+ * drmm_release_action - release a managed action from a &drm_device
+ * @dev: DRM device
+ * @action: function which would be called when @dev is released
+ * @data: opaque pointer, passed to @action
+ *
+ * This function calls the @action previously added by drmm_add_action()
+ * immediately.
+ * The @action is removed from the list of cleanup actions for @dev,
+ * which means that it won't be called in the final drm_dev_put().
+ */
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data)
+{
+   struct drmres *dr_match = NULL, *dr;
+   unsigned long flags;
+
+   spin_lock_irqsave(&dev->managed.lock, flags);
+   list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) {
+   if (dr->node.release == action) {
+   if (!data || (data && *(void **)dr->data == data)) {
+   dr_match = dr;
+   del_dr(dev, dr_match);
+   break;
+   }
+   }
+   }
+   spin_unlock_irqrestore(&dev->managed.lock, flags);
+
+   if (WARN_ON(!dr_match))
+   return;
+
+   action(dev, data);
+
+   free_dr(dr_match);
+}
+EXPORT_SYMBOL(drmm_release_action);
+
 /**
  * drmm_kmalloc - &drm_device managed kmalloc()
  * @dev: DRM device
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h
index ad08f834af408..f547b09ca0239 100644
--- a/include/drm/drm_managed.h
+++ b/include/drm/drm_managed.h
@@ -45,6 +45,10 @@ int __must_check __drmm_add_action_or_reset(struct 
drm_device *dev,
drmres_release_t action,
void *data, const char *name);
 
+void drmm_release_action(struct drm_device *dev,
+drmres_release_t action,
+void *data);
+
 void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc;
 
 /**
-- 
2.43.0



[PATCH 2/2] drm/tests: managed: Add a simple test for drmm_managed_release

2023-11-29 Thread Michał Winiarski
Add a simple test that checks whether the action is indeed called right
away and that it is not called on the final drm_dev_put().

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_managed_test.c | 65 ++--
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_managed_test.c 
b/drivers/gpu/drm/tests/drm_managed_test.c
index 1652dca11d30c..a645ea42aee56 100644
--- a/drivers/gpu/drm/tests/drm_managed_test.c
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -12,6 +12,8 @@
 #define TEST_TIMEOUT_MS100
 
 struct managed_test_priv {
+   struct drm_device *drm;
+   struct device *dev;
bool action_done;
wait_queue_head_t action_wq;
 };
@@ -26,42 +28,75 @@ static void drm_action(struct drm_device *drm, void *ptr)
 
 static void drm_test_managed_run_action(struct kunit *test)
 {
-   struct managed_test_priv *priv;
-   struct drm_device *drm;
-   struct device *dev;
+   struct managed_test_priv *priv = test->priv;
int ret;
 
-   priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
-   init_waitqueue_head(&priv->action_wq);
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_EQ(test, ret, 0);
 
-   dev = drm_kunit_helper_alloc_device(test);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+   ret = drm_dev_register(priv->drm, 0);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->dev);
 
-   drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, 
DRIVER_MODESET);
-   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+   ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
+  
msecs_to_jiffies(TEST_TIMEOUT_MS));
+   KUNIT_EXPECT_GT(test, ret, 0);
+}
 
-   ret = drmm_add_action_or_reset(drm, drm_action, priv);
+static void drm_test_managed_release_action(struct kunit *test)
+{
+   struct managed_test_priv *priv = test->priv;
+   int ret;
+
+   ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
KUNIT_EXPECT_EQ(test, ret, 0);
 
-   ret = drm_dev_register(drm, 0);
+   ret = drm_dev_register(priv->drm, 0);
KUNIT_ASSERT_EQ(test, ret, 0);
 
-   drm_dev_unregister(drm);
-   drm_kunit_helper_free_device(test, dev);
+   drmm_release_action(priv->drm, drm_action, priv);
+   KUNIT_EXPECT_TRUE(test, priv->action_done);
+   priv->action_done = false;
+
+   drm_dev_unregister(priv->drm);
+   drm_kunit_helper_free_device(test, priv->dev);
 
ret = wait_event_interruptible_timeout(priv->action_wq, 
priv->action_done,
   
msecs_to_jiffies(TEST_TIMEOUT_MS));
-   KUNIT_EXPECT_GT(test, ret, 0);
+   KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static int drm_managed_test_init(struct kunit *test)
+{
+   struct managed_test_priv *priv;
+
+   priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+   init_waitqueue_head(&priv->action_wq);
+
+   priv->dev = drm_kunit_helper_alloc_device(test);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
+
+   priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, 
sizeof(*priv->drm),
+   0, DRIVER_MODESET);
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
+
+   test->priv = priv;
+
+   return 0;
 }
 
 static struct kunit_case drm_managed_tests[] = {
KUNIT_CASE(drm_test_managed_run_action),
+   KUNIT_CASE(drm_test_managed_release_action),
{}
 };
 
 static struct kunit_suite drm_managed_test_suite = {
.name = "drm-test-managed",
+   .init = drm_managed_test_init,
.test_cases = drm_managed_tests
 };
 
-- 
2.43.0



[PATCH 0/2] drm/managed: Add drmm_release_action

2023-11-29 Thread Michał Winiarski
Upcoming Intel Xe driver will need to have a more fine-grained control
over DRM managed actions - namely, the ability to release a given
action, triggering it manually at a different point in time than the
final drm_dev_put().
This series adds a drmm_release_action function (which is similar to
devres devm_release_action) and a simple test that uses it.

Michał Winiarski (2):
  drm/managed: Add drmm_release_action
  drm/tests: managed: Add a simple test for drmm_managed_release

 drivers/gpu/drm/drm_managed.c| 39 ++
 drivers/gpu/drm/tests/drm_managed_test.c | 65 ++--
 include/drm/drm_managed.h|  4 ++
 3 files changed, 93 insertions(+), 15 deletions(-)

-- 
2.43.0



[PATCH] iosys-map: Rename locals used inside macros

2023-10-24 Thread Michał Winiarski
Widely used variable names can be used by macro users, potentially
leading to name collisions.
Suffix locals used inside the macros with an underscore, to reduce the
collision potential.

Suggested-by: Lucas De Marchi 
Signed-off-by: Michał Winiarski 
---
 include/linux/iosys-map.h | 44 +++
 1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h
index cb71aa616bd37..bb3a8c30fb920 100644
--- a/include/linux/iosys-map.h
+++ b/include/linux/iosys-map.h
@@ -168,9 +168,9 @@ struct iosys_map {
  * about the use of uninitialized variable.
  */
 #define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({
\
-   struct iosys_map copy = *map_;  \
-   iosys_map_incr(©, offset_); \
-   copy;   \
+   struct iosys_map copy_ = *map_; \
+   iosys_map_incr(©_, offset_);\
+   copy_;  \
 })
 
 /**
@@ -391,14 +391,14 @@ static inline void iosys_map_memset(struct iosys_map 
*dst, size_t offset,
  * Returns:
  * The value read from the mapping.
  */
-#define iosys_map_rd(map__, offset__, type__) ({   
\
-   type__ val; 
\
-   if ((map__)->is_iomem) {
\
-   __iosys_map_rd_io(val, (map__)->vaddr_iomem + (offset__), 
type__);\
-   } else {
\
-   __iosys_map_rd_sys(val, (map__)->vaddr + (offset__), type__);   
\
-   }   
\
-   val;
\
+#define iosys_map_rd(map__, offset__, type__) ({   
\
+   type__ val_;
\
+   if ((map__)->is_iomem) {
\
+   __iosys_map_rd_io(val_, (map__)->vaddr_iomem + (offset__), 
type__); \
+   } else {
\
+   __iosys_map_rd_sys(val_, (map__)->vaddr + (offset__), type__);  
\
+   }   
\
+   val_;   
\
 })
 
 /**
@@ -413,13 +413,13 @@ static inline void iosys_map_memset(struct iosys_map 
*dst, size_t offset,
  * or if pointer may be unaligned (and problematic for the architecture
  * supported), use iosys_map_memcpy_to()
  */
-#define iosys_map_wr(map__, offset__, type__, val__) ({
\
-   type__ val = (val__);   
\
-   if ((map__)->is_iomem) {
\
-   __iosys_map_wr_io(val, (map__)->vaddr_iomem + (offset__), 
type__);\
-   } else {
\
-   __iosys_map_wr_sys(val, (map__)->vaddr + (offset__), type__);   
\
-   }   
\
+#define iosys_map_wr(map__, offset__, type__, val__) ({
\
+   type__ val_ = (val__);  
\
+   if ((map__)->is_iomem) {
\
+   __iosys_map_wr_io(val_, (map__)->vaddr_iomem + (offset__), 
type__); \
+   } else {
\
+   __iosys_map_wr_sys(val_, (map__)->vaddr + (offset__), type__);  
\
+   }   
\
 })
 
 /**
@@ -485,9 +485,9 @@ static inline void iosys_map_memset(struct iosys_map *dst, 
size_t offset,
  * The value read from the mapping.
  */
 #define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({  
\
-   struct_type__ *s;   
\
+   struct_type__ *s_;  
\
iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__), 
\
-typeof(s->field__));   
\
+typeof(s_->field__));  
\
 })
 
 /**
@@ -508,9 +508,9 @@ static inline void iosys_map_memset(struct iosys_map *dst, 
size_t offset,
  * usage and memor

Re: [PATCH v6 1/4] drm: Use XArray instead of IDR for minors

2023-08-28 Thread Michał Winiarski
On Fri, Aug 25, 2023 at 12:59:26PM -0400, James Zhu wrote:
> 
> On 2023-07-24 17:14, Michał Winiarski wrote:
> > IDR is deprecated, and since XArray manages its own state with internal
> > locking, it simplifies the locking on DRM side.
> > Additionally, don't use the IRQ-safe variant, since operating on drm
> > minor is not done in IRQ context.
> > 
> > Signed-off-by: Michał Winiarski
> > Suggested-by: Matthew Wilcox
> > ---
> >   drivers/gpu/drm/drm_drv.c | 63 ---
> >   1 file changed, 25 insertions(+), 38 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > index 3eda026ffac6..3faecb01186f 100644
> > --- a/drivers/gpu/drm/drm_drv.c
> > +++ b/drivers/gpu/drm/drm_drv.c
> > @@ -34,6 +34,7 @@
> >   #include 
> >   #include 
> >   #include 
> > +#include 
> >   #include 
> >   #include 
> > @@ -54,8 +55,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, 
> > Jon Smirl");
> >   MODULE_DESCRIPTION("DRM shared core routines");
> >   MODULE_LICENSE("GPL and additional rights");
> > -static DEFINE_SPINLOCK(drm_minor_lock);
> > -static struct idr drm_minors_idr;
> > +static DEFINE_XARRAY_ALLOC(drm_minors_xa);
> >   /*
> >* If the drm core fails to init for whatever reason,
> > @@ -101,26 +101,23 @@ static struct drm_minor **drm_minor_get_slot(struct 
> > drm_device *dev,
> >   static void drm_minor_alloc_release(struct drm_device *dev, void *data)
> >   {
> > struct drm_minor *minor = data;
> > -   unsigned long flags;
> > WARN_ON(dev != minor->dev);
> > put_device(minor->kdev);
> > -   if (minor->type == DRM_MINOR_ACCEL) {
> > +   if (minor->type == DRM_MINOR_ACCEL)
> > accel_minor_remove(minor->index);
> > -   } else {
> > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > -   idr_remove(&drm_minors_idr, minor->index);
> > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > -   }
> > +   else
> > +   xa_erase(&drm_minors_xa, minor->index);
> >   }
> > +#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * 
> > _t + 63); })
> > +
> >   static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type 
> > type)
> >   {
> > struct drm_minor *minor;
> > -   unsigned long flags;
> > -   int r;
> > +   int index, r;
> > minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
> > if (!minor)
> > @@ -129,24 +126,17 @@ static int drm_minor_alloc(struct drm_device *dev, 
> > enum drm_minor_type type)
> > minor->type = type;
> > minor->dev = dev;
> > -   idr_preload(GFP_KERNEL);
> > if (type == DRM_MINOR_ACCEL) {
> > r = accel_minor_alloc();
> > +   index = r;
> > } else {
> > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > -   r = idr_alloc(&drm_minors_idr,
> > -   NULL,
> > -   64 * type,
> > -   64 * (type + 1),
> > -   GFP_NOWAIT);
> > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > +   r = xa_alloc(&drm_minors_xa, &index, NULL, 
> > DRM_MINOR_LIMIT(type), GFP_KERNEL);
> > }
> > -   idr_preload_end();
> > if (r < 0)
> > return r;
> > -   minor->index = r;
> > +   minor->index = index;
> > r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
> > if (r)
> > @@ -163,7 +153,7 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
> > drm_minor_type type)
> >   static int drm_minor_register(struct drm_device *dev, enum drm_minor_type 
> > type)
> >   {
> > struct drm_minor *minor;
> > -   unsigned long flags;
> > +   void *entry;
> > int ret;
> > DRM_DEBUG("\n");
> > @@ -190,9 +180,12 @@ static int drm_minor_register(struct drm_device *dev, 
> > enum drm_minor_type type)
> > if (minor->type == DRM_MINOR_ACCEL) {
> > accel_minor_replace(minor, minor->index);
> > } else {
> > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > -   idr_replace(&drm_minors_idr, minor, minor->index);
> > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > +   entry = xa_store(&drm_minors_xa, minor->index, minor, 
> > GFP_KE

[PATCH v6 3/4] drm: Expand max DRM device number to full MINORBITS

2023-07-24 Thread Michał Winiarski
Having a limit of 64 DRM devices is not good enough for modern world
where we have multi-GPU servers, SR-IOV virtual functions and virtual
devices used for testing.
Let's utilize full minor range for DRM devices.
To avoid regressing the existing userspace, we're still maintaining the
numbering scheme where 0-63 is used for primary, 64-127 is reserved
(formerly for control) and 128-191 is used for render.
For minors >= 192, we're allocating minors dynamically on a first-come,
first-served basis.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 34b60196c443..c2c6e80e6b31 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -121,10 +121,19 @@ static void drm_minor_alloc_release(struct drm_device 
*dev, void *data)
xa_erase(drm_minor_get_xa(minor->type), minor->index);
 }
 
+/*
+ * DRM used to support 64 devices, for backwards compatibility we need to 
maintain the
+ * minor allocation scheme where minors 0-63 are primary nodes, 64-127 are 
control nodes,
+ * and 128-191 are render nodes.
+ * After reaching the limit, we're allocating minors dynamically - first-come, 
first-serve.
+ * Accel nodes are using a distinct major, so the minors are allocated in 
continuous 0-MAX
+ * range.
+ */
 #define DRM_MINOR_LIMIT(t) ({ \
typeof(t) _t = (t); \
_t == DRM_MINOR_ACCEL ? XA_LIMIT(0, ACCEL_MAX_MINORS) : XA_LIMIT(64 * 
_t, 64 * _t + 63); \
 })
+#define DRM_EXTENDED_MINOR_LIMIT XA_LIMIT(192, (1 << MINORBITS) - 1)
 
 static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
 {
@@ -140,6 +149,9 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
drm_minor_type type)
 
r = xa_alloc(drm_minor_get_xa(type), &minor->index,
 NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL);
+   if (r == -EBUSY && (type == DRM_MINOR_PRIMARY || type == 
DRM_MINOR_RENDER))
+   r = xa_alloc(&drm_minors_xa, &minor->index,
+NULL, DRM_EXTENDED_MINOR_LIMIT, GFP_KERNEL);
if (r < 0)
return r;
 
-- 
2.41.0



[PATCH v6 4/4] drm: Introduce force_extended_minors modparam

2023-07-24 Thread Michał Winiarski
While there is support for >64 DRM devices on kernel side, existing
userspace may still have some hardcoded assumptions and it's possible
that it will require changes to be able to use more than 64 devices.
Add a modparam to simplify testing and development of >64 devices
support on userspace side by allocating minors from the >=192 range
(without the need of having >64 physical devices connected).

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 12 +---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index c2c6e80e6b31..ef6d7b498784 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -57,6 +57,11 @@ MODULE_LICENSE("GPL and additional rights");
 
 DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
+static bool force_extended_minors;
+module_param_unsafe(force_extended_minors, bool, 0400);
+MODULE_PARM_DESC(force_extended_minors,
+"Don't allocate minors in 0-192 range. This can be used for 
testing userspace support for >64 drm devices (default: false)");
+
 /*
  * If the drm core fails to init for whatever reason,
  * we should prevent any drivers from registering with it.
@@ -138,7 +143,7 @@ static void drm_minor_alloc_release(struct drm_device *dev, 
void *data)
 static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
 {
struct drm_minor *minor;
-   int r;
+   int r = -EBUSY;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
if (!minor)
@@ -147,8 +152,9 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
drm_minor_type type)
minor->type = type;
minor->dev = dev;
 
-   r = xa_alloc(drm_minor_get_xa(type), &minor->index,
-NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL);
+   if (type == DRM_MINOR_ACCEL || !force_extended_minors)
+   r = xa_alloc(drm_minor_get_xa(type), &minor->index,
+NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL);
if (r == -EBUSY && (type == DRM_MINOR_PRIMARY || type == 
DRM_MINOR_RENDER))
r = xa_alloc(&drm_minors_xa, &minor->index,
 NULL, DRM_EXTENDED_MINOR_LIMIT, GFP_KERNEL);
-- 
2.41.0



[PATCH v6 2/4] accel: Use XArray instead of IDR for minors

2023-07-24 Thread Michał Winiarski
Accel minor management is based on DRM (and is also using struct
drm_minor internally), since DRM is using XArray for minors, it makes
sense to also convert accel.
As the two implementations are identical (only difference being the
underlying xarray), move the accel_minor_* functionality to DRM.

Signed-off-by: Michał Winiarski 
---
 drivers/accel/drm_accel.c  | 110 +++--
 drivers/gpu/drm/drm_drv.c  |  66 ++--
 drivers/gpu/drm/drm_file.c |   2 +-
 drivers/gpu/drm/drm_internal.h |   4 --
 include/drm/drm_accel.h|  18 +-
 include/drm/drm_file.h |   5 ++
 6 files changed, 47 insertions(+), 158 deletions(-)

diff --git a/drivers/accel/drm_accel.c b/drivers/accel/drm_accel.c
index 4a9baf02439e..8827cb78ca9d 100644
--- a/drivers/accel/drm_accel.c
+++ b/drivers/accel/drm_accel.c
@@ -8,7 +8,7 @@
 
 #include 
 #include 
-#include 
+#include 
 
 #include 
 #include 
@@ -17,8 +17,7 @@
 #include 
 #include 
 
-static DEFINE_SPINLOCK(accel_minor_lock);
-static struct idr accel_minors_idr;
+DEFINE_XARRAY_ALLOC(accel_minors_xa);
 
 static struct dentry *accel_debugfs_root;
 static struct class *accel_class;
@@ -120,99 +119,6 @@ void accel_set_device_instance_params(struct device *kdev, 
int index)
kdev->type = &accel_sysfs_device_minor;
 }
 
-/**
- * accel_minor_alloc() - Allocates a new accel minor
- *
- * This function access the accel minors idr and allocates from it
- * a new id to represent a new accel minor
- *
- * Return: A new id on success or error code in case idr_alloc failed
- */
-int accel_minor_alloc(void)
-{
-   unsigned long flags;
-   int r;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-
-   return r;
-}
-
-/**
- * accel_minor_remove() - Remove an accel minor
- * @index: The minor id to remove.
- *
- * This function access the accel minors idr and removes from
- * it the member with the id that is passed to this function.
- */
-void accel_minor_remove(int index)
-{
-   unsigned long flags;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   idr_remove(&accel_minors_idr, index);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-}
-
-/**
- * accel_minor_replace() - Replace minor pointer in accel minors idr.
- * @minor: Pointer to the new minor.
- * @index: The minor id to replace.
- *
- * This function access the accel minors idr structure and replaces the pointer
- * that is associated with an existing id. Because the minor pointer can be
- * NULL, we need to explicitly pass the index.
- *
- * Return: 0 for success, negative value for error
- */
-void accel_minor_replace(struct drm_minor *minor, int index)
-{
-   unsigned long flags;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   idr_replace(&accel_minors_idr, minor, index);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-}
-
-/*
- * Looks up the given minor-ID and returns the respective DRM-minor object. The
- * refence-count of the underlying device is increased so you must release this
- * object with accel_minor_release().
- *
- * The object can be only a drm_minor that represents an accel device.
- *
- * As long as you hold this minor, it is guaranteed that the object and the
- * minor->dev pointer will stay valid! However, the device may get unplugged 
and
- * unregistered while you hold the minor.
- */
-static struct drm_minor *accel_minor_acquire(unsigned int minor_id)
-{
-   struct drm_minor *minor;
-   unsigned long flags;
-
-   spin_lock_irqsave(&accel_minor_lock, flags);
-   minor = idr_find(&accel_minors_idr, minor_id);
-   if (minor)
-   drm_dev_get(minor->dev);
-   spin_unlock_irqrestore(&accel_minor_lock, flags);
-
-   if (!minor) {
-   return ERR_PTR(-ENODEV);
-   } else if (drm_dev_is_unplugged(minor->dev)) {
-   drm_dev_put(minor->dev);
-   return ERR_PTR(-ENODEV);
-   }
-
-   return minor;
-}
-
-static void accel_minor_release(struct drm_minor *minor)
-{
-   drm_dev_put(minor->dev);
-}
-
 /**
  * accel_open - open method for ACCEL file
  * @inode: device inode
@@ -230,7 +136,7 @@ int accel_open(struct inode *inode, struct file *filp)
struct drm_minor *minor;
int retcode;
 
-   minor = accel_minor_acquire(iminor(inode));
+   minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
 
@@ -249,7 +155,7 @@ int accel_open(struct inode *inode, struct file *filp)
 
 err_undo:
atomic_dec(&dev->open_count);
-   accel_minor_release(minor);
+   drm_minor_release(minor);
return retcode;
 }
 EXPORT_SYMBOL_GPL(accel_open);
@@ -260,7 +166,7 @@ static int accel_s

[PATCH v6 1/4] drm: Use XArray instead of IDR for minors

2023-07-24 Thread Michał Winiarski
IDR is deprecated, and since XArray manages its own state with internal
locking, it simplifies the locking on DRM side.
Additionally, don't use the IRQ-safe variant, since operating on drm
minor is not done in IRQ context.

Signed-off-by: Michał Winiarski 
Suggested-by: Matthew Wilcox 
---
 drivers/gpu/drm/drm_drv.c | 63 ---
 1 file changed, 25 insertions(+), 38 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 3eda026ffac6..3faecb01186f 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -54,8 +55,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon 
Smirl");
 MODULE_DESCRIPTION("DRM shared core routines");
 MODULE_LICENSE("GPL and additional rights");
 
-static DEFINE_SPINLOCK(drm_minor_lock);
-static struct idr drm_minors_idr;
+static DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
 /*
  * If the drm core fails to init for whatever reason,
@@ -101,26 +101,23 @@ static struct drm_minor **drm_minor_get_slot(struct 
drm_device *dev,
 static void drm_minor_alloc_release(struct drm_device *dev, void *data)
 {
struct drm_minor *minor = data;
-   unsigned long flags;
 
WARN_ON(dev != minor->dev);
 
put_device(minor->kdev);
 
-   if (minor->type == DRM_MINOR_ACCEL) {
+   if (minor->type == DRM_MINOR_ACCEL)
accel_minor_remove(minor->index);
-   } else {
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_remove(&drm_minors_idr, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
-   }
+   else
+   xa_erase(&drm_minors_xa, minor->index);
 }
 
+#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 
63); })
+
 static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
 {
struct drm_minor *minor;
-   unsigned long flags;
-   int r;
+   int index, r;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
if (!minor)
@@ -129,24 +126,17 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
drm_minor_type type)
minor->type = type;
minor->dev = dev;
 
-   idr_preload(GFP_KERNEL);
if (type == DRM_MINOR_ACCEL) {
r = accel_minor_alloc();
+   index = r;
} else {
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   r = idr_alloc(&drm_minors_idr,
-   NULL,
-   64 * type,
-   64 * (type + 1),
-   GFP_NOWAIT);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   r = xa_alloc(&drm_minors_xa, &index, NULL, 
DRM_MINOR_LIMIT(type), GFP_KERNEL);
}
-   idr_preload_end();
 
if (r < 0)
return r;
 
-   minor->index = r;
+   minor->index = index;
 
r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
if (r)
@@ -163,7 +153,7 @@ static int drm_minor_alloc(struct drm_device *dev, enum 
drm_minor_type type)
 static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type)
 {
struct drm_minor *minor;
-   unsigned long flags;
+   void *entry;
int ret;
 
DRM_DEBUG("\n");
@@ -190,9 +180,12 @@ static int drm_minor_register(struct drm_device *dev, enum 
drm_minor_type type)
if (minor->type == DRM_MINOR_ACCEL) {
accel_minor_replace(minor, minor->index);
} else {
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, minor, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   entry = xa_store(&drm_minors_xa, minor->index, minor, 
GFP_KERNEL);
+   if (xa_is_err(entry)) {
+   ret = xa_err(entry);
+   goto err_debugfs;
+   }
+   WARN_ON(entry);
}
 
DRM_DEBUG("new minor registered %d\n", minor->index);
@@ -206,20 +199,16 @@ static int drm_minor_register(struct drm_device *dev, 
enum drm_minor_type type)
 static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type 
type)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
minor = *drm_minor_get_slot(dev, type);
if (!minor || !device_is_registered(minor->kdev))
return;
 
/* replace @minor with NULL so lookups will fail from now on */
-   if (minor->type == DRM_MINOR_ACCEL) {
+   if (minor->type == DRM_MINOR_ACCEL)
accel_minor_replace(NULL, minor->index);
-   } else {
-   spin_lock_irqsave(&dr

[PATCH v6 0/4] drm: Use full allocated minor range for DRM

2023-07-24 Thread Michał Winiarski
64 DRM device nodes is not enough for everyone.
Upgrade it to ~512K (which definitely is more than enough).

To allow testing userspace support for >64 devices, add additional DRM
modparam (force_extended_minors) which causes DRM to skip allocating minors
in 0-192 range.
Additionally - convert minors to use XArray instead of IDR to simplify the
locking.

v1 -> v2:
Don't touch DRM_MINOR_CONTROL and its range (Simon Ser)

v2 -> v3:
Don't use legacy scheme for >=192 minor range (Dave Airlie)
Add modparam for testing (Dave Airlie)
Add lockdep annotation for IDR (Daniel Vetter)

v3 -> v4:
Convert from IDR to XArray (Matthew Wilcox)

v4 -> v5:
Fixup IDR to XArray conversion (Matthew Wilcox)

v5 -> v6:
Also convert Accel to XArray
Rename skip_legacy_minors to force_extended_minors

Michał Winiarski (4):
  drm: Use XArray instead of IDR for minors
  accel: Use XArray instead of IDR for minors
  drm: Expand max DRM device number to full MINORBITS
  drm: Introduce force_extended_minors modparam

 drivers/accel/drm_accel.c  | 110 +++--
 drivers/gpu/drm/drm_drv.c  | 105 ---
 drivers/gpu/drm/drm_file.c |   2 +-
 drivers/gpu/drm/drm_internal.h |   4 --
 include/drm/drm_accel.h|  18 +-
 include/drm/drm_file.h |   5 ++
 6 files changed, 69 insertions(+), 175 deletions(-)

-- 
2.41.0



Re: [PATCH RESEND] drm/tests: Suballocator test

2023-03-26 Thread Michał Winiarski
On Thu, Mar 02, 2023 at 09:34:22AM +0100, Thomas Hellström wrote:
> Add a suballocator test to get some test coverage for the new drm
> suballocator, and perform some basic timing (elapsed time).
> 
> Signed-off-by: Thomas Hellström 
> ---
>  drivers/gpu/drm/Kconfig   |   1 +
>  drivers/gpu/drm/tests/Makefile|   3 +-
>  drivers/gpu/drm/tests/drm_suballoc_test.c | 356 ++
>  3 files changed, 359 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/tests/drm_suballoc_test.c
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 8fbe57407c60..dced53723721 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -78,6 +78,7 @@ config DRM_KUNIT_TEST
>   select DRM_LIB_RANDOM
>   select DRM_KMS_HELPER
>   select DRM_BUDDY
> + select DRM_SUBALLOC_HELPER
>   select DRM_EXPORT_FOR_TESTS if m
>   select DRM_KUNIT_TEST_HELPERS
>   default KUNIT_ALL_TESTS
> diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
> index bca726a8f483..c664944a48ab 100644
> --- a/drivers/gpu/drm/tests/Makefile
> +++ b/drivers/gpu/drm/tests/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
>   drm_modes_test.o \
>   drm_plane_helper_test.o \
>   drm_probe_helper_test.o \
> - drm_rect_test.o
> + drm_rect_test.o \
> + drm_suballoc_test.o
>  
>  CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN)
> diff --git a/drivers/gpu/drm/tests/drm_suballoc_test.c 
> b/drivers/gpu/drm/tests/drm_suballoc_test.c
> new file mode 100644
> index ..e7303a5505a0
> --- /dev/null
> +++ b/drivers/gpu/drm/tests/drm_suballoc_test.c
> @@ -0,0 +1,356 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +/*
> + * Test case for the drm_suballoc suballocator manager
> + * Copyright 2023 Intel Corporation.
> + */
> +
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#define SA_ITERATIONS 1
> +#define SA_SIZE SZ_1M
> +#define SA_DEFAULT_ALIGN SZ_4K
> +
> +static bool intr = true;
> +static bool from_reclaim;
> +static bool pre_throttle;
> +static unsigned int num_rings = 4;
> +static unsigned int iterations = SA_ITERATIONS;
> +
> +static atomic64_t free_space;
> +
> +static atomic_t other_id;
> +
> +struct suballoc_fence;
> +
> +/**
> + * struct suballoc_ring - fake gpu engine.
> + * @list: List of fences to signal.
> + * @signal_time: Accumulated fence signal execution time.
> + * @lock: Protects the suballoc ring members. hardirq safe.
> + * @hrtimer: Fake execution time timer.
> + * @active: The currently active fence for which we have pending work or a
> + *  timer running.
> + * @seqno: Fence submissin seqno.
> + * @idx: Index for calculation of fake execution time.
> + * @work: Work struct used solely to move the timer start to a different
> + *processor than that used for submission.
> + */
> +struct suballoc_ring {
> + ktime_t signal_time;
> + struct list_head list;
> + /* Protect the ring processing. */
> + spinlock_t lock;
> + struct hrtimer hrtimer;
> + struct suballoc_fence *active;
> + atomic64_t seqno;
> + u32 idx;
> + struct work_struct work;
> +};
> +
> +/**
> + * struct suballoc_fence - Hrtimer-driven fence.
> + * @fence: The base class fence struct.
> + * @link: Link for the ring's fence list.
> + * @size: The size of the suballocator range associated with this fence.
> + * @id: Cpu id likely used by the submission thread for suballoc allocation.
> + */
> +struct suballoc_fence {
> + struct dma_fence fence;
> + struct list_head link;
> + size_t size;
> + unsigned int id;
> +};
> +
> +/* A varying but repeatable fake execution time */
> +static ktime_t ring_next_delay(struct suballoc_ring *ring)
> +{
> + return ns_to_ktime((u64)(++ring->idx % 8) * 200 * NSEC_PER_USEC);
> +}

Is there any way we can avoid using time (and large number of
iterations) here, while keeping the coverage?
drm_suballoc have longest runtime out of all tests in DRM (taking ~60%
of the whole DRM kunit execution, drm_mm being the second and taking
~35%, without those two suites DRM tests execute in milliseconds rather
than tens of seconds),
Building test cases in a way that operate on time basis makes it tricky
to optimize the runtime.
If we extract various parameters from modparams to separate test cases,
it's going to get even worse.

> +
> +/*
> + * Launch from a work item to decrease the likelyhood of the timer expiry
> + * callback getting called from the allocating cpu.
> + * We want to trigger cache-line bouncing between allocating and signalling
> + * cpus.
> + */
> +static void ring_launch_timer_work(struct work_struct *work)
> +{
> + struct suballoc_ring *ring =
> + container_of(work, typeof(*ring), work);
> +
> + spin_lock_irq(&ring->lock);
> + if (ring->active)
> + hrtimer_start_range_ns(&ri

Re: KUnit issues - Was: [igt-dev] [PATCH RFC v2 8/8] drm/i915: check if current->mm is not NULL

2022-11-07 Thread Michał Winiarski
On Thu, Nov 03, 2022 at 04:23:02PM +0100, Mauro Carvalho Chehab wrote:
> Hi,
> 
> I'm facing a couple of issues when testing KUnit with the i915 driver.
> 
> The DRM subsystem and the i915 driver has, for a long time, his own
> way to do unit tests, which seems to be added before KUnit.
> 
> I'm now checking if it is worth start using KUnit at i915. So, I wrote
> a RFC with some patches adding support for the tests we have to be
> reported using Kernel TAP and KUnit.
> 
> There are basically 3 groups of tests there:
> 
> - mock tests - check i915 hardware-independent logic;
> - live tests - run some hardware-specific tests;
> - perf tests - check perf support - also hardware-dependent.
> 
> As they depend on i915 driver, they run only on x86, with PCI
> stack enabled, but the mock tests run nicely via qemu.
> 
> The live and perf tests require a real hardware. As we run them
> together with our CI, which, among other things, test module
> unload/reload and test loading i915 driver with different
> modprobe parameters, the KUnit tests should be able to run as
> a module.

Note that KUnit tests that are doing more of a functional/integration
testing (on "live" hardware) rather than unit testing (where hardware
interactions are mocked) are not very common.
Do we have other KUnit tests like this merged?
Some of the "live tests" are not even that, being more of a pure
hardware tests (e.g. live_workarounds, which is checking whether values
in MMIO regs stick over various HW state transitions).

I'm wondering, is KUnit the right tool for this job?

-Michał


Re: [PATCH v5 1/3] drm: Use XArray instead of IDR for minors

2022-11-07 Thread Michał Winiarski
On Sun, Nov 06, 2022 at 04:51:39PM +0200, Oded Gabbay wrote:
> On Wed, Nov 2, 2022 at 4:23 PM Oded Gabbay  wrote:
> >
> > On Mon, Sep 12, 2022 at 12:17 AM Michał Winiarski
> >  wrote:
> > >
> > > IDR is deprecated, and since XArray manages its own state with internal
> > > locking, it simplifies the locking on DRM side.
> > > Additionally, don't use the IRQ-safe variant, since operating on drm
> > > minor is not done in IRQ context.
> > >
> > > Signed-off-by: Michał Winiarski 
> > > Suggested-by: Matthew Wilcox 
> > > ---
> > >  drivers/gpu/drm/drm_drv.c | 51 ++-
> > >  1 file changed, 18 insertions(+), 33 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > > index 8214a0b1ab7f..61d24cdcd0f8 100644
> > > --- a/drivers/gpu/drm/drm_drv.c
> > > +++ b/drivers/gpu/drm/drm_drv.c
> > > @@ -34,6 +34,7 @@
> > >  #include 
> > >  #include 
> > >  #include 
> > > +#include 
> > >
> > >  #include 
> > >  #include 
> > > @@ -53,8 +54,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José 
> > > Fonseca, Jon Smirl");
> > >  MODULE_DESCRIPTION("DRM shared core routines");
> > >  MODULE_LICENSE("GPL and additional rights");
> > >
> > > -static DEFINE_SPINLOCK(drm_minor_lock);
> > > -static struct idr drm_minors_idr;
> > > +static DEFINE_XARRAY_ALLOC(drm_minors_xa);
> > >
> > >  /*
> > >   * If the drm core fails to init for whatever reason,
> > > @@ -98,21 +98,19 @@ static struct drm_minor **drm_minor_get_slot(struct 
> > > drm_device *dev,
> > >  static void drm_minor_alloc_release(struct drm_device *dev, void *data)
> > >  {
> > > struct drm_minor *minor = data;
> > > -   unsigned long flags;
> > >
> > > WARN_ON(dev != minor->dev);
> > >
> > > put_device(minor->kdev);
> > >
> > > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > > -   idr_remove(&drm_minors_idr, minor->index);
> > > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > > +   xa_erase(&drm_minors_xa, minor->index);
> > >  }
> > >
> > > +#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * 
> > > _t + 63); })
> > > +
> > >  static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
> > >  {
> > > struct drm_minor *minor;
> > > -   unsigned long flags;
> > > int r;
> > >
> > > minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
> > > @@ -122,21 +120,10 @@ static int drm_minor_alloc(struct drm_device *dev, 
> > > unsigned int type)
> > > minor->type = type;
> > > minor->dev = dev;
> > >
> > > -   idr_preload(GFP_KERNEL);
> > > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > > -   r = idr_alloc(&drm_minors_idr,
> > > - NULL,
> > > - 64 * type,
> > > - 64 * (type + 1),
> > > - GFP_NOWAIT);
> > > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > > -   idr_preload_end();
> > > -
> > > +   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
> > > DRM_MINOR_LIMIT(type), GFP_KERNEL);
> This was GFP_NOWAIT in the original code.

Because we were using spinlock and idr_preload.
We're actually fine with GFP_KERNEL (and we could just use mutex with
idr):
https://lore.kernel.org/dri-devel/20220823210612.296922-3-michal.winiar...@intel.com/

> 
> > > if (r < 0)
> > > return r;
> > >
> > > -   minor->index = r;
> > > -
> > > r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
> > > if (r)
> > > return r;
> > > @@ -152,7 +139,7 @@ static int drm_minor_alloc(struct drm_device *dev, 
> > > unsigned int type)
> > >  static int drm_minor_register(struct drm_device *dev, unsigned int type)
> > >  {
> > > struct drm_minor *minor;
> > > -   unsigned long flags;
> > > +   void *entry;
> > > int ret;
> > >
> > > DRM_DEBUG("\n");
> > > @@ -172,9 +15

[PATCH v3] drm: Use XArray instead of IDR for minors

2022-11-07 Thread Michał Winiarski
IDR is deprecated, and since XArray manages its own state with internal
locking, it simplifies the locking on DRM side.
Additionally, don't use the IRQ-safe variant, since operating on drm
minor is not done in IRQ context.

Signed-off-by: Michał Winiarski 
Suggested-by: Matthew Wilcox 
---
This was originally sent as v4/v5 in larger series:
https://lore.kernel.org/dri-devel/20220911211443.581481-2-michal.winiar...@intel.com/
v1 -> v2:
Fixed locking and corrected some misuse of XArray API (Matthew Wilcox)
v2 -> v3:
Store correct pointer, use xa_store rather than xa_cmpxchg (Oded Gabbay)

 drivers/gpu/drm/drm_drv.c | 51 ++-
 1 file changed, 18 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 8214a0b1ab7f..bdcdfeac76bf 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -53,8 +54,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon 
Smirl");
 MODULE_DESCRIPTION("DRM shared core routines");
 MODULE_LICENSE("GPL and additional rights");
 
-static DEFINE_SPINLOCK(drm_minor_lock);
-static struct idr drm_minors_idr;
+static DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
 /*
  * If the drm core fails to init for whatever reason,
@@ -98,21 +98,19 @@ static struct drm_minor **drm_minor_get_slot(struct 
drm_device *dev,
 static void drm_minor_alloc_release(struct drm_device *dev, void *data)
 {
struct drm_minor *minor = data;
-   unsigned long flags;
 
WARN_ON(dev != minor->dev);
 
put_device(minor->kdev);
 
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_remove(&drm_minors_idr, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   xa_erase(&drm_minors_xa, minor->index);
 }
 
+#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 
63); })
+
 static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
int r;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
@@ -122,21 +120,10 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
minor->type = type;
minor->dev = dev;
 
-   idr_preload(GFP_KERNEL);
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   r = idr_alloc(&drm_minors_idr,
- NULL,
- 64 * type,
- 64 * (type + 1),
- GFP_NOWAIT);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
-   idr_preload_end();
-
+   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_MINOR_LIMIT(type), GFP_KERNEL);
if (r < 0)
return r;
 
-   minor->index = r;
-
r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
if (r)
return r;
@@ -152,7 +139,7 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 static int drm_minor_register(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
+   void *entry;
int ret;
 
DRM_DEBUG("\n");
@@ -172,9 +159,12 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
goto err_debugfs;
 
/* replace NULL with @minor so lookups will succeed from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, minor, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   entry = xa_store(&drm_minors_xa, minor->index, minor, GFP_KERNEL);
+   if (xa_is_err(entry)) {
+   ret = xa_err(entry);
+   goto err_debugfs;
+   }
+   WARN_ON(entry);
 
DRM_DEBUG("new minor registered %d\n", minor->index);
return 0;
@@ -187,16 +177,13 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
 static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
minor = *drm_minor_get_slot(dev, type);
if (!minor || !device_is_registered(minor->kdev))
return;
 
/* replace @minor with NULL so lookups will fail from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, NULL, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   xa_store(&drm_minors_xa, minor->index, NULL, GFP_KERNEL);
 
device_del(minor->kdev);
dev_set_drvdata(minor->kdev, NULL); /* safety belt */
@@ -215,13 +202,12 @@ static void drm_minor_unregister(struct drm_device *dev, 
unsigned int type)
 struct drm_minor *drm_minor_acquire(u

Re: [PATCH 1/2] drm: remove DRM_MINOR_CONTROL

2022-10-25 Thread Michał Winiarski
On Tue, Oct 11, 2022 at 01:55:01PM +0200, Christian König wrote:
> Am 11.10.22 um 13:39 schrieb Simon Ser:
> > On Tuesday, October 11th, 2022 at 13:04, Christian König 
> >  wrote:
> > 
> > > --- a/include/drm/drm_file.h
> > > +++ b/include/drm/drm_file.h
> > > @@ -54,7 +54,6 @@ struct file;
> > >*/
> > >   enum drm_minor_type {
> > >   DRM_MINOR_PRIMARY,
> > > - DRM_MINOR_CONTROL,
> > >   DRM_MINOR_RENDER,
> > >   };
> > This makes me uncomfortable: this enum no longer matches DRM_NODE_* in
> > libdrm.
> 
> Ah! There it was! I was remembering in the back of my head that we had
> somehow used this in libdrm as well, but couldn't really get where exactly.
> 
> But I don't really see a problem here. The control nodes are identified by
> name and we don't expose them for quite some time now without any negative
> impact.
> 
> Even the minor number distribution stays the same. So what bad can come from
> this?
> 
> Thanks,
> Christian.
> 

I proposed something similar in:
https://lore.kernel.org/dri-devel/20220817230600.272790-1-michal.winiar...@intel.com/
except acompanied by expanding the minor pool to accomodate more than
128 devices:

And after receiving similar feedback, that eventually evolved into
leaving the "legacy minors" alone, and changing the rules only for cases
where we have more than 64 devices  (when we run out of the "legacy
minors").

Perhaps something like this:
https://lore.kernel.org/dri-devel/20220911211443.581481-1-michal.winiar...@intel.com/
Would work for your usecase as well?

-Michał


Re: [RFC PATCH 3/3] drm: add dedicated minor for accelerator devices

2022-10-25 Thread Michał Winiarski
On Mon, Oct 24, 2022 at 08:43:58PM +0300, Oded Gabbay wrote:
> On Mon, Oct 24, 2022 at 6:21 PM Jeffrey Hugo  wrote:
> >
> > On 10/22/2022 3:46 PM, Oded Gabbay wrote:
> > > The accelerator devices are exposed to user-space using a dedicated
> > > major. In addition, they are represented in /dev with new, dedicated
> > > device char names: /dev/accel/accel*. This is done to make sure any
> > > user-space software that tries to open a graphic card won't open
> > > the accelerator device by mistake.
> > >
> > > The above implies that the minor numbering should be separated from
> > > the rest of the drm devices. However, to avoid code duplication, we
> > > want the drm_minor structure to be able to represent the accelerator
> > > device.
> > >
> > > To achieve this, we add a new drm_minor* to drm_device that represents
> > > the accelerator device. This pointer is initialized for drivers that
> > > declare they handle compute accelerator, using a new driver feature
> > > flag called DRIVER_COMPUTE_ACCEL. It is important to note that this
> > > driver feature is mutually exclusive with DRIVER_RENDER. Devices that
> > > want to expose both graphics and compute device char files should be
> > > handled by two drivers that are connected using the auxiliary bus
> > > framework.
> > >
> > > In addition, we define a different idr to handle the accelerators
> > > minors. This is done to make the minor's index be identical to the
> > > device index in /dev/. In most places, this is hidden inside the drm
> > > core functions except when calling drm_minor_acquire(), where I had to
> > > add an extra parameter to specify the idr to use (because the
> > > accelerators minors index and the drm primary minor index both begin
> > > at 0).
> > >
> > > Signed-off-by: Oded Gabbay 
> > > ---
> > >   drivers/gpu/drm/drm_drv.c  | 171 +
> > >   drivers/gpu/drm/drm_file.c |  69 +
> > >   drivers/gpu/drm/drm_internal.h |   2 +-
> > >   drivers/gpu/drm/drm_sysfs.c|  29 --
> > >   include/drm/drm_device.h   |   3 +
> > >   include/drm/drm_drv.h  |   8 ++
> > >   include/drm/drm_file.h |  21 +++-
> > >   7 files changed, 235 insertions(+), 68 deletions(-)
> >
> > Can we please add something to Documentation?  I know this leverages DRM
> > a lot, but I believe that a new subsystem should not be introduced
> > without documentation.  A lot of the info in the commit message is very
> > good, but should not be buried in the git log.
> >
> > Besides, imagine this has been in mainline for N years, and someone
> > completely new to the kernel wants to write an accel driver.  They
> > should be able to get started with something from Documentation that
> > at-least gives that person some insight into what to grep the code for.
> Agreed. The only reason I haven't done it at this stage was because I
> wanted to get an initial reaction to the code itself, see if the
> direction is accepted.
> I didn't want to write documentation and then completely re-write it.
> So I will do it for the next patch-set, once I collect everyone's
> feedback and I see there is a majority agreement.
> >
> > >
> > > diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > > index b58ffb1433d6..c13701a8d4be 100644
> > > --- a/drivers/gpu/drm/drm_drv.c
> > > +++ b/drivers/gpu/drm/drm_drv.c
> > > @@ -56,6 +56,9 @@ MODULE_LICENSE("GPL and additional rights");
> > >   static DEFINE_SPINLOCK(drm_minor_lock);
> > >   static struct idr drm_minors_idr;
> > >
> > > +static DEFINE_SPINLOCK(accel_minor_lock);
> > > +static struct idr accel_minors_idr;
> >
> > IDR is deprecated.  XArray is the preferred mechanism.
> > Yes, there already is IDR here, but I believe we should not be adding
> > new uses.  Maybe at some point, the current IDR will be converted.  Also
> > with XArray, I think you don't need the spinlock since XArray has
> > internal locking already.
> ok, I wasn't aware. I don't have any problem replacing the idr to xarray.

The conversion is sitting on the mailinglist for a while now
(unfortunately, without much interest).
Perhaps you could help with reviewing it?
https://lore.kernel.org/dri-devel/20220911211443.581481-2-michal.winiar...@intel.com/

-Michał

> 
> Thanks,
> Oded
> 


[PATCH v4 2/2] drm/plane_helper: Split into parameterized test cases

2022-10-20 Thread Michał Winiarski
The test was constructed as a single function (test case) which checks
multiple conditions, calling the function that is tested multiple times
with different arguments.
This usually means that it can be easily converted into multiple test
cases.
Split igt_check_plane_state into two parameterized test cases,
drm_check_plane_state and drm_check_invalid_plane_state.

Passing output:

== drm_plane_helper (2 subtests) ===
== drm_check_plane_state ===
[PASSED] clipping_simple
[PASSED] clipping_rotate_reflect
[PASSED] positioning_simple
[PASSED] upscaling
[PASSED] downscaling
[PASSED] rounding1
[PASSED] rounding2
[PASSED] rounding3
[PASSED] rounding4
== [PASSED] drm_check_plane_state ==
== drm_check_invalid_plane_state ===
[PASSED] positioning_invalid
[PASSED] upscaling_invalid
[PASSED] downscaling_invalid
== [PASSED] drm_check_invalid_plane_state ==
 [PASSED] drm_plane_helper =

Testing complete. Ran 12 tests: passed: 12

v2: Add missing EXPECT/ASSERT (Maíra)
v3: Use single EXPECT insted of condition + KUNIT_FAILURE (Maíra)
v4: Rebase after "drm_test" rename

Signed-off-by: Michał Winiarski 
Reviewed-by: Maíra Canal 
---
 drivers/gpu/drm/tests/drm_plane_helper_test.c | 466 ++
 1 file changed, 268 insertions(+), 198 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_plane_helper_test.c 
b/drivers/gpu/drm/tests/drm_plane_helper_test.c
index 4963f0c960d8..6070671834ea 100644
--- a/drivers/gpu/drm/tests/drm_plane_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_plane_helper_test.c
@@ -12,233 +12,303 @@
 #include 
 #include 
 
-static void set_src(struct drm_plane_state *plane_state,
-   unsigned int src_x, unsigned int src_y,
-   unsigned int src_w, unsigned int src_h)
+static const struct drm_crtc_state crtc_state = {
+   .crtc = ZERO_SIZE_PTR,
+   .enable = true,
+   .active = true,
+   .mode = {
+   DRM_MODE("1024x768", 0, 65000, 1024, 1048,
+1184, 1344, 0, 768, 771, 777, 806, 0,
+DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
+   },
+};
+
+struct drm_check_plane_state_test {
+   const char *name;
+   const char *msg;
+   struct {
+   unsigned int x;
+   unsigned int y;
+   unsigned int w;
+   unsigned int h;
+   } src, src_expected;
+   struct {
+   int x;
+   int y;
+   unsigned int w;
+   unsigned int h;
+   } crtc, crtc_expected;
+   unsigned int rotation;
+   int min_scale;
+   int max_scale;
+   bool can_position;
+};
+
+static int drm_plane_helper_init(struct kunit *test)
 {
-   plane_state->src_x = src_x;
-   plane_state->src_y = src_y;
-   plane_state->src_w = src_w;
-   plane_state->src_h = src_h;
+   const struct drm_check_plane_state_test *params = test->param_value;
+   struct drm_plane *plane;
+   struct drm_framebuffer *fb;
+   struct drm_plane_state *mock;
+
+   plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_NULL(test, plane);
+
+   fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_NULL(test, fb);
+   fb->width = 2048;
+   fb->height = 2048;
+
+   mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_NULL(test, mock);
+   mock->plane = plane;
+   mock->crtc = ZERO_SIZE_PTR;
+   mock->fb = fb;
+   mock->rotation = params->rotation;
+   mock->src_x = params->src.x;
+   mock->src_y = params->src.y;
+   mock->src_w = params->src.w;
+   mock->src_h = params->src.h;
+   mock->crtc_x = params->crtc.x;
+   mock->crtc_y = params->crtc.y;
+   mock->crtc_w = params->crtc.w;
+   mock->crtc_h = params->crtc.h;
+
+   test->priv = mock;
+
+   return 0;
 }
 
-static bool check_src_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
+static void check_src_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
 unsigned int src_x, unsigned int src_y,
 unsigned int src_w, unsigned int src_h)
 {
struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
 
-   if (plane_state->src.x1 < 0) {
-   kunit_err(test,
- "src x coordinate %x should never be below 0, src: " 
DRM_RECT_FP_FMT,
- plane_state->src.x1, 
DRM_RECT_FP_ARG(&plane_state->src));
-   return false;
-   }
-   if (plane_state->src.y

[PATCH v4 1/2] drm/plane_helper: Print actual/expected values on failure

2022-10-20 Thread Michał Winiarski
Currently the values are printed with debug log level.
Adjust the log level and link the output with the test by using kunit_err.

Example output:
foo: dst: 20x20+10+10, expected: 10x10+0+0
foo: EXPECTATION FAILED at drivers/gpu/drm/tests/drm_plane_helper_test.c:85

Signed-off-by: Michał Winiarski 
Reviewed-by: Maíra Canal 
---
 drivers/gpu/drm/tests/drm_plane_helper_test.c | 78 +++
 1 file changed, 44 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_plane_helper_test.c 
b/drivers/gpu/drm/tests/drm_plane_helper_test.c
index ec71af791f1f..4963f0c960d8 100644
--- a/drivers/gpu/drm/tests/drm_plane_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_plane_helper_test.c
@@ -10,6 +10,7 @@
 #include 
 #include 
 #include 
+#include 
 
 static void set_src(struct drm_plane_state *plane_state,
unsigned int src_x, unsigned int src_y,
@@ -21,26 +22,32 @@ static void set_src(struct drm_plane_state *plane_state,
plane_state->src_h = src_h;
 }
 
-static bool check_src_eq(struct drm_plane_state *plane_state,
+static bool check_src_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
 unsigned int src_x, unsigned int src_y,
 unsigned int src_w, unsigned int src_h)
 {
+   struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
+
if (plane_state->src.x1 < 0) {
-   pr_err("src x coordinate %x should never be below 0.\n", 
plane_state->src.x1);
-   drm_rect_debug_print("src: ", &plane_state->src, true);
+   kunit_err(test,
+ "src x coordinate %x should never be below 0, src: " 
DRM_RECT_FP_FMT,
+ plane_state->src.x1, 
DRM_RECT_FP_ARG(&plane_state->src));
return false;
}
if (plane_state->src.y1 < 0) {
-   pr_err("src y coordinate %x should never be below 0.\n", 
plane_state->src.y1);
-   drm_rect_debug_print("src: ", &plane_state->src, true);
+   kunit_err(test,
+ "src y coordinate %x should never be below 0, src: " 
DRM_RECT_FP_FMT,
+ plane_state->src.y1, 
DRM_RECT_FP_ARG(&plane_state->src));
return false;
}
 
-   if (plane_state->src.x1 != src_x ||
-   plane_state->src.y1 != src_y ||
-   drm_rect_width(&plane_state->src) != src_w ||
-   drm_rect_height(&plane_state->src) != src_h) {
-   drm_rect_debug_print("src: ", &plane_state->src, true);
+   if (plane_state->src.x1 != expected.x1 ||
+   plane_state->src.y1 != expected.y1 ||
+   drm_rect_width(&plane_state->src) != drm_rect_width(&expected) ||
+   drm_rect_height(&plane_state->src) != drm_rect_height(&expected)) {
+   kunit_err(test, "src: " DRM_RECT_FP_FMT ", expected: " 
DRM_RECT_FP_FMT,
+ DRM_RECT_FP_ARG(&plane_state->src), 
DRM_RECT_FP_ARG(&expected));
+
return false;
}
 
@@ -57,15 +64,18 @@ static void set_crtc(struct drm_plane_state *plane_state,
plane_state->crtc_h = crtc_h;
 }
 
-static bool check_crtc_eq(struct drm_plane_state *plane_state,
+static bool check_crtc_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
  int crtc_x, int crtc_y,
  unsigned int crtc_w, unsigned int crtc_h)
 {
-   if (plane_state->dst.x1 != crtc_x ||
-   plane_state->dst.y1 != crtc_y ||
-   drm_rect_width(&plane_state->dst) != crtc_w ||
-   drm_rect_height(&plane_state->dst) != crtc_h) {
-   drm_rect_debug_print("dst: ", &plane_state->dst, false);
+   struct drm_rect expected = DRM_RECT_INIT(crtc_x, crtc_y, crtc_w, 
crtc_h);
+
+   if (plane_state->dst.x1 != expected.x1 ||
+   plane_state->dst.y1 != expected.y1 ||
+   drm_rect_width(&plane_state->dst) != drm_rect_width(&expected) ||
+   drm_rect_height(&plane_state->dst) != drm_rect_height(&expected)) {
+   kunit_err(test, "dst: " DRM_RECT_FMT ", expected: " 
DRM_RECT_FMT,
+ DRM_RECT_ARG(&plane_state->dst), 
DRM_RECT_ARG(&expected));
 
return false;
}
@@ -109,8 +119,8 @@ static void drm_test_check_plane_state(struct kunit *test)
  false, false);
KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Simple clipping check should 
pass\n");
KUNIT_EXPECT_TRUE(test, plane_state.visible);
-   KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 1024 << 16, 

Re: [PATCH v5 07/22] drm/client: Add some tests for drm_connector_pick_cmdline_mode()

2022-10-20 Thread Michał Winiarski
On Thu, Oct 13, 2022 at 03:18:51PM +0200, Maxime Ripard wrote:
> +static struct kunit_case drm_pick_cmdline_tests[] = {
> + KUNIT_CASE(drm_pick_cmdline_res_1920_1080_60),
> + {}
> +};

drm_test_pick_cmdline_res_1920_1080_60, since we adopted a consistent naming
convention for test cases in DRM.

-Michał

> +
> +static struct kunit_suite drm_pick_cmdline_test_suite = {
> + .name = "drm_pick_cmdline",
> + .init = drm_client_modeset_test_init,
> + .test_cases = drm_pick_cmdline_tests
> +};
> +
> +kunit_test_suite(drm_pick_cmdline_test_suite);
> 
> -- 
> b4 0.11.0-dev-7da52
> 


Re: [PATCH v3 2/2] drm/tests: Split drm_test_dp_mst_sideband_msg_req_decode into parameterized tests

2022-10-03 Thread Michał Winiarski
On Sat, Oct 01, 2022 at 07:34:22PM -0300, Maíra Canal wrote:
> The drm_test_dp_mst_sideband_msg_req_decode repeats the same test
> structure with different parameters. This could be better represented
> by parameterized tests, provided by KUnit.
> 
> In addition to the parameterization of the tests, the test case for the
> client ID was changed: instead of using get_random_bytes to generate
> the client ID, the client ID is now hardcoded in the test case. This
> doesn't affect the assertively of the tests, as this test case only compare
> the data going in with the data going out and it doesn't transform the data
> itself in any way.
> 
> So, convert drm_test_dp_mst_sideband_msg_req_decode into parameterized
> tests and make the tests' allocations and prints completely managed by KUnit.
> 
> Signed-off-by: Maíra Canal 

Reviewed-by: Michał Winiarski 

Thanks!
-Michał

> ---
> v1 -> v2: 
> https://lore.kernel.org/dri-devel/20220925222719.345424-1-mca...@igalia.com/T/#m056610a23a63109484afeafefb5846178c4d36b2
> - Mention on the commit message the change on the test case for the client ID 
> (Michał Winiarski).
> 
> v2 -> v3: 
> https://lore.kernel.org/dri-devel/20220927221206.55930-1-mca...@igalia.com/T/#m2dc961da2d4921566cd0f9a8ed9d2d33a1cf4416
> - Mention on the commit message that the "random" usage is not incompatible 
> with parameterized tests (Michał Winiarski).
> ---
>  .../gpu/drm/tests/drm_dp_mst_helper_test.c| 370 --
>  1 file changed, 243 insertions(+), 127 deletions(-)


Re: [PATCH v2 2/2] drm/tests: Split drm_test_dp_mst_sideband_msg_req_decode into parameterized tests

2022-09-30 Thread Michał Winiarski
On Fri, Sep 30, 2022 at 02:50:43PM +0800, David Gow wrote:
> On Fri, Sep 30, 2022 at 6:33 AM Michał Winiarski
>  wrote:
> >
> > On Tue, Sep 27, 2022 at 07:12:06PM -0300, Maíra Canal wrote:
> > > The drm_test_dp_mst_sideband_msg_req_decode repeats the same test
> > > structure with different parameters. This could be better represented
> > > by parameterized tests, provided by KUnit.
> > >
> > > In order to convert the tests to parameterized tests, the test case for
> > > the client ID was changed: instead of using get_random_bytes to generate
> > > the client ID, the client ID is now hardcoded in the test case.
> >
> > Generally "random" usage is not incompatible with parameterized tests, we 
> > can
> > create parameterized tests that use random data.
> > The idea is to pass a function that generates the actual param (where we 
> > have a
> > pointer to function as one of the members in "params" struct).
> >
> > For example, see "random_dp_query_enc_client_id" usage here:
> > https://lore.kernel.org/dri-devel/20220117232259.180459-7-michal.winiar...@intel.com/
> >
> > In this case, we just compare data going in with data going out (and the 
> > data
> > itself is not transformed in any way), so it doesn't really matter for 
> > coverage
> > and we can hardcode.
> >
> > -Michał
> 
> FWIW, while the uses of randomness in DRM tests so far haven't
> concerned me much, I think we'll eventually want to have some way of
> ensuring the inputs to tests are deterministic.
> 
> My thoughts are that (at some point) we'll add a kunit_random()
> function or similar, which will use a pseudorandom number generator
> which can be set to a deterministic seed before each test case. That
> way, there'd be a way to reproduce an error easily if it occurred. (Of
> course, there'd be a way of setting different or random seeds to
> preserve the extra coverage you'd otherwise get.)

That's exactly what DRM tests do (well... most DRM tests, this one being the
exception, and those other tests also seem to have lost a printk with seed value
after being refactored into kunit).
See the usage of DRM_RND_STATE in drm_mm_test and drm_buddy_test.
Having kunit_random() would definitely be useful and let us remove bunch of
boilerplate from the tests, but it doesn't prevent using reproducible random
data in existing tests.

> I don't think this is something worth holding up or changing existing
> tests at the moment, but having tests behave deterministically is
> definitely desirable, so +1 to avoiding get_random_bytes() if it's not
> giving you any real benefit.

Yeah - all I was refering to in my previous message was the wording of the
commit message. We're just removing it because it is desirable in this
particular case, not because of the fact that the test is now parameterized and
that's somehow preventing get_random_bytes() usage.

-Michał

> We've also had a few requests in the past for being able to pass in a
> custom set of parameters from userspace, which opens up some other
> interesting possibilities, though it's not a priority at the moment.
> 
> Cheers,
> -- David




Re: [PATCH v2 2/2] drm/tests: Split drm_test_dp_mst_sideband_msg_req_decode into parameterized tests

2022-09-29 Thread Michał Winiarski
On Tue, Sep 27, 2022 at 07:12:06PM -0300, Maíra Canal wrote:
> The drm_test_dp_mst_sideband_msg_req_decode repeats the same test
> structure with different parameters. This could be better represented
> by parameterized tests, provided by KUnit.
> 
> In order to convert the tests to parameterized tests, the test case for
> the client ID was changed: instead of using get_random_bytes to generate
> the client ID, the client ID is now hardcoded in the test case.

Generally "random" usage is not incompatible with parameterized tests, we can
create parameterized tests that use random data.
The idea is to pass a function that generates the actual param (where we have a
pointer to function as one of the members in "params" struct).

For example, see "random_dp_query_enc_client_id" usage here:
https://lore.kernel.org/dri-devel/20220117232259.180459-7-michal.winiar...@intel.com/

In this case, we just compare data going in with data going out (and the data
itself is not transformed in any way), so it doesn't really matter for coverage
and we can hardcode.

-Michał

> So, convert drm_test_dp_mst_sideband_msg_req_decode into parameterized
> tests and make the tests' allocations and prints completely managed by KUnit.
> 
> Signed-off-by: Maíra Canal 
> ---
> v1 -> v2: 
> https://lore.kernel.org/dri-devel/20220925222719.345424-1-mca...@igalia.com/T/#m056610a23a63109484afeafefb5846178c4d36b2
> - Mention on the commit message the change on the test case for the client ID 
> (Michał Winiarski).
> ---
>  .../gpu/drm/tests/drm_dp_mst_helper_test.c| 370 --
>  1 file changed, 243 insertions(+), 127 deletions(-)


Re: [PATCH 1/2] drm/tests: Split drm_test_dp_mst_calc_pbn_mode into parameterized tests

2022-09-25 Thread Michał Winiarski
On Sun, Sep 25, 2022 at 07:27:18PM -0300, Maíra Canal wrote:
> The drm_test_dp_mst_calc_pbn_mode is based on a loop that executes tests
> for a couple of test cases. This could be better represented by
> parameterized tests, provided by KUnit.
> 
> So, convert the drm_test_dp_mst_calc_pbn_mode into parameterized tests.
> 
> Signed-off-by: Maíra Canal 

Reviewed-by: Michał Winiarski 

-Michał

> ---
>  .../gpu/drm/tests/drm_dp_mst_helper_test.c| 77 +--
>  1 file changed, 53 insertions(+), 24 deletions(-)


Re: [PATCH 2/2] drm/tests: Split drm_test_dp_mst_sideband_msg_req_decode into parameterized tests

2022-09-25 Thread Michał Winiarski
On Sun, Sep 25, 2022 at 07:27:19PM -0300, Maíra Canal wrote:
> The drm_test_dp_mst_sideband_msg_req_decode repeats the same test
> structure with different parameters. This could be better represented
> by parameterized tests, provided by KUnit.
> 
> So, convert drm_test_dp_mst_sideband_msg_req_decode into parameterized
> tests and make the test's allocations and prints completly managed by KUnit.

There's a small functional change not mentioned in the commit message.

> 
> Signed-off-by: Maíra Canal 
> ---
>  .../gpu/drm/tests/drm_dp_mst_helper_test.c| 370 --
>  1 file changed, 243 insertions(+), 127 deletions(-)
> 
> diff --git a/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c 
> b/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c
> index 12f41881db6b..545beea33e8c 100644
> --- a/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c
> @@ -5,12 +5,8 @@
>   * Copyright (c) 2022 Maíra Canal 
>   */
>  
> -#define PREFIX_STR "[drm_dp_mst_helper]"
> -
>  #include 
>  
> -#include 
> -
>  #include 
>  #include 
>  
> @@ -72,6 +68,217 @@ static void dp_mst_calc_pbn_mode_desc(const struct 
> drm_dp_mst_calc_pbn_mode_test
>  KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_cases,
> dp_mst_calc_pbn_mode_desc);
>  
> +static u8 data[] = { 0xff, 0x00, 0xdd };
> +
> +struct drm_dp_mst_sideband_msg_req_test {
> + const char *desc;
> + const struct drm_dp_sideband_msg_req_body in;
> +};
> +
> +static const struct drm_dp_mst_sideband_msg_req_test 
> drm_dp_mst_sideband_msg_req_cases[] = {
> + {
> + .desc = "DP_ENUM_PATH_RESOURCES with port number",
> + .in = {
> + .req_type = DP_ENUM_PATH_RESOURCES,
> + .u.port_num.port_number = 5,
> + },
> + },
> + {
> + .desc = "DP_POWER_UP_PHY with port number",
> + .in = {
> + .req_type = DP_POWER_UP_PHY,
> + .u.port_num.port_number = 5,
> + },
> + },
> + {
> + .desc = "DP_POWER_DOWN_PHY with port number",
> + .in = {
> + .req_type = DP_POWER_DOWN_PHY,
> + .u.port_num.port_number = 5,
> + },
> + },
> + {
> + .desc = "DP_ALLOCATE_PAYLOAD with SDP stream sinks",
> + .in = {
> + .req_type = DP_ALLOCATE_PAYLOAD,
> + .u.allocate_payload.number_sdp_streams = 3,
> + .u.allocate_payload.sdp_stream_sink = { 1, 2, 3 },
> + },
> + },
> + {
> + .desc = "DP_ALLOCATE_PAYLOAD with port number",
> + .in = {
> + .req_type = DP_ALLOCATE_PAYLOAD,
> + .u.allocate_payload.port_number = 0xf,
> + },
> + },
> + {
> + .desc = "DP_ALLOCATE_PAYLOAD with VCPI",
> + .in = {
> + .req_type = DP_ALLOCATE_PAYLOAD,
> + .u.allocate_payload.vcpi = 0x7f,
> + },
> + },
> + {
> + .desc = "DP_ALLOCATE_PAYLOAD with PBN",
> + .in = {
> + .req_type = DP_ALLOCATE_PAYLOAD,
> + .u.allocate_payload.pbn = U16_MAX,
> + },
> + },
> + {
> + .desc = "DP_QUERY_PAYLOAD with port number",
> + .in = {
> + .req_type = DP_QUERY_PAYLOAD,
> + .u.query_payload.port_number = 0xf,
> + },
> + },
> + {
> + .desc = "DP_QUERY_PAYLOAD with VCPI",
> + .in = {
> + .req_type = DP_QUERY_PAYLOAD,
> + .u.query_payload.vcpi = 0x7f,
> + },
> + },
> + {
> + .desc = "DP_REMOTE_DPCD_READ with port number",
> + .in = {
> + .req_type = DP_REMOTE_DPCD_READ,
> + .u.dpcd_read.port_number = 0xf,
> + },
> + },
> + {
> + .desc = "DP_REMOTE_DPCD_READ with DPCD address",
> + .in = {
> + .req_type = DP_REMOTE_DPCD_READ,
> + .u.dpcd_read.dpcd_address = 0xfedcb,
> + },
> + },
> + {
> + .desc = "DP_REMOTE_DPCD_READ with max number of bytes",
> + .in = {
> + .req_type = DP_REMOTE_DPCD_READ,
> + .u.dpcd_read.num_bytes = U8_MAX,
> + },
> + },
> + {
> + .desc = "DP_REMOTE_DPCD_WRITE with port number",
> + .in = {
> + .req_type = DP_REMOTE_DPCD_WRITE,
> + .u.dpcd_write.port_number = 0xf,
> + },
> + },
> + {
> + .desc = "DP_REMOTE_DPCD_WRITE with DPCD address",
> + .in = {
> + .req_type = DP_REMOTE_DPCD_WRITE,
> + .u

[PATCH v5 3/3] drm: Introduce skip_legacy_minors modparam

2022-09-11 Thread Michał Winiarski
While there is support for >64 DRM devices on kernel side, existing
userspace may still have some hardcoded assumptions and it's possible
that it will require changes to be able to use more than 64 devices.
Add a modparam to simplify testing and development of >64 devices
support on userspace side by allocating minors from the >=192 range
(without the need of having >64 physical devices connected).

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 10 --
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 3718bd6bbef6..368408997fed 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -56,6 +56,11 @@ MODULE_LICENSE("GPL and additional rights");
 
 static DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
+static bool skip_legacy_minors;
+module_param_unsafe(skip_legacy_minors, bool, 0400);
+MODULE_PARM_DESC(skip_legacy_minors,
+"Don't allocate minors in 0-192 range. This can be used for 
testing userspace support for >64 drm devices (default: false)");
+
 /*
  * If the drm core fails to init for whatever reason,
  * we should prevent any drivers from registering with it.
@@ -112,7 +117,7 @@ static void drm_minor_alloc_release(struct drm_device *dev, 
void *data)
 static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   int r;
+   int r = -EBUSY;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
if (!minor)
@@ -127,7 +132,8 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 * and 128-191 are render nodes.
 * After reaching the limit, we're allocating minors dynamically - 
first-come, first-serve.
 */
-   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_LEGACY_MINOR_LIMIT(type), GFP_KERNEL);
+   if (!skip_legacy_minors)
+   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_LEGACY_MINOR_LIMIT(type), GFP_KERNEL);
if (r == -EBUSY)
r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_MINOR_LIMIT, GFP_KERNEL);
if (r < 0)
-- 
2.37.3



[PATCH v5 1/3] drm: Use XArray instead of IDR for minors

2022-09-11 Thread Michał Winiarski
IDR is deprecated, and since XArray manages its own state with internal
locking, it simplifies the locking on DRM side.
Additionally, don't use the IRQ-safe variant, since operating on drm
minor is not done in IRQ context.

Signed-off-by: Michał Winiarski 
Suggested-by: Matthew Wilcox 
---
 drivers/gpu/drm/drm_drv.c | 51 ++-
 1 file changed, 18 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 8214a0b1ab7f..61d24cdcd0f8 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -53,8 +54,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon 
Smirl");
 MODULE_DESCRIPTION("DRM shared core routines");
 MODULE_LICENSE("GPL and additional rights");
 
-static DEFINE_SPINLOCK(drm_minor_lock);
-static struct idr drm_minors_idr;
+static DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
 /*
  * If the drm core fails to init for whatever reason,
@@ -98,21 +98,19 @@ static struct drm_minor **drm_minor_get_slot(struct 
drm_device *dev,
 static void drm_minor_alloc_release(struct drm_device *dev, void *data)
 {
struct drm_minor *minor = data;
-   unsigned long flags;
 
WARN_ON(dev != minor->dev);
 
put_device(minor->kdev);
 
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_remove(&drm_minors_idr, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   xa_erase(&drm_minors_xa, minor->index);
 }
 
+#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 
63); })
+
 static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
int r;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
@@ -122,21 +120,10 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
minor->type = type;
minor->dev = dev;
 
-   idr_preload(GFP_KERNEL);
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   r = idr_alloc(&drm_minors_idr,
- NULL,
- 64 * type,
- 64 * (type + 1),
- GFP_NOWAIT);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
-   idr_preload_end();
-
+   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_MINOR_LIMIT(type), GFP_KERNEL);
if (r < 0)
return r;
 
-   minor->index = r;
-
r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
if (r)
return r;
@@ -152,7 +139,7 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 static int drm_minor_register(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
+   void *entry;
int ret;
 
DRM_DEBUG("\n");
@@ -172,9 +159,12 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
goto err_debugfs;
 
/* replace NULL with @minor so lookups will succeed from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, minor, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   entry = xa_cmpxchg(&drm_minors_xa, minor->index, NULL, &minor, 
GFP_KERNEL);
+   if (xa_is_err(entry)) {
+   ret = xa_err(entry);
+   goto err_debugfs;
+   }
+   WARN_ON(entry);
 
DRM_DEBUG("new minor registered %d\n", minor->index);
return 0;
@@ -187,16 +177,13 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
 static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
minor = *drm_minor_get_slot(dev, type);
if (!minor || !device_is_registered(minor->kdev))
return;
 
/* replace @minor with NULL so lookups will fail from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, NULL, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   xa_store(&drm_minors_xa, minor->index, NULL, GFP_KERNEL);
 
device_del(minor->kdev);
dev_set_drvdata(minor->kdev, NULL); /* safety belt */
@@ -215,13 +202,12 @@ static void drm_minor_unregister(struct drm_device *dev, 
unsigned int type)
 struct drm_minor *drm_minor_acquire(unsigned int minor_id)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   minor = idr_find(&drm_minors_idr, minor_id);
+   xa_lock(&drm_minors_xa);
+   minor = xa_load(&drm

[PATCH v5 2/3] drm: Expand max DRM device number to full MINORBITS

2022-09-11 Thread Michał Winiarski
Having a limit of 64 DRM devices is not good enough for modern world
where we have multi-GPU servers, SR-IOV virtual functions and virtual
devices used for testing.
Let's utilize full minor range for DRM devices.
To avoid regressing the existing userspace, we're still maintaining the
numbering scheme where 0-63 is used for primary, 64-127 is reserved
(formerly for control) and 128-191 is used for render.
For minors >= 192, we're allocating minors dynamically on a first-come,
first-served basis.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 13 +++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 61d24cdcd0f8..3718bd6bbef6 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -106,7 +106,8 @@ static void drm_minor_alloc_release(struct drm_device *dev, 
void *data)
xa_erase(&drm_minors_xa, minor->index);
 }
 
-#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 
63); })
+#define DRM_LEGACY_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 
* _t + 63); })
+#define DRM_MINOR_LIMIT XA_LIMIT(192, (1 << MINORBITS) - 1)
 
 static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
 {
@@ -120,7 +121,15 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
minor->type = type;
minor->dev = dev;
 
-   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_MINOR_LIMIT(type), GFP_KERNEL);
+   /*
+* DRM used to support 64 devices, for backwards compatibility we need 
to maintain the
+* minor allocation scheme where minors 0-63 are primary nodes, 64-127 
are control nodes,
+* and 128-191 are render nodes.
+* After reaching the limit, we're allocating minors dynamically - 
first-come, first-serve.
+*/
+   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_LEGACY_MINOR_LIMIT(type), GFP_KERNEL);
+   if (r == -EBUSY)
+   r = xa_alloc(&drm_minors_xa, &minor->index, NULL, 
DRM_MINOR_LIMIT, GFP_KERNEL);
if (r < 0)
return r;
 
-- 
2.37.3



[PATCH v5 0/3] drm: Use full allocated minor range for DRM

2022-09-11 Thread Michał Winiarski
64 DRM device nodes is not enough for everyone.
Upgrade it to ~512K (which definitely is more than enough).

To allow testing userspace support for >64 devices, add additional DRM
modparam (skip_legacy_minors) which causes DRM to skip allocating minors
in 0-192 range.
Additionally - convert minors to use XArray instead of IDR to simplify the
locking.

v1 -> v2:
Don't touch DRM_MINOR_CONTROL and its range (Simon Ser)

v2 -> v3:
Don't use legacy scheme for >=192 minor range (Dave Airlie)
Add modparam for testing (Dave Airlie)
Add lockdep annotation for IDR (Daniel Vetter)

v3 -> v4:
Convert from IDR to XArray (Matthew Wilcox)

v4 -> v5:
Fixup IDR to XArray conversion (Matthew Wilcox)

Michał Winiarski (3):
  drm: Use XArray instead of IDR for minors
  drm: Expand max DRM device number to full MINORBITS
  drm: Introduce skip_legacy_minors modparam

 drivers/gpu/drm/drm_drv.c | 68 +++
 1 file changed, 34 insertions(+), 34 deletions(-)

-- 
2.37.3



Re: [PATCH v4 1/3] drm: Use XArray instead of IDR for minors

2022-09-11 Thread Michał Winiarski
On Tue, Sep 06, 2022 at 10:02:24PM +0100, Matthew Wilcox wrote:
> On Tue, Sep 06, 2022 at 10:16:27PM +0200, Michał Winiarski wrote:
> > IDR is deprecated, and since XArray manages its own state with internal
> > locking, it simplifies the locking on DRM side.
> > Additionally, don't use the IRQ-safe variant, since operating on drm
> > minor is not done in IRQ context.
> > 
> > Signed-off-by: Michał Winiarski 
> > Suggested-by: Matthew Wilcox 
> 
> I have a few questions, but I like where you're going.
> 
> > @@ -98,21 +98,18 @@ static struct drm_minor **drm_minor_get_slot(struct 
> > drm_device *dev,
> >  static void drm_minor_alloc_release(struct drm_device *dev, void *data)
> >  {
> > struct drm_minor *minor = data;
> > -   unsigned long flags;
> >  
> > WARN_ON(dev != minor->dev);
> >  
> > put_device(minor->kdev);
> >  
> > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > -   idr_remove(&drm_minors_idr, minor->index);
> > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > +   xa_release(&drm_minors_xa, minor->index);
> 
> Has it definitely been unused at this point?  I would think that
> xa_erase() (an unconditional store) would be the correct function to
> call.

Yes, unless there's a programmers error somewhere - I'll replace it though.

> 
> > @@ -122,20 +119,12 @@ static int drm_minor_alloc(struct drm_device *dev, 
> > unsigned int type)
> > minor->type = type;
> > minor->dev = dev;
> >  
> > -   idr_preload(GFP_KERNEL);
> > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > -   r = idr_alloc(&drm_minors_idr,
> > - NULL,
> > - 64 * type,
> > - 64 * (type + 1),
> > - GFP_NOWAIT);
> > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > -   idr_preload_end();
> > -
> > +   r = xa_alloc(&drm_minors_xa, &id, NULL,
> > +XA_LIMIT(64 * type, 64 * (type + 1) - 1), GFP_KERNEL);
> > if (r < 0)
> > return r;
> >  
> > -   minor->index = r;
> > +   minor->index = id;
> 
> Wouldn't it be better to call:
> 
>   r = xa_alloc(&drm_minors_xa, &minor->index, NULL,
>   XA_LIMIT(64 * type, 64 * (type + 1) - 1), GFP_KERNEL);
> 
> I might also prefer a little syntactic sugar like:
> 
> #define DRM_MINOR_LIMIT(type) XA_LIMIT(64 * (type), 64 * (type) + 63)
> 
> but that's definitely a matter of taste.

Sure.

> 
> > @@ -172,9 +161,12 @@ static int drm_minor_register(struct drm_device *dev, 
> > unsigned int type)
> > goto err_debugfs;
> >  
> > /* replace NULL with @minor so lookups will succeed from now on */
> > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > -   idr_replace(&drm_minors_idr, minor, minor->index);
> > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > +   entry = xa_store(&drm_minors_xa, minor->index, &minor, GFP_KERNEL);
> > +   if (xa_is_err(entry)) {
> > +   ret = xa_err(entry);
> > +   goto err_debugfs;
> > +   }
> > +   WARN_ON(entry);
> 
> Might be better as an xa_cmpxchg()?

Ack.

> 
> > @@ -187,16 +179,13 @@ static int drm_minor_register(struct drm_device *dev, 
> > unsigned int type)
> >  static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
> >  {
> > struct drm_minor *minor;
> > -   unsigned long flags;
> >  
> > minor = *drm_minor_get_slot(dev, type);
> > if (!minor || !device_is_registered(minor->kdev))
> > return;
> >  
> > /* replace @minor with NULL so lookups will fail from now on */
> > -   spin_lock_irqsave(&drm_minor_lock, flags);
> > -   idr_replace(&drm_minors_idr, NULL, minor->index);
> > -   spin_unlock_irqrestore(&drm_minor_lock, flags);
> > +   xa_erase(&drm_minors_xa, minor->index);
> 
> This isn't an exact replacement, but I'm not sure whether that makes a
> difference.  xa_erase() allows allocation of this ID again while
> idr_replace() means that lookups return NULL, but the ID remains in
> use.  The equivalent of idr_replace() is:
>   xa_store(&drm_minors_xa, minor->index, NULL, GFP_KERNEL);

It does makes a difference, I'll change it to the equivalent.

> 
> > @@ -215,13 +204,10 @@ static void drm_minor_unregister(struct drm_device 
> > *dev, unsigned int type)
> >  struct drm_minor *drm_minor_acquire(unsigned

[PATCH v4 3/3] drm: Introduce skip_legacy_minors modparam

2022-09-06 Thread Michał Winiarski
While there is support for >64 DRM devices on kernel side, existing
userspace may still have some hardcoded assumptions and it's possible
that it will require changes to be able to use more than 64 devices.
Add a modparam to simplify testing and development of >64 devices
support on userspace side by allocating minors from the >=192 range
(without the need of having >64 physical devices connected).

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 12 +---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 2c6e0b8d3b7a..11c691543fec 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -56,6 +56,11 @@ MODULE_LICENSE("GPL and additional rights");
 
 static DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
+static bool skip_legacy_minors;
+module_param_unsafe(skip_legacy_minors, bool, 0400);
+MODULE_PARM_DESC(skip_legacy_minors,
+"Don't allocate minors in 0-192 range. This can be used for 
testing userspace support for >64 drm devices (default: false)");
+
 /*
  * If the drm core fails to init for whatever reason,
  * we should prevent any drivers from registering with it.
@@ -110,7 +115,7 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 {
struct drm_minor *minor;
u32 id;
-   int r;
+   int r = -EBUSY;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
if (!minor)
@@ -125,8 +130,9 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 * and 128-191 are render nodes.
 * After reaching the limit, we're allocating minors dynamically - 
first-come, first-serve.
 */
-   r = xa_alloc(&drm_minors_xa, &id, NULL,
-XA_LIMIT(64 * type, 64 * (type + 1) - 1), GFP_KERNEL);
+   if (!skip_legacy_minors)
+   r = xa_alloc(&drm_minors_xa, &id, NULL,
+XA_LIMIT(64 * type, 64 * (type + 1) - 1), 
GFP_KERNEL);
if (r == -EBUSY)
r = xa_alloc(&drm_minors_xa, &id, NULL,
 XA_LIMIT(192, (1 << MINORBITS) - 1), GFP_KERNEL);
-- 
2.37.3



[PATCH v4 1/3] drm: Use XArray instead of IDR for minors

2022-09-06 Thread Michał Winiarski
IDR is deprecated, and since XArray manages its own state with internal
locking, it simplifies the locking on DRM side.
Additionally, don't use the IRQ-safe variant, since operating on drm
minor is not done in IRQ context.

Signed-off-by: Michał Winiarski 
Suggested-by: Matthew Wilcox 
---
 drivers/gpu/drm/drm_drv.c | 49 ++-
 1 file changed, 17 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 8214a0b1ab7f..41799e4d0432 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -53,8 +54,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon 
Smirl");
 MODULE_DESCRIPTION("DRM shared core routines");
 MODULE_LICENSE("GPL and additional rights");
 
-static DEFINE_SPINLOCK(drm_minor_lock);
-static struct idr drm_minors_idr;
+static DEFINE_XARRAY_ALLOC(drm_minors_xa);
 
 /*
  * If the drm core fails to init for whatever reason,
@@ -98,21 +98,18 @@ static struct drm_minor **drm_minor_get_slot(struct 
drm_device *dev,
 static void drm_minor_alloc_release(struct drm_device *dev, void *data)
 {
struct drm_minor *minor = data;
-   unsigned long flags;
 
WARN_ON(dev != minor->dev);
 
put_device(minor->kdev);
 
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_remove(&drm_minors_idr, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   xa_release(&drm_minors_xa, minor->index);
 }
 
 static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
+   u32 id;
int r;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
@@ -122,20 +119,12 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
minor->type = type;
minor->dev = dev;
 
-   idr_preload(GFP_KERNEL);
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   r = idr_alloc(&drm_minors_idr,
- NULL,
- 64 * type,
- 64 * (type + 1),
- GFP_NOWAIT);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
-   idr_preload_end();
-
+   r = xa_alloc(&drm_minors_xa, &id, NULL,
+XA_LIMIT(64 * type, 64 * (type + 1) - 1), GFP_KERNEL);
if (r < 0)
return r;
 
-   minor->index = r;
+   minor->index = id;
 
r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
if (r)
@@ -152,7 +141,7 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 static int drm_minor_register(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
+   void *entry;
int ret;
 
DRM_DEBUG("\n");
@@ -172,9 +161,12 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
goto err_debugfs;
 
/* replace NULL with @minor so lookups will succeed from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, minor, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   entry = xa_store(&drm_minors_xa, minor->index, &minor, GFP_KERNEL);
+   if (xa_is_err(entry)) {
+   ret = xa_err(entry);
+   goto err_debugfs;
+   }
+   WARN_ON(entry);
 
DRM_DEBUG("new minor registered %d\n", minor->index);
return 0;
@@ -187,16 +179,13 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
 static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
minor = *drm_minor_get_slot(dev, type);
if (!minor || !device_is_registered(minor->kdev))
return;
 
/* replace @minor with NULL so lookups will fail from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   idr_replace(&drm_minors_idr, NULL, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   xa_erase(&drm_minors_xa, minor->index);
 
device_del(minor->kdev);
dev_set_drvdata(minor->kdev, NULL); /* safety belt */
@@ -215,13 +204,10 @@ static void drm_minor_unregister(struct drm_device *dev, 
unsigned int type)
 struct drm_minor *drm_minor_acquire(unsigned int minor_id)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
-   spin_lock_irqsave(&drm_minor_lock, flags);
-   minor = idr_find(&drm_minors_idr, minor_id);
+   minor = xa_load(&drm_minors_xa, minor_id);
if (minor)
drm_dev_get(minor->dev);
-   spin_unlock_irq

[PATCH v4 2/3] drm: Expand max DRM device number to full MINORBITS

2022-09-06 Thread Michał Winiarski
Having a limit of 64 DRM devices is not good enough for modern world
where we have multi-GPU servers, SR-IOV virtual functions and virtual
devices used for testing.
Let's utilize full minor range for DRM devices.
To avoid regressing the existing userspace, we're still maintaining the
numbering scheme where 0-63 is used for primary, 64-127 is reserved
(formerly for control) and 128-191 is used for render.
For minors >= 192, we're allocating minors dynamically on a first-come,
first-served basis.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 9 +
 1 file changed, 9 insertions(+)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 41799e4d0432..2c6e0b8d3b7a 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -119,8 +119,17 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
minor->type = type;
minor->dev = dev;
 
+   /*
+* DRM used to support 64 devices, for backwards compatibility we need 
to maintain the
+* minor allocation scheme where minors 0-63 are primary nodes, 64-127 
are control nodes,
+* and 128-191 are render nodes.
+* After reaching the limit, we're allocating minors dynamically - 
first-come, first-serve.
+*/
r = xa_alloc(&drm_minors_xa, &id, NULL,
 XA_LIMIT(64 * type, 64 * (type + 1) - 1), GFP_KERNEL);
+   if (r == -EBUSY)
+   r = xa_alloc(&drm_minors_xa, &id, NULL,
+XA_LIMIT(192, (1 << MINORBITS) - 1), GFP_KERNEL);
if (r < 0)
return r;
 
-- 
2.37.3



[PATCH v4 0/3] drm: Use full allocated minor range for DRM

2022-09-06 Thread Michał Winiarski
64 DRM device nodes is not enough for everyone.
Upgrade it to ~512K (which definitely is more than enough).

To allow testing userspace support for >64 devices, add additional DRM
modparam (skip_legacy_minors) which causes DRM to skip allocating minors
in 0-192 range.
Additionally - convert minors to use XArray instead of IDR to simplify the
locking.

v1 -> v2:
Don't touch DRM_MINOR_CONTROL and its range (Simon Ser)

v2 -> v3:
Don't use legacy scheme for >=192 minor range (Dave Airlie)
Add modparam for testing (Dave Airlie)
Add lockdep annotation for IDR (Daniel Vetter)

v3 -> v4:
Convert from IDR to XArray (Matthew Wilcox)

Michał Winiarski (3):
  drm: Use XArray instead of IDR for minors
  drm: Expand max DRM device number to full MINORBITS
  drm: Introduce skip_legacy_minors modparam

 drivers/gpu/drm/drm_drv.c | 66 +++
 1 file changed, 33 insertions(+), 33 deletions(-)

-- 
2.37.3



Re: [PATCH v3 0/4] drm: Use full allocated minor range for DRM

2022-09-06 Thread Michał Winiarski
On Tue, Sep 06, 2022 at 03:21:25PM +0100, Matthew Wilcox wrote:
> On Tue, Sep 06, 2022 at 04:01:13PM +0200, Michał Winiarski wrote:
> > 64 DRM device nodes is not enough for everyone.
> > Upgrade it to ~512K (which definitely is more than enough).
> > 
> > To allow testing userspace support for >64 devices, add additional DRM
> > modparam (skip_legacy_minors) which causes DRM to skip allocating minors
> > in 0-192 range.
> > Additionally - one minor tweak around minor DRM IDR locking and IDR lockdep
> > annotations.
> 
> The IDR is deprecated; rather than making all these changes around
> the IDR, could you convert it to use the XArray instead?  I did it
> once before, but those patches bounced off the submissions process.

Sure. The IDR annotation can still be useful for existing users though,
are you saying I should drop it as well?

-Michał


[PATCH v3 4/4] idr: Add might_alloc() annotation

2022-09-06 Thread Michał Winiarski
Using might_alloc() lets us catch problems in a deterministic manner,
even if we end up not allocating anything.

Signed-off-by: Michał Winiarski 
---
 lib/radix-tree.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index 3c78e1e8b2ad..787ab01001de 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -23,6 +23,7 @@
 #include  /* in_interrupt() */
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -1481,6 +1482,8 @@ void __rcu **idr_get_free(struct radix_tree_root *root,
unsigned long maxindex, start = iter->next_index;
unsigned int shift, offset = 0;
 
+   might_alloc(gfp);
+
  grow:
shift = radix_tree_load_root(root, &child, &maxindex);
if (!radix_tree_tagged(root, IDR_FREE))
-- 
2.37.3



[PATCH v3 3/4] drm: Use mutex for minors

2022-09-06 Thread Michał Winiarski
Operating on drm minor is not done in IRQ context, which means that we
could safely downgrade to regular non-irq spinlock.
But we can also go further and drop the idr_preload tricks by just using
a mutex.

Signed-off-by: Michał Winiarski 
Reviewed-by: Daniel Vetter 
---
 drivers/gpu/drm/drm_drv.c | 33 +
 1 file changed, 13 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 0bdcca0db611..f66904527256 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -53,7 +53,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon 
Smirl");
 MODULE_DESCRIPTION("DRM shared core routines");
 MODULE_LICENSE("GPL and additional rights");
 
-static DEFINE_SPINLOCK(drm_minor_lock);
+static DEFINE_MUTEX(drm_minor_lock);
 static struct idr drm_minors_idr;
 
 static bool skip_legacy_minors;
@@ -103,21 +103,19 @@ static struct drm_minor **drm_minor_get_slot(struct 
drm_device *dev,
 static void drm_minor_alloc_release(struct drm_device *dev, void *data)
 {
struct drm_minor *minor = data;
-   unsigned long flags;
 
WARN_ON(dev != minor->dev);
 
put_device(minor->kdev);
 
-   spin_lock_irqsave(&drm_minor_lock, flags);
+   mutex_lock(&drm_minor_lock);
idr_remove(&drm_minors_idr, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   mutex_unlock(&drm_minor_lock);
 }
 
 static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
int r = -ENOSPC;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
@@ -133,18 +131,16 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
 * and 128-191 are render nodes.
 * After reaching the limit, we're allocating minors dynamically - 
first-come, first-serve.
 */
-   idr_preload(GFP_KERNEL);
-   spin_lock_irqsave(&drm_minor_lock, flags);
+   mutex_lock(&drm_minor_lock);
if (!skip_legacy_minors)
r = idr_alloc(&drm_minors_idr,
  NULL,
  64 * type,
  64 * (type + 1),
- GFP_NOWAIT);
+ GFP_KERNEL);
if (r == -ENOSPC)
-   r = idr_alloc(&drm_minors_idr, NULL, 192, 1 << MINORBITS, 
GFP_NOWAIT);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
-   idr_preload_end();
+   r = idr_alloc(&drm_minors_idr, NULL, 192, 1 << MINORBITS, 
GFP_KERNEL);
+   mutex_unlock(&drm_minor_lock);
 
if (r < 0)
return r;
@@ -166,7 +162,6 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 static int drm_minor_register(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
int ret;
 
DRM_DEBUG("\n");
@@ -186,9 +181,9 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
goto err_debugfs;
 
/* replace NULL with @minor so lookups will succeed from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
+   mutex_lock(&drm_minor_lock);
idr_replace(&drm_minors_idr, minor, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   mutex_unlock(&drm_minor_lock);
 
DRM_DEBUG("new minor registered %d\n", minor->index);
return 0;
@@ -201,16 +196,15 @@ static int drm_minor_register(struct drm_device *dev, 
unsigned int type)
 static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
minor = *drm_minor_get_slot(dev, type);
if (!minor || !device_is_registered(minor->kdev))
return;
 
/* replace @minor with NULL so lookups will fail from now on */
-   spin_lock_irqsave(&drm_minor_lock, flags);
+   mutex_lock(&drm_minor_lock);
idr_replace(&drm_minors_idr, NULL, minor->index);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   mutex_unlock(&drm_minor_lock);
 
device_del(minor->kdev);
dev_set_drvdata(minor->kdev, NULL); /* safety belt */
@@ -229,13 +223,12 @@ static void drm_minor_unregister(struct drm_device *dev, 
unsigned int type)
 struct drm_minor *drm_minor_acquire(unsigned int minor_id)
 {
struct drm_minor *minor;
-   unsigned long flags;
 
-   spin_lock_irqsave(&drm_minor_lock, flags);
+   mutex_lock(&drm_minor_lock);
minor = idr_find(&drm_minors_idr, minor_id);
if (minor)
drm_dev_get(minor->dev);
-   spin_unlock_irqrestore(&drm_minor_lock, flags);
+   mutex_unlock(&drm_minor_lock);
 
if (!minor) {
return ERR_PTR(-ENODEV);
-- 
2.37.3



[PATCH v3 2/4] drm: Introduce skip_legacy_minors modparam

2022-09-06 Thread Michał Winiarski
While there is support for >64 DRM devices on kernel side, existing
userspace may still have some hardcoded assumptions and it's possible
that it will require changes to be able to use more than 64 devices.
Add a modparam to simplify testing and development of >64 devices
support on userspace side by allocating minors from the >=192 range
(without the need of having >64 physical devices connected).

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 18 --
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 9432b1619602..0bdcca0db611 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -56,6 +56,11 @@ MODULE_LICENSE("GPL and additional rights");
 static DEFINE_SPINLOCK(drm_minor_lock);
 static struct idr drm_minors_idr;
 
+static bool skip_legacy_minors;
+module_param_unsafe(skip_legacy_minors, bool, 0400);
+MODULE_PARM_DESC(skip_legacy_minors,
+"Don't allocate minors in 0-192 range. This can be used for 
testing userspace support for >64 drm devices (default: false)");
+
 /*
  * If the drm core fails to init for whatever reason,
  * we should prevent any drivers from registering with it.
@@ -113,7 +118,7 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
 {
struct drm_minor *minor;
unsigned long flags;
-   int r;
+   int r = -ENOSPC;
 
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
if (!minor)
@@ -130,11 +135,12 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
 */
idr_preload(GFP_KERNEL);
spin_lock_irqsave(&drm_minor_lock, flags);
-   r = idr_alloc(&drm_minors_idr,
- NULL,
- 64 * type,
- 64 * (type + 1),
- GFP_NOWAIT);
+   if (!skip_legacy_minors)
+   r = idr_alloc(&drm_minors_idr,
+ NULL,
+ 64 * type,
+ 64 * (type + 1),
+ GFP_NOWAIT);
if (r == -ENOSPC)
r = idr_alloc(&drm_minors_idr, NULL, 192, 1 << MINORBITS, 
GFP_NOWAIT);
spin_unlock_irqrestore(&drm_minor_lock, flags);
-- 
2.37.3



[PATCH v3 1/4] drm: Expand max DRM device number to full MINORBITS

2022-09-06 Thread Michał Winiarski
Having a limit of 64 DRM devices is not good enough for modern world
where we have multi-GPU servers, SR-IOV virtual functions and virtual
devices used for testing.
Let's utilize full minor range for DRM devices.
To avoid regressing the existing userspace, we're still maintaining the
numbering scheme where 0-63 is used for primary, 64-127 is reserved
(formerly for control) and 128-191 is used for render.
For minors >= 192, we're allocating minors dynamically on a first-come,
first-served basis.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/drm_drv.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 8214a0b1ab7f..9432b1619602 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -122,6 +122,12 @@ static int drm_minor_alloc(struct drm_device *dev, 
unsigned int type)
minor->type = type;
minor->dev = dev;
 
+   /*
+* DRM used to support 64 devices, for backwards compatibility we need 
to maintain the
+* minor allocation scheme where minors 0-63 are primary nodes, 64-127 
are control nodes,
+* and 128-191 are render nodes.
+* After reaching the limit, we're allocating minors dynamically - 
first-come, first-serve.
+*/
idr_preload(GFP_KERNEL);
spin_lock_irqsave(&drm_minor_lock, flags);
r = idr_alloc(&drm_minors_idr,
@@ -129,6 +135,8 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned 
int type)
  64 * type,
  64 * (type + 1),
  GFP_NOWAIT);
+   if (r == -ENOSPC)
+   r = idr_alloc(&drm_minors_idr, NULL, 192, 1 << MINORBITS, 
GFP_NOWAIT);
spin_unlock_irqrestore(&drm_minor_lock, flags);
idr_preload_end();
 
-- 
2.37.3



[PATCH v3 0/4] drm: Use full allocated minor range for DRM

2022-09-06 Thread Michał Winiarski
64 DRM device nodes is not enough for everyone.
Upgrade it to ~512K (which definitely is more than enough).

To allow testing userspace support for >64 devices, add additional DRM
modparam (skip_legacy_minors) which causes DRM to skip allocating minors
in 0-192 range.
Additionally - one minor tweak around minor DRM IDR locking and IDR lockdep
annotations.

v1 -> v2:
Don't touch DRM_MINOR_CONTROL and its range (Simon Ser)

v2 -> v3:
Don't use legacy scheme for >=192 minor range (Dave Airlie)
Add modparam for testing (Dave Airlie)
Add lockdep annotation for IDR (Daniel Vetter)

Michał Winiarski (4):
  drm: Expand max DRM device number to full MINORBITS
  drm: Introduce skip_legacy_minors modparam
  drm: Use mutex for minors
  idr: Add might_alloc() annotation

 drivers/gpu/drm/drm_drv.c | 55 ++-
 lib/radix-tree.c  |  3 +++
 2 files changed, 34 insertions(+), 24 deletions(-)

-- 
2.37.3



Re: [PATCH] drm/doc: Custom Kconfig for KUnit is no longer needed

2022-09-06 Thread Michał Winiarski
On Tue, Sep 06, 2022 at 08:37:00AM +0700, Bagas Sanjaya wrote:
> On 9/6/22 01:47, Michał Winiarski wrote:
> > References: commit 6fc3a8636a7b ("kunit: tool: Enable virtio/PCI by default 
> > on UML")
> 
> Use Fixes: tag for bugfix patches instead.

Can documentation update (when the referenced patch didn't touch the docs)
really be treated as a bugfix?
Or is it just a reference, validating the reasoning behind this patch?

-Michał

> 
> -- 
> An old man doll... just what I always wanted! - Clara


[PATCH] drm/doc: Custom Kconfig for KUnit is no longer needed

2022-09-05 Thread Michał Winiarski
When built for UML, KUnit provides virtio/PCI, which means that the
DMA/IOMEM UML emulation needed by DRM is already present and does not
need to be manually added with --kconfig_add.

References: commit 6fc3a8636a7b ("kunit: tool: Enable virtio/PCI by default on 
UML")
Signed-off-by: Michał Winiarski 
---
 Documentation/gpu/drm-internals.rst | 7 +--
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/Documentation/gpu/drm-internals.rst 
b/Documentation/gpu/drm-internals.rst
index 5fd20a306718..c264a9587d21 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -228,16 +228,11 @@ follows:
 
 .. code-block:: bash
 
-   $ ./tools/testing/kunit/kunit.py run 
--kunitconfig=drivers/gpu/drm/tests \
-   --kconfig_add CONFIG_VIRTIO_UML=y \
-   --kconfig_add CONFIG_UML_PCI_OVER_VIRTIO=y
+   $ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/gpu/drm/tests
 
 .. note::
The configuration included in ``.kunitconfig`` should be as generic as
possible.
-   ``CONFIG_VIRTIO_UML`` and ``CONFIG_UML_PCI_OVER_VIRTIO`` are not
-   included in it because they are only required for User Mode Linux.
-
 
 Legacy Support Code
 ===
-- 
2.37.3



[PATCH v3 2/2] drm/plane_helper: Split into parameterized test cases

2022-09-05 Thread Michał Winiarski
The test was constructed as a single function (test case) which checks
multiple conditions, calling the function that is tested multiple times
with different arguments.
This usually means that it can be easily converted into multiple test
cases.
Split igt_check_plane_state into two parameterized test cases,
drm_check_plane_state and drm_check_invalid_plane_state.

Passing output:

== drm_plane_helper (2 subtests) ===
== drm_check_plane_state ===
[PASSED] clipping_simple
[PASSED] clipping_rotate_reflect
[PASSED] positioning_simple
[PASSED] upscaling
[PASSED] downscaling
[PASSED] rounding1
[PASSED] rounding2
[PASSED] rounding3
[PASSED] rounding4
== [PASSED] drm_check_plane_state ==
== drm_check_invalid_plane_state ===
[PASSED] positioning_invalid
[PASSED] upscaling_invalid
[PASSED] downscaling_invalid
== [PASSED] drm_check_invalid_plane_state ==
 [PASSED] drm_plane_helper =

Testing complete. Ran 12 tests: passed: 12

v2: Add missing EXPECT/ASSERT (Maíra)
v3: Use single EXPECT insted of condition + KUNIT_FAILURE (Maíra)

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_plane_helper_test.c | 466 ++
 1 file changed, 268 insertions(+), 198 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_plane_helper_test.c 
b/drivers/gpu/drm/tests/drm_plane_helper_test.c
index 0bbd42d2d37b..01b76f1d93f4 100644
--- a/drivers/gpu/drm/tests/drm_plane_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_plane_helper_test.c
@@ -12,233 +12,303 @@
 #include 
 #include 
 
-static void set_src(struct drm_plane_state *plane_state,
-   unsigned int src_x, unsigned int src_y,
-   unsigned int src_w, unsigned int src_h)
+static const struct drm_crtc_state crtc_state = {
+   .crtc = ZERO_SIZE_PTR,
+   .enable = true,
+   .active = true,
+   .mode = {
+   DRM_MODE("1024x768", 0, 65000, 1024, 1048,
+1184, 1344, 0, 768, 771, 777, 806, 0,
+DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
+   },
+};
+
+struct drm_check_plane_state_test {
+   const char *name;
+   const char *msg;
+   struct {
+   unsigned int x;
+   unsigned int y;
+   unsigned int w;
+   unsigned int h;
+   } src, src_expected;
+   struct {
+   int x;
+   int y;
+   unsigned int w;
+   unsigned int h;
+   } crtc, crtc_expected;
+   unsigned int rotation;
+   int min_scale;
+   int max_scale;
+   bool can_position;
+};
+
+static int drm_plane_helper_init(struct kunit *test)
 {
-   plane_state->src_x = src_x;
-   plane_state->src_y = src_y;
-   plane_state->src_w = src_w;
-   plane_state->src_h = src_h;
+   const struct drm_check_plane_state_test *params = test->param_value;
+   struct drm_plane *plane;
+   struct drm_framebuffer *fb;
+   struct drm_plane_state *mock;
+
+   plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_NULL(test, plane);
+
+   fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_NULL(test, fb);
+   fb->width = 2048;
+   fb->height = 2048;
+
+   mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_NULL(test, mock);
+   mock->plane = plane;
+   mock->crtc = ZERO_SIZE_PTR;
+   mock->fb = fb;
+   mock->rotation = params->rotation;
+   mock->src_x = params->src.x;
+   mock->src_y = params->src.y;
+   mock->src_w = params->src.w;
+   mock->src_h = params->src.h;
+   mock->crtc_x = params->crtc.x;
+   mock->crtc_y = params->crtc.y;
+   mock->crtc_w = params->crtc.w;
+   mock->crtc_h = params->crtc.h;
+
+   test->priv = mock;
+
+   return 0;
 }
 
-static bool check_src_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
+static void check_src_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
 unsigned int src_x, unsigned int src_y,
 unsigned int src_w, unsigned int src_h)
 {
struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
 
-   if (plane_state->src.x1 < 0) {
-   kunit_err(test,
- "src x coordinate %x should never be below 0, src: " 
DRM_RECT_FP_FMT,
- plane_state->src.x1, 
DRM_RECT_FP_ARG(&plane_state->src));
-   return false;
-   }
-   if (plane_state->src.y1 < 0) {
-   kunit_err(test,
- "

[PATCH v3 1/2] drm/plane_helper: Print actual/expected values on failure

2022-09-05 Thread Michał Winiarski
Currently the values are printed with debug log level.
Adjust the log level and link the output with the test by using kunit_err.

Example output:
foo: dst: 20x20+10+10, expected: 10x10+0+0
foo: EXPECTATION FAILED at drivers/gpu/drm/tests/drm_plane_helper_test.c:85

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_plane_helper_test.c | 78 +++
 1 file changed, 44 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_plane_helper_test.c 
b/drivers/gpu/drm/tests/drm_plane_helper_test.c
index be6cff0020ed..0bbd42d2d37b 100644
--- a/drivers/gpu/drm/tests/drm_plane_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_plane_helper_test.c
@@ -10,6 +10,7 @@
 #include 
 #include 
 #include 
+#include 
 
 static void set_src(struct drm_plane_state *plane_state,
unsigned int src_x, unsigned int src_y,
@@ -21,26 +22,32 @@ static void set_src(struct drm_plane_state *plane_state,
plane_state->src_h = src_h;
 }
 
-static bool check_src_eq(struct drm_plane_state *plane_state,
+static bool check_src_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
 unsigned int src_x, unsigned int src_y,
 unsigned int src_w, unsigned int src_h)
 {
+   struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
+
if (plane_state->src.x1 < 0) {
-   pr_err("src x coordinate %x should never be below 0.\n", 
plane_state->src.x1);
-   drm_rect_debug_print("src: ", &plane_state->src, true);
+   kunit_err(test,
+ "src x coordinate %x should never be below 0, src: " 
DRM_RECT_FP_FMT,
+ plane_state->src.x1, 
DRM_RECT_FP_ARG(&plane_state->src));
return false;
}
if (plane_state->src.y1 < 0) {
-   pr_err("src y coordinate %x should never be below 0.\n", 
plane_state->src.y1);
-   drm_rect_debug_print("src: ", &plane_state->src, true);
+   kunit_err(test,
+ "src y coordinate %x should never be below 0, src: " 
DRM_RECT_FP_FMT,
+ plane_state->src.y1, 
DRM_RECT_FP_ARG(&plane_state->src));
return false;
}
 
-   if (plane_state->src.x1 != src_x ||
-   plane_state->src.y1 != src_y ||
-   drm_rect_width(&plane_state->src) != src_w ||
-   drm_rect_height(&plane_state->src) != src_h) {
-   drm_rect_debug_print("src: ", &plane_state->src, true);
+   if (plane_state->src.x1 != expected.x1 ||
+   plane_state->src.y1 != expected.y1 ||
+   drm_rect_width(&plane_state->src) != drm_rect_width(&expected) ||
+   drm_rect_height(&plane_state->src) != drm_rect_height(&expected)) {
+   kunit_err(test, "src: " DRM_RECT_FP_FMT ", expected: " 
DRM_RECT_FP_FMT,
+ DRM_RECT_FP_ARG(&plane_state->src), 
DRM_RECT_FP_ARG(&expected));
+
return false;
}
 
@@ -57,15 +64,18 @@ static void set_crtc(struct drm_plane_state *plane_state,
plane_state->crtc_h = crtc_h;
 }
 
-static bool check_crtc_eq(struct drm_plane_state *plane_state,
+static bool check_crtc_eq(struct kunit *test, struct drm_plane_state 
*plane_state,
  int crtc_x, int crtc_y,
  unsigned int crtc_w, unsigned int crtc_h)
 {
-   if (plane_state->dst.x1 != crtc_x ||
-   plane_state->dst.y1 != crtc_y ||
-   drm_rect_width(&plane_state->dst) != crtc_w ||
-   drm_rect_height(&plane_state->dst) != crtc_h) {
-   drm_rect_debug_print("dst: ", &plane_state->dst, false);
+   struct drm_rect expected = DRM_RECT_INIT(crtc_x, crtc_y, crtc_w, 
crtc_h);
+
+   if (plane_state->dst.x1 != expected.x1 ||
+   plane_state->dst.y1 != expected.y1 ||
+   drm_rect_width(&plane_state->dst) != drm_rect_width(&expected) ||
+   drm_rect_height(&plane_state->dst) != drm_rect_height(&expected)) {
+   kunit_err(test, "dst: " DRM_RECT_FMT ", expected: " 
DRM_RECT_FMT,
+ DRM_RECT_ARG(&plane_state->dst), 
DRM_RECT_ARG(&expected));
 
return false;
}
@@ -109,8 +119,8 @@ static void igt_check_plane_state(struct kunit *test)
  false, false);
KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Simple clipping check should 
pass\n");
KUNIT_EXPECT_TRUE(test, plane_state.visible);
-   KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 1024 << 16, 
768 << 16));
-   KUNIT_EXPECT_TRUE(test, ch

Re: [PATCH v2 2/2] drm/tests: Change "igt_" prefix to "test_drm_"

2022-09-05 Thread Michał Winiarski
On Mon, Sep 05, 2022 at 02:10:00PM +0200, Maxime Ripard wrote:
> On Fri, Sep 02, 2022 at 03:38:28PM +0200, Michał Winiarski wrote:
> > On Fri, Sep 02, 2022 at 04:03:20PM +0300, Jani Nikula wrote:
> > > On Fri, 02 Sep 2022, Maxime Ripard  wrote:
> > > > On Fri, Sep 02, 2022 at 11:04:14AM +0300, Jani Nikula wrote:
> > > >> On Thu, 01 Sep 2022, Maíra Canal  wrote:
> > > >> > Hi Maxime,
> > > >> >
> > > >> > On 9/1/22 09:55, Maxime Ripard wrote:
> > > >> >> Hi,
> > > >> >> 
> > > >> >> On Thu, Sep 01, 2022 at 09:42:10AM -0300, Maíra Canal wrote:
> > > >> >>> With the introduction of KUnit, IGT is no longer the only option 
> > > >> >>> to run
> > > >> >>> the DRM unit tests, as the tests can be run through kunit-tool or 
> > > >> >>> on
> > > >> >>> real hardware with CONFIG_KUNIT.
> > > >> >>>
> > > >> >>> Therefore, remove the "igt_" prefix from the tests and replace it 
> > > >> >>> with
> > > >> >>> the "test_drm_" prefix, making the tests' names independent from 
> > > >> >>> the tool
> > > >> >>> used.
> > > >> >>>
> > > >> >>> Signed-off-by: Maíra Canal 
> > > >> >>>
> > > >> >>> ---
> > > >> >>> v1 -> v2: 
> > > >> >>> https://lore.kernel.org/dri-devel/20220830211603.191734-1-mairaca...@riseup.net/
> > > >> >>> - Change "drm_" prefix to "test_drm_", as "drm_" can be a bit 
> > > >> >>> confusing (Jani Nikula).
> > > >> >> 
> > > >> >> I appreciate it's a bit of a bikeshed but I disagree with this. The
> > > >> >> majority of the kunit tests already out there start with the 
> > > >> >> framework
> > > >> >> name, including *all* the examples in the kunit doc. Plus, it's 
> > > >> >> fairly
> > > >> >> obvious that it's a test, kunit is only about running tests in the 
> > > >> >> first
> > > >> >> place.
> > > >> >
> > > >> > Would it be better to keep it as "drm_"?
> > > >> 
> > > >> That's not "keeping". That's renaming igt to drm.
> > > >
> > > > Well, there's like half the tests that are prefixed with drm, the other
> > > > with igt, so it's both really
> > > >
> > > >> > Currently, I don't think it is appropriate to hold the "igt_" 
> > > >> > prefix, as
> > > >> > the tests are not IGT exclusive, but I don't have a strong opinion on
> > > >> > using the "drm_" or the "test_drm" prefixes.
> > > >> 
> > > >> I repeat my stance that "drm_" alone is confusing.
> > > >
> > > > What are you confusing it with?
> > > >
> > > >> For the reason alone that it pollutes the code tagging tools, mixing
> > > >> actual drm_ types and functions with unit test functions.
> > > >
> > > > I don't get it, I'm sorry. All these functions are static and not part
> > > > of any API, so I can't see how it would pollute a code tagging tool. Or
> > > > at least, not more than any driver does.
> > > >
> > > > And we're part of a larger project here, it's about consistency with the
> > > > rest of the ecosystem.
> > > 
> > > Okay, so I'm just going to explain what I mean, but say "whatever" right
> > > after and move on.
> > > 
> > > For example, drm_buddy_test.c includes drm_buddy.h so with the igt_ ->
> > > drm_ rename none of the test functions may clash with the drm_buddy_
> > > prefixed existing functions. Ditto for all tests similarly.
> > > 
> > > For example drm_buddy_alloc_range() as a name sounds like something that
> > > allocs a range, not something that tests range allocation. On the other
> > > hand, you have drm_buddy_alloc_blocks() which is actually a real
> > > drm_buddy function, not a test. What would you call a test that tests
> > > that

Re: [PATCH v2 2/2] drm/plane_helper: Split into parameterized test cases

2022-09-02 Thread Michał Winiarski
On Thu, Sep 01, 2022 at 09:20:55AM -0300, Maíra Canal wrote:
> Hi Michał
> 
> On 8/31/22 17:45, Michał Winiarski wrote:
> > The test was constructed as a single function (test case) which checks
> > multiple conditions, calling the function that is tested multiple times
> > with different arguments.
> > This usually means that it can be easily converted into multiple test
> > cases.
> > Split igt_check_plane_state into two parameterized test cases,
> > drm_check_plane_state and drm_check_invalid_plane_state.
> > 
> > Passing output:
> > 
> > == drm_plane_helper (2 subtests) ===
> > == drm_check_plane_state ===
> > [PASSED] clipping_simple
> > [PASSED] clipping_rotate_reflect
> > [PASSED] positioning_simple
> > [PASSED] upscaling
> > [PASSED] downscaling
> > [PASSED] rounding1
> > [PASSED] rounding2
> > [PASSED] rounding3
> > [PASSED] rounding4
> > == [PASSED] drm_check_plane_state ==
> > == drm_check_invalid_plane_state ===
> > [PASSED] positioning_invalid
> > [PASSED] upscaling_invalid
> > [PASSED] downscaling_invalid
> > == [PASSED] drm_check_invalid_plane_state ==
> >  [PASSED] drm_plane_helper =
> > ====
> > Testing complete. Ran 12 tests: passed: 12
> > 
> > v2: Add missing EXPECT/ASSERT (Maíra)
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  drivers/gpu/drm/tests/drm_plane_helper_test.c | 456 ++
> >  1 file changed, 267 insertions(+), 189 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/tests/drm_plane_helper_test.c 
> > b/drivers/gpu/drm/tests/drm_plane_helper_test.c
> > index 0bbd42d2d37b..505173b019d7 100644
> > --- a/drivers/gpu/drm/tests/drm_plane_helper_test.c
> > +++ b/drivers/gpu/drm/tests/drm_plane_helper_test.c
> > @@ -12,59 +12,97 @@
> >  #include 
> >  #include 
> >  
> > -static void set_src(struct drm_plane_state *plane_state,
> > -   unsigned int src_x, unsigned int src_y,
> > -   unsigned int src_w, unsigned int src_h)
> > +static const struct drm_crtc_state crtc_state = {
> > +   .crtc = ZERO_SIZE_PTR,
> > +   .enable = true,
> > +   .active = true,
> > +   .mode = {
> > +   DRM_MODE("1024x768", 0, 65000, 1024, 1048,
> > +1184, 1344, 0, 768, 771, 777, 806, 0,
> > +DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
> > +   },
> > +};
> > +
> > +struct drm_check_plane_state_test {
> > +   const char *name;
> > +   const char *msg;
> > +   struct {
> > +   unsigned int x;
> > +   unsigned int y;
> > +   unsigned int w;
> > +   unsigned int h;
> > +   } src, src_expected;
> > +   struct {
> > +   int x;
> > +   int y;
> > +   unsigned int w;
> > +   unsigned int h;
> > +   } crtc, crtc_expected;
> > +   unsigned int rotation;
> > +   int min_scale;
> > +   int max_scale;
> > +   bool can_position;
> > +};
> > +
> > +static int drm_plane_helper_init(struct kunit *test)
> >  {
> > -   plane_state->src_x = src_x;
> > -   plane_state->src_y = src_y;
> > -   plane_state->src_w = src_w;
> > -   plane_state->src_h = src_h;
> > +   const struct drm_check_plane_state_test *params = test->param_value;
> > +   struct drm_plane *plane;
> > +   struct drm_framebuffer *fb;
> > +   struct drm_plane_state *mock;
> > +
> > +   plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
> > +   KUNIT_ASSERT_NOT_NULL(test, plane);
> > +
> > +   fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
> > +   KUNIT_ASSERT_NOT_NULL(test, fb);
> > +   fb->width = 2048;
> > +   fb->height = 2048;
> > +
> > +   mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
> > +   KUNIT_ASSERT_NOT_NULL(test, mock);
> > +   mock->plane = plane;
> > +   mock->crtc = ZERO_SIZE_PTR;
> > +   mock->fb = fb;
> > +   mock->rotation = params->rotation;
> > +   mock->src_x = params->src.x;
> > +   mock->src_y = params->src.y;
> > +   mock->src_w = params->src.w;
> > +   mock->src_h = params->src.h;
> > +   mock->crtc_x = params->crtc.x;
> 

Re: [PATCH 2/2] drm/format: Split into more granular test cases

2022-09-02 Thread Michał Winiarski
On Wed, Aug 31, 2022 at 07:15:11PM -0300, Maíra Canal wrote:
> Hi Michał
> 
> Some very minor nits inline, but either way:
> 
> Reviewed-by: Maíra Canal 
> 
> On 8/31/22 18:56, Michał Winiarski wrote:
> > While we have multiple test cases, most of them check multiple
> > conditions, calling the function that is tested multiple times with
> > different arguments (with comments that indicate test case boundary).
> > This usually means that it can be easily converted into multiple test
> > cases.
> > 
> > Passing output:
> > 
> > = drm_format (18 subtests) =
> > [PASSED] drm_format_block_width_invalid
> > [PASSED] drm_format_block_width_one_plane
> > [PASSED] drm_format_block_width_two_plane
> > [PASSED] drm_format_block_width_three_plane
> > [PASSED] drm_format_block_width_tiled
> > [PASSED] drm_format_block_height_invalid
> > [PASSED] drm_format_block_height_one_plane
> > [PASSED] drm_format_block_height_two_plane
> > [PASSED] drm_format_block_height_three_plane
> > [PASSED] drm_format_block_height_tiled
> > [PASSED] drm_format_min_pitch_invalid
> > [PASSED] drm_format_min_pitch_one_plane_8bpp
> > [PASSED] drm_format_min_pitch_one_plane_16bpp
> > [PASSED] drm_format_min_pitch_one_plane_24bpp
> > [PASSED] drm_format_min_pitch_one_plane_32bpp
> > [PASSED] drm_format_min_pitch_two_plane
> > [PASSED] drm_format_min_pitch_three_plane_8bpp
> > [PASSED] drm_format_min_pitch_tiled
> 
> As Jani pointed out in [1], "drm_" prefix can be a bit confusing. I will
> send a patch tomorrow using the prefix "test_drm_" on all tests to make the
> naming more consistent. It would be nice if this patch already hold the new
> naming, but anyway I can send a patch changing it later with the new prefix
> gets approved.
> 
> [1] 
> https://lore.kernel.org/dri-devel/20220831104941.doc75juindcm5...@nostramo.hardline.pl/T/#m82b4e710063b47029a8bd4716d137e575640da9a

Sure - I can resend with different naming convention if needed.

> 
> > === [PASSED] drm_format 
> > 
> > Testing complete. Ran 18 tests: passed: 18
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >   drivers/gpu/drm/tests/drm_format_test.c | 156 
> >   1 file changed, 108 insertions(+), 48 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/tests/drm_format_test.c 
> > b/drivers/gpu/drm/tests/drm_format_test.c
> > index 0efa88bf56a9..1936d2d59908 100644
> > --- a/drivers/gpu/drm/tests/drm_format_test.c
> > +++ b/drivers/gpu/drm/tests/drm_format_test.c
> > @@ -9,100 +9,133 @@
> >   #include 
> > -static void igt_check_drm_format_block_width(struct kunit *test)
> > +static void drm_format_block_width_invalid(struct kunit *test)
> >   {
> > const struct drm_format_info *info = NULL;
> > -   /* Test invalid arguments */
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 0);
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
> > +}
> > +
> > +static void drm_format_block_width_one_plane(struct kunit *test)
> > +{
> > +   const struct drm_format_info *info = 
> > drm_format_info(DRM_FORMAT_XRGB);
> > -   /* Test 1 plane format */
> > -   info = drm_format_info(DRM_FORMAT_XRGB);
> > KUNIT_ASSERT_NOT_NULL(test, info);
> > +
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
> > +}
> > +
> > +static void drm_format_block_width_two_plane(struct kunit *test)
> 
> s/plane/planes

NV12 format has two planes, therefore it's a two-plane format.

-Michał

> 
> Best Regards,
> - Maíra Canal
> 
> > +{
> > +   const struct drm_format_info *info = drm_format_info(DRM_FORMAT_NV12);
> > -   /* Test 2 planes format */
> > -   info = drm_format_info(DRM_FORMAT_NV12);
> > KUNIT_ASSERT_NOT_NULL(test, info);
> > +
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 1);
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 2), 0);
> > KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
> > +}
> > +
> > +s

Re: [PATCH v2 2/2] drm/tests: Change "igt_" prefix to "test_drm_"

2022-09-02 Thread Michał Winiarski
On Fri, Sep 02, 2022 at 04:03:20PM +0300, Jani Nikula wrote:
> On Fri, 02 Sep 2022, Maxime Ripard  wrote:
> > On Fri, Sep 02, 2022 at 11:04:14AM +0300, Jani Nikula wrote:
> >> On Thu, 01 Sep 2022, Maíra Canal  wrote:
> >> > Hi Maxime,
> >> >
> >> > On 9/1/22 09:55, Maxime Ripard wrote:
> >> >> Hi,
> >> >> 
> >> >> On Thu, Sep 01, 2022 at 09:42:10AM -0300, Maíra Canal wrote:
> >> >>> With the introduction of KUnit, IGT is no longer the only option to run
> >> >>> the DRM unit tests, as the tests can be run through kunit-tool or on
> >> >>> real hardware with CONFIG_KUNIT.
> >> >>>
> >> >>> Therefore, remove the "igt_" prefix from the tests and replace it with
> >> >>> the "test_drm_" prefix, making the tests' names independent from the 
> >> >>> tool
> >> >>> used.
> >> >>>
> >> >>> Signed-off-by: Maíra Canal 
> >> >>>
> >> >>> ---
> >> >>> v1 -> v2: 
> >> >>> https://lore.kernel.org/dri-devel/20220830211603.191734-1-mairaca...@riseup.net/
> >> >>> - Change "drm_" prefix to "test_drm_", as "drm_" can be a bit 
> >> >>> confusing (Jani Nikula).
> >> >> 
> >> >> I appreciate it's a bit of a bikeshed but I disagree with this. The
> >> >> majority of the kunit tests already out there start with the framework
> >> >> name, including *all* the examples in the kunit doc. Plus, it's fairly
> >> >> obvious that it's a test, kunit is only about running tests in the first
> >> >> place.
> >> >
> >> > Would it be better to keep it as "drm_"?
> >> 
> >> That's not "keeping". That's renaming igt to drm.
> >
> > Well, there's like half the tests that are prefixed with drm, the other
> > with igt, so it's both really
> >
> >> > Currently, I don't think it is appropriate to hold the "igt_" prefix, as
> >> > the tests are not IGT exclusive, but I don't have a strong opinion on
> >> > using the "drm_" or the "test_drm" prefixes.
> >> 
> >> I repeat my stance that "drm_" alone is confusing.
> >
> > What are you confusing it with?
> >
> >> For the reason alone that it pollutes the code tagging tools, mixing
> >> actual drm_ types and functions with unit test functions.
> >
> > I don't get it, I'm sorry. All these functions are static and not part
> > of any API, so I can't see how it would pollute a code tagging tool. Or
> > at least, not more than any driver does.
> >
> > And we're part of a larger project here, it's about consistency with the
> > rest of the ecosystem.
> 
> Okay, so I'm just going to explain what I mean, but say "whatever" right
> after and move on.
> 
> For example, drm_buddy_test.c includes drm_buddy.h so with the igt_ ->
> drm_ rename none of the test functions may clash with the drm_buddy_
> prefixed existing functions. Ditto for all tests similarly.
> 
> For example drm_buddy_alloc_range() as a name sounds like something that
> allocs a range, not something that tests range allocation. On the other
> hand, you have drm_buddy_alloc_blocks() which is actually a real
> drm_buddy function, not a test. What would you call a test that tests
> that? Here, we end up with names that are all prefixed drm_buddy and you
> won't know what's the actual function and what's the test unless you
> look it up.
> 
> I use code tagging that I can use for finding and completing
> e.g. functions. Currently I have the following completions, for
> igt_buddy_ and drm_buddy_, respectively:
> 
> Possible completions are:
> igt_buddy_alloc_limit igt_buddy_alloc_optimistic  
> igt_buddy_alloc_pathological
> igt_buddy_alloc_pessimistic   igt_buddy_alloc_range   igt_buddy_alloc_smoke
> 
> Possible completions are:
> drm_buddy_alloc_blocksdrm_buddy_block 
> drm_buddy_block_is_allocateddrm_buddy_block_is_free
> drm_buddy_block_is_split  drm_buddy_block_offset  drm_buddy_block_order   
> drm_buddy_block_print
> drm_buddy_block_size  drm_buddy_block_state   drm_buddy_block_trim
> drm_buddy_fini
> drm_buddy_free_block  drm_buddy_free_list drm_buddy_init  
> drm_buddy_init_test
> drm_buddy_module_exit drm_buddy_module_init   drm_buddy_print
> 
> With the patch at hand, they'll all be lumped under drm_buddy_
> completions, and some of them will be actual drm buddy functions and
> some not.
> 
> I just find it a very odd convention to name the tests in a way that's
> indistinguishable from the real things. Even *within* drm_buddy_test.c
> where you read the test code. Because currently you do have calls to
> igt_buddy_ prefixed functions from other igt_buddy_ prefixed functions,
> along with the drm_buddy_ prefixed calls. I think it's just going to be
> a mess.
> 
> /rant
> 
> Whatever. Moving on.

KUnit docs [1] state:
https://docs.kernel.org/dev-tools/kunit/style.html#test-cases
"As tests are themselves functions, their names cannot conflict with other
C identifiers in the kernel. This may require some creative naming."
And give examples of names. But this should be local to individual test suite -
as long as the test is readable, and the name describes what it is testing, we
should be fine

Re: [PATCH] drm: Simplify testing on UML with kunit.py

2022-09-01 Thread Michał Winiarski
On Thu, Sep 01, 2022 at 04:02:53PM +0200, Maxime Ripard wrote:
> Hi,
> 
> On Thu, Sep 01, 2022 at 03:36:21PM +0200, Michał Winiarski wrote:
> > DRM depends on IOMEM and DMA, introduce an additional Kconfig to pull in
> > IOMEM and DMA emulation on UML.
> > Since --kconfig_add usage is no longer needed, remove it from
> > documentation.
> > 
> > Signed-off-by: Michał Winiarski 
> > ---
> >  Documentation/gpu/drm-internals.rst | 7 +--
> >  drivers/video/Kconfig   | 4 
> >  2 files changed, 5 insertions(+), 6 deletions(-)
> > 
> > diff --git a/Documentation/gpu/drm-internals.rst 
> > b/Documentation/gpu/drm-internals.rst
> > index 5fd20a306718..c264a9587d21 100644
> > --- a/Documentation/gpu/drm-internals.rst
> > +++ b/Documentation/gpu/drm-internals.rst
> > @@ -228,16 +228,11 @@ follows:
> >  
> >  .. code-block:: bash
> >  
> > -   $ ./tools/testing/kunit/kunit.py run 
> > --kunitconfig=drivers/gpu/drm/tests \
> > -   --kconfig_add CONFIG_VIRTIO_UML=y \
> > -   --kconfig_add CONFIG_UML_PCI_OVER_VIRTIO=y
> > +   $ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/gpu/drm/tests
> >  
> >  .. note::
> > The configuration included in ``.kunitconfig`` should be as generic as
> > possible.
> > -   ``CONFIG_VIRTIO_UML`` and ``CONFIG_UML_PCI_OVER_VIRTIO`` are not
> > -   included in it because they are only required for User Mode Linux.
> > -
> 
> I'm all for removing that part of the documentation, but because
> 6fc3a8636a7b, in 6.0 should address this entirely? Why would we need
> that other symbol?

We don't. I wasn't aware that 6fc3a8636a7b exists and had this one in my tree
prior to 6.0.
(well... technically we do need those other symbols and don't need VIRTIO, but
since CONFIG_UML_PCI_OVER_VIRTIO pull in those two, it also works).
Since docs were not updated, I didn't see any conflicts when rebasing.

Sorry for the noise, please ignore this patch.

-Michał

> 
> Maxime




[PATCH] drm: Simplify testing on UML with kunit.py

2022-09-01 Thread Michał Winiarski
DRM depends on IOMEM and DMA, introduce an additional Kconfig to pull in
IOMEM and DMA emulation on UML.
Since --kconfig_add usage is no longer needed, remove it from
documentation.

Signed-off-by: Michał Winiarski 
---
 Documentation/gpu/drm-internals.rst | 7 +--
 drivers/video/Kconfig   | 4 
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/Documentation/gpu/drm-internals.rst 
b/Documentation/gpu/drm-internals.rst
index 5fd20a306718..c264a9587d21 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -228,16 +228,11 @@ follows:
 
 .. code-block:: bash
 
-   $ ./tools/testing/kunit/kunit.py run 
--kunitconfig=drivers/gpu/drm/tests \
-   --kconfig_add CONFIG_VIRTIO_UML=y \
-   --kconfig_add CONFIG_UML_PCI_OVER_VIRTIO=y
+   $ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/gpu/drm/tests
 
 .. note::
The configuration included in ``.kunitconfig`` should be as generic as
possible.
-   ``CONFIG_VIRTIO_UML`` and ``CONFIG_UML_PCI_OVER_VIRTIO`` are not
-   included in it because they are only required for User Mode Linux.
-
 
 Legacy Support Code
 ===
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0587e21abad9..f3266c9fa8a6 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -52,5 +52,9 @@ if FB || SGI_NEWPORT_CONSOLE
 
 endif
 
+config DRM_UML_IO_EMULATION
+   def_bool y if UML && KUNIT
+   select UML_DMA_EMULATION
+   select UML_IOMEM_EMULATION
 
 endmenu
-- 
2.37.3



[PATCH 2/2] drm/format: Split into more granular test cases

2022-08-31 Thread Michał Winiarski
While we have multiple test cases, most of them check multiple
conditions, calling the function that is tested multiple times with
different arguments (with comments that indicate test case boundary).
This usually means that it can be easily converted into multiple test
cases.

Passing output:

= drm_format (18 subtests) =
[PASSED] drm_format_block_width_invalid
[PASSED] drm_format_block_width_one_plane
[PASSED] drm_format_block_width_two_plane
[PASSED] drm_format_block_width_three_plane
[PASSED] drm_format_block_width_tiled
[PASSED] drm_format_block_height_invalid
[PASSED] drm_format_block_height_one_plane
[PASSED] drm_format_block_height_two_plane
[PASSED] drm_format_block_height_three_plane
[PASSED] drm_format_block_height_tiled
[PASSED] drm_format_min_pitch_invalid
[PASSED] drm_format_min_pitch_one_plane_8bpp
[PASSED] drm_format_min_pitch_one_plane_16bpp
[PASSED] drm_format_min_pitch_one_plane_24bpp
[PASSED] drm_format_min_pitch_one_plane_32bpp
[PASSED] drm_format_min_pitch_two_plane
[PASSED] drm_format_min_pitch_three_plane_8bpp
[PASSED] drm_format_min_pitch_tiled
=== [PASSED] drm_format 

Testing complete. Ran 18 tests: passed: 18

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_format_test.c | 156 
 1 file changed, 108 insertions(+), 48 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_format_test.c 
b/drivers/gpu/drm/tests/drm_format_test.c
index 0efa88bf56a9..1936d2d59908 100644
--- a/drivers/gpu/drm/tests/drm_format_test.c
+++ b/drivers/gpu/drm/tests/drm_format_test.c
@@ -9,100 +9,133 @@
 
 #include 
 
-static void igt_check_drm_format_block_width(struct kunit *test)
+static void drm_format_block_width_invalid(struct kunit *test)
 {
const struct drm_format_info *info = NULL;
 
-   /* Test invalid arguments */
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
+}
+
+static void drm_format_block_width_one_plane(struct kunit *test)
+{
+   const struct drm_format_info *info = 
drm_format_info(DRM_FORMAT_XRGB);
 
-   /* Test 1 plane format */
-   info = drm_format_info(DRM_FORMAT_XRGB);
KUNIT_ASSERT_NOT_NULL(test, info);
+
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
+}
+
+static void drm_format_block_width_two_plane(struct kunit *test)
+{
+   const struct drm_format_info *info = drm_format_info(DRM_FORMAT_NV12);
 
-   /* Test 2 planes format */
-   info = drm_format_info(DRM_FORMAT_NV12);
KUNIT_ASSERT_NOT_NULL(test, info);
+
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 2), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
+}
+
+static void drm_format_block_width_three_plane(struct kunit *test)
+{
+   const struct drm_format_info *info = drm_format_info(DRM_FORMAT_YUV422);
 
-   /* Test 3 planes format */
-   info = drm_format_info(DRM_FORMAT_YUV422);
KUNIT_ASSERT_NOT_NULL(test, info);
+
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 2), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 3), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
+}
+
+static void drm_format_block_width_tiled(struct kunit *test)
+{
+   const struct drm_format_info *info = drm_format_info(DRM_FORMAT_X0L0);
 
-   /* Test a tiled format */
-   info = drm_format_info(DRM_FORMAT_X0L0);
KUNIT_ASSERT_NOT_NULL(test, info);
+
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
 }
 
-static void igt_check_drm_format_block_height(struct kunit *test)
+static void drm_format_block_height_invalid(struct kunit *test)
 {
const struct drm_format_info *info = NULL;
 
-   /* Test invalid arguments */
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, -1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 1), 0);
+}
+
+static void drm_format_block_height_one_plane(struct kunit *test)
+{
+   const struct

[PATCH 1/2] drm/format: Use appropriate types in expect/assert

2022-08-31 Thread Michał Winiarski
drm_format_info_* functions don't return bool, and the info variable is a
pointer.
Expecting non-NULL info will cause the test to crash if it is NULL in
checks that follow (which dereference it).
Use appropriate KUNIT_EXPECT/KUNIT_ASSERT variants.

Signed-off-by: Michał Winiarski 
---
 drivers/gpu/drm/tests/drm_format_test.c | 152 
 1 file changed, 76 insertions(+), 76 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_format_test.c 
b/drivers/gpu/drm/tests/drm_format_test.c
index afb4bca72187..0efa88bf56a9 100644
--- a/drivers/gpu/drm/tests/drm_format_test.c
+++ b/drivers/gpu/drm/tests/drm_format_test.c
@@ -14,40 +14,40 @@ static void igt_check_drm_format_block_width(struct kunit 
*test)
const struct drm_format_info *info = NULL;
 
/* Test invalid arguments */
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 0));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 1));
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
 
/* Test 1 plane format */
info = drm_format_info(DRM_FORMAT_XRGB);
-   KUNIT_EXPECT_TRUE(test, info);
-   KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 0));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 1));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1));
+   KUNIT_ASSERT_NOT_NULL(test, info);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
 
/* Test 2 planes format */
info = drm_format_info(DRM_FORMAT_NV12);
-   KUNIT_EXPECT_TRUE(test, info);
-   KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 0));
-   KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 1));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 2));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1));
+   KUNIT_ASSERT_NOT_NULL(test, info);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 1);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 2), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
 
/* Test 3 planes format */
info = drm_format_info(DRM_FORMAT_YUV422);
-   KUNIT_EXPECT_TRUE(test, info);
-   KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 0));
-   KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 1));
-   KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 2));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 3));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1));
+   KUNIT_ASSERT_NOT_NULL(test, info);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 1);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 2), 1);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 3), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
 
/* Test a tiled format */
info = drm_format_info(DRM_FORMAT_X0L0);
-   KUNIT_EXPECT_TRUE(test, info);
+   KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 2);
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 1));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1));
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
 }
 
 static void igt_check_drm_format_block_height(struct kunit *test)
@@ -55,40 +55,40 @@ static void igt_check_drm_format_block_height(struct kunit 
*test)
const struct drm_format_info *info = NULL;
 
/* Test invalid arguments */
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 0));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, -1));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 1));
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, -1), 0);
+   KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 1), 0);
 
/* Test 1 plane format */
info = drm_format_info(DRM_FORMAT_XRGB);
-   KUNIT_EXPECT_TRUE(test, info);
-   KUNIT_EXPECT_TRUE(test, drm_format_info_block_height(info, 0));
-   KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(inf

  1   2   >