<div dir="ltr"><div>Pardon my ignorance but I'm a bit confused by this.  What is a "logical GPU"?  What are we subdividing?  Are we carving up memory?  Compute power?  Both?</div><div><br></div><div>If it's carving up memory, why aren't we just measuring it in megabytes?</div><div><br></div><div>If it's carving up compute power, what's actually being carved up?  Time?  Execution units/waves/threads?  Even if that's the case, what advantage does it give to have it in terms of a fixed set of lgpus where each cgroup gets to pick a fixed set.  Does affinity matter that much?  Why not just say how many waves the GPU supports and that they have to be allocated in chunks of 16 waves (pulling a number out of thin air) and let the cgroup specify how many waves it wants.</div><div><br></div><div>Don't get me wrong here.  I'm all for the notion of being able to use cgroups to carve up GPU compute resources.  However, this sounds to me like the most AMD-specific solution possible.  We (Intel) could probably do some sort of carving up as well but we'd likely want to do it with preemption and time-slicing rather than handing out specific EUs.</div><div><br></div><div>--Jason</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Feb 14, 2020 at 9:57 AM Kenny Ho <<a href="mailto:Kenny.Ho@amd.com">Kenny.Ho@amd.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">drm.lgpu<br>
      A read-write nested-keyed file which exists on all cgroups.<br>
      Each entry is keyed by the DRM device's major:minor.<br>
<br>
      lgpu stands for logical GPU, it is an abstraction used to<br>
      subdivide a physical DRM device for the purpose of resource<br>
      management.  This file stores user configuration while the<br>
      drm.lgpu.effective reflects the actual allocation after<br>
      considering the relationship between the cgroups and their<br>
      configurations.<br>
<br>
      The lgpu is a discrete quantity that is device specific (i.e.<br>
      some DRM devices may have 64 lgpus while others may have 100<br>
      lgpus.)  The lgpu is a single quantity that can be allocated<br>
      in three different ways denoted by the following nested keys.<br>
<br>
        =====     ==============================================<br>
        weight    Allocate by proportion in relationship with<br>
                  active sibling cgroups<br>
        count     Allocate by amount statically, treat lgpu as<br>
                  anonymous resources<br>
        list      Allocate statically, treat lgpu as named<br>
                  resource<br>
        =====     ==============================================<br>
<br>
      For example:<br>
      226:0 weight=100 count=256 list=0-255<br>
      226:1 weight=100 count=4 list=0,2,4,6<br>
      226:2 weight=100 count=32 list=32-63<br>
      226:3 weight=100 count=0 list=<br>
      226:4 weight=500 count=0 list=<br>
<br>
      lgpu is represented by a bitmap and uses the bitmap_parselist<br>
      kernel function so the list key input format is a<br>
      comma-separated list of decimal numbers and ranges.<br>
<br>
      Consecutively set bits are shown as two hyphen-separated decimal<br>
      numbers, the smallest and largest bit numbers set in the range.<br>
      Optionally each range can be postfixed to denote that only parts<br>
      of it should be set.  The range will divided to groups of<br>
      specific size.<br>
      Syntax: range:used_size/group_size<br>
      Example: 0-1023:2/256 ==> 0,1,256,257,512,513,768,769<br>
<br>
      The count key is the hamming weight / hweight of the bitmap.<br>
<br>
      Weight, count and list accept the max and default keywords.<br>
<br>
      Some DRM devices may only support lgpu as anonymous resources.<br>
      In such case, the significance of the position of the set bits<br>
      in list will be ignored.<br>
<br>
      The weight quantity is only in effect when static allocation<br>
      is not used (by setting count=0) for this cgroup.  The weight<br>
      quantity distributes lgpus that are not statically allocated by<br>
      the siblings.  For example, given siblings cgroupA, cgroupB and<br>
      cgroupC for a DRM device that has 64 lgpus, if cgroupA occupies<br>
      0-63, no lgpu is available to be distributed by weight.<br>
      Similarly, if cgroupA has list=0-31 and cgroupB has list=16-63,<br>
      cgroupC will be starved if it tries to allocate by weight.<br>
<br>
      On the other hand, if cgroupA has weight=100 count=0, cgroupB<br>
      has list=16-47, and cgroupC has weight=100 count=0, then 32<br>
      lgpus are available to be distributed evenly between cgroupA<br>
      and cgroupC.  In drm.lgpu.effective, cgroupA will have<br>
      list=0-15 and cgroupC will have list=48-63.<br>
<br>
      This lgpu resource supports the 'allocation' and 'weight'<br>
      resource distribution model.<br>
<br>
drm.lgpu.effective<br>
      A read-only nested-keyed file which exists on all cgroups.<br>
      Each entry is keyed by the DRM device's major:minor.<br>
<br>
      lgpu stands for logical GPU, it is an abstraction used to<br>
      subdivide a physical DRM device for the purpose of resource<br>
      management.  This file reflects the actual allocation after<br>
      considering the relationship between the cgroups and their<br>
      configurations in drm.lgpu.<br>
<br>
Change-Id: Idde0ef9a331fd67bb9c7eb8ef9978439e6452488<br>
Signed-off-by: Kenny Ho <<a href="mailto:Kenny.Ho@amd.com" target="_blank">Kenny.Ho@amd.com</a>><br>
---<br>
 Documentation/admin-guide/cgroup-v2.rst |  80 ++++++<br>
 include/drm/drm_cgroup.h                |   3 +<br>
 include/linux/cgroup_drm.h              |  22 ++<br>
 kernel/cgroup/drm.c                     | 324 +++++++++++++++++++++++-<br>
 4 files changed, 427 insertions(+), 2 deletions(-)<br>
<br>
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst<br>
index ce5dc027366a..d8a41956e5c7 100644<br>
--- a/Documentation/admin-guide/cgroup-v2.rst<br>
+++ b/Documentation/admin-guide/cgroup-v2.rst<br>
@@ -2120,6 +2120,86 @@ DRM Interface Files<br>
        Set largest allocation for /dev/dri/card1 to 4MB<br>
        echo "226:1 4m" > drm.buffer.peak.max<br>
<br>
+  drm.lgpu<br>
+       A read-write nested-keyed file which exists on all cgroups.<br>
+       Each entry is keyed by the DRM device's major:minor.<br>
+<br>
+       lgpu stands for logical GPU, it is an abstraction used to<br>
+       subdivide a physical DRM device for the purpose of resource<br>
+       management.  This file stores user configuration while the<br>
+        drm.lgpu.effective reflects the actual allocation after<br>
+        considering the relationship between the cgroups and their<br>
+        configurations.<br>
+<br>
+       The lgpu is a discrete quantity that is device specific (i.e.<br>
+       some DRM devices may have 64 lgpus while others may have 100<br>
+       lgpus.)  The lgpu is a single quantity that can be allocated<br>
+        in three different ways denoted by the following nested keys.<br>
+<br>
+         =====     ==============================================<br>
+         weight    Allocate by proportion in relationship with<br>
+                    active sibling cgroups<br>
+         count     Allocate by amount statically, treat lgpu as<br>
+                    anonymous resources<br>
+         list      Allocate statically, treat lgpu as named<br>
+                    resource<br>
+         =====     ==============================================<br>
+<br>
+       For example:<br>
+       226:0 weight=100 count=256 list=0-255<br>
+       226:1 weight=100 count=4 list=0,2,4,6<br>
+       226:2 weight=100 count=32 list=32-63<br>
+       226:3 weight=100 count=0 list=<br>
+       226:4 weight=500 count=0 list=<br>
+<br>
+       lgpu is represented by a bitmap and uses the bitmap_parselist<br>
+       kernel function so the list key input format is a<br>
+       comma-separated list of decimal numbers and ranges.<br>
+<br>
+       Consecutively set bits are shown as two hyphen-separated decimal<br>
+       numbers, the smallest and largest bit numbers set in the range.<br>
+       Optionally each range can be postfixed to denote that only parts<br>
+       of it should be set.  The range will divided to groups of<br>
+       specific size.<br>
+       Syntax: range:used_size/group_size<br>
+       Example: 0-1023:2/256 ==> 0,1,256,257,512,513,768,769<br>
+<br>
+       The count key is the hamming weight / hweight of the bitmap.<br>
+<br>
+       Weight, count and list accept the max and default keywords.<br>
+<br>
+       Some DRM devices may only support lgpu as anonymous resources.<br>
+       In such case, the significance of the position of the set bits<br>
+       in list will be ignored.<br>
+<br>
+       The weight quantity is only in effect when static allocation<br>
+       is not used (by setting count=0) for this cgroup.  The weight<br>
+       quantity distributes lgpus that are not statically allocated by<br>
+       the siblings.  For example, given siblings cgroupA, cgroupB and<br>
+       cgroupC for a DRM device that has 64 lgpus, if cgroupA occupies<br>
+       0-63, no lgpu is available to be distributed by weight.<br>
+       Similarly, if cgroupA has list=0-31 and cgroupB has list=16-63,<br>
+       cgroupC will be starved if it tries to allocate by weight.<br>
+<br>
+       On the other hand, if cgroupA has weight=100 count=0, cgroupB<br>
+       has list=16-47, and cgroupC has weight=100 count=0, then 32<br>
+       lgpus are available to be distributed evenly between cgroupA<br>
+       and cgroupC.  In drm.lgpu.effective, cgroupA will have<br>
+       list=0-15 and cgroupC will have list=48-63.<br>
+<br>
+       This lgpu resource supports the 'allocation' and 'weight'<br>
+       resource distribution model.<br>
+<br>
+  drm.lgpu.effective<br>
+       A read-only nested-keyed file which exists on all cgroups.<br>
+       Each entry is keyed by the DRM device's major:minor.<br>
+<br>
+       lgpu stands for logical GPU, it is an abstraction used to<br>
+       subdivide a physical DRM device for the purpose of resource<br>
+       management.  This file reflects the actual allocation after<br>
+        considering the relationship between the cgroups and their<br>
+        configurations in drm.lgpu.<br>
+<br>
 GEM Buffer Ownership<br>
 ~~~~~~~~~~~~~~~~~~~~<br>
<br>
diff --git a/include/drm/drm_cgroup.h b/include/drm/drm_cgroup.h<br>
index 2b41d4d22e33..619a110cc748 100644<br>
--- a/include/drm/drm_cgroup.h<br>
+++ b/include/drm/drm_cgroup.h<br>
@@ -17,6 +17,9 @@ struct drmcg_props {<br>
<br>
        s64                     bo_limits_total_allocated_default;<br>
        s64                     bo_limits_peak_allocated_default;<br>
+<br>
+       int                     lgpu_capacity;<br>
+       DECLARE_BITMAP(lgpu_slots, MAX_DRMCG_LGPU_CAPACITY);<br>
 };<br>
<br>
 void drmcg_bind(struct drm_minor (*(*acq_dm)(unsigned int minor_id)),<br>
diff --git a/include/linux/cgroup_drm.h b/include/linux/cgroup_drm.h<br>
index eae400f3d9b4..bb09704e7f71 100644<br>
--- a/include/linux/cgroup_drm.h<br>
+++ b/include/linux/cgroup_drm.h<br>
@@ -11,10 +11,14 @@<br>
 /* limit defined per the way drm_minor_alloc operates */<br>
 #define MAX_DRM_DEV (64 * DRM_MINOR_RENDER)<br>
<br>
+#define MAX_DRMCG_LGPU_CAPACITY 256<br>
+<br>
 enum drmcg_res_type {<br>
        DRMCG_TYPE_BO_TOTAL,<br>
        DRMCG_TYPE_BO_PEAK,<br>
        DRMCG_TYPE_BO_COUNT,<br>
+       DRMCG_TYPE_LGPU,<br>
+       DRMCG_TYPE_LGPU_EFF,<br>
        __DRMCG_TYPE_LAST,<br>
 };<br>
<br>
@@ -32,6 +36,24 @@ struct drmcg_device_resource {<br>
        s64                     bo_limits_peak_allocated;<br>
<br>
        s64                     bo_stats_count_allocated;<br>
+<br>
+       /**<br>
+        * Logical GPU<br>
+        *<br>
+        * *_cfg are properties configured by users<br>
+        * *_eff are the effective properties being applied to the hardware<br>
+         * *_stg is used to calculate _eff before applying to _eff<br>
+        * after considering the entire hierarchy<br>
+        */<br>
+       DECLARE_BITMAP(lgpu_stg, MAX_DRMCG_LGPU_CAPACITY);<br>
+       /* user configurations */<br>
+       s64                     lgpu_weight_cfg;<br>
+       DECLARE_BITMAP(lgpu_cfg, MAX_DRMCG_LGPU_CAPACITY);<br>
+       /* effective lgpu for the cgroup after considering<br>
+        * relationship with other cgroup<br>
+        */<br>
+       s64                     lgpu_count_eff;<br>
+       DECLARE_BITMAP(lgpu_eff, MAX_DRMCG_LGPU_CAPACITY);<br>
 };<br>
<br>
 /**<br>
diff --git a/kernel/cgroup/drm.c b/kernel/cgroup/drm.c<br>
index 5fcbbc13fa1c..a4e88a3704bb 100644<br>
--- a/kernel/cgroup/drm.c<br>
+++ b/kernel/cgroup/drm.c<br>
@@ -9,6 +9,7 @@<br>
 #include <linux/seq_file.h><br>
 #include <linux/mutex.h><br>
 #include <linux/kernel.h><br>
+#include <linux/bitmap.h><br>
 #include <linux/cgroup_drm.h><br>
 #include <drm/drm_file.h><br>
 #include <drm/drm_drv.h><br>
@@ -41,6 +42,10 @@ enum drmcg_file_type {<br>
        DRMCG_FTYPE_DEFAULT,<br>
 };<br>
<br>
+#define LGPU_LIMITS_NAME_LIST "list"<br>
+#define LGPU_LIMITS_NAME_COUNT "count"<br>
+#define LGPU_LIMITS_NAME_WEIGHT "weight"<br>
+<br>
 /**<br>
  * drmcg_bind - Bind DRM subsystem to cgroup subsystem<br>
  * @acq_dm: function pointer to the drm_minor_acquire function<br>
@@ -98,6 +103,13 @@ static inline int init_drmcg_single(struct drmcg *drmcg, struct drm_device *dev)<br>
        ddr->bo_limits_peak_allocated =<br>
                dev->drmcg_props.bo_limits_peak_allocated_default;<br>
<br>
+       bitmap_copy(ddr->lgpu_cfg, dev->drmcg_props.lgpu_slots,<br>
+                       MAX_DRMCG_LGPU_CAPACITY);<br>
+       bitmap_copy(ddr->lgpu_stg, dev->drmcg_props.lgpu_slots,<br>
+                       MAX_DRMCG_LGPU_CAPACITY);<br>
+<br>
+       ddr->lgpu_weight_cfg = CGROUP_WEIGHT_DFL;<br>
+<br>
        return 0;<br>
 }<br>
<br>
@@ -121,6 +133,120 @@ static inline void drmcg_update_cg_tree(struct drm_device *dev)<br>
        mutex_unlock(&cgroup_mutex);<br>
 }<br>
<br>
+static void drmcg_calculate_effective_lgpu(struct drm_device *dev,<br>
+               const unsigned long *free_static,<br>
+               const unsigned long *free_weighted,<br>
+               struct drmcg *parent_drmcg)<br>
+{<br>
+       int capacity = dev->drmcg_props.lgpu_capacity;<br>
+       DECLARE_BITMAP(lgpu_unused, MAX_DRMCG_LGPU_CAPACITY);<br>
+       DECLARE_BITMAP(lgpu_by_weight, MAX_DRMCG_LGPU_CAPACITY);<br>
+       struct drmcg_device_resource *parent_ddr;<br>
+       struct drmcg_device_resource *ddr;<br>
+       int minor = dev->primary->index;<br>
+       struct cgroup_subsys_state *pos;<br>
+       struct drmcg *child;<br>
+       s64 weight_sum = 0;<br>
+       s64 unused;<br>
+<br>
+       parent_ddr = parent_drmcg->dev_resources[minor];<br>
+<br>
+       if (bitmap_empty(parent_ddr->lgpu_cfg, capacity))<br>
+               /* no static cfg, use weight for calculating the effective */<br>
+               bitmap_copy(parent_ddr->lgpu_stg, free_weighted, capacity);<br>
+       else<br>
+               /* lgpu statically configured, use the overlap as effective */<br>
+               bitmap_and(parent_ddr->lgpu_stg, free_static,<br>
+                               parent_ddr->lgpu_cfg, capacity);<br>
+<br>
+       /* calculate lgpu available for distribution by weight for children */<br>
+       bitmap_copy(lgpu_unused, parent_ddr->lgpu_stg, capacity);<br>
+       css_for_each_child(pos, &parent_drmcg->css) {<br>
+               child = css_to_drmcg(pos);<br>
+               ddr = child->dev_resources[minor];<br>
+<br>
+               if (bitmap_empty(ddr->lgpu_cfg, capacity))<br>
+                       /* no static allocation, participate in weight dist */<br>
+                       weight_sum += ddr->lgpu_weight_cfg;<br>
+               else<br>
+                       /* take out statically allocated lgpu by siblings */<br>
+                       bitmap_andnot(lgpu_unused, lgpu_unused, ddr->lgpu_cfg,<br>
+                                       capacity);<br>
+       }<br>
+<br>
+       unused = bitmap_weight(lgpu_unused, capacity);<br>
+<br>
+       css_for_each_child(pos, &parent_drmcg->css) {<br>
+               child = css_to_drmcg(pos);<br>
+               ddr = child->dev_resources[minor];<br>
+<br>
+               bitmap_zero(lgpu_by_weight, capacity);<br>
+               /* no static allocation, participate in weight distribution */<br>
+               if (bitmap_empty(ddr->lgpu_cfg, capacity)) {<br>
+                       int c;<br>
+                       int p = 0;<br>
+<br>
+                       for (c = ddr->lgpu_weight_cfg * unused / weight_sum;<br>
+                                       c > 0; c--) {<br>
+                               p = find_next_bit(lgpu_unused, capacity, p);<br>
+                               if (p < capacity) {<br>
+                                       clear_bit(p, lgpu_unused);<br>
+                                       set_bit(p, lgpu_by_weight);<br>
+                               }<br>
+                       }<br>
+<br>
+               }<br>
+<br>
+               drmcg_calculate_effective_lgpu(dev, parent_ddr->lgpu_stg,<br>
+                               lgpu_by_weight, child);<br>
+       }<br>
+}<br>
+<br>
+static void drmcg_apply_effective_lgpu(struct drm_device *dev)<br>
+{<br>
+       int capacity = dev->drmcg_props.lgpu_capacity;<br>
+       int minor = dev->primary->index;<br>
+       struct drmcg_device_resource *ddr;<br>
+       struct cgroup_subsys_state *pos;<br>
+       struct drmcg *drmcg;<br>
+<br>
+       if (root_drmcg == NULL) {<br>
+               WARN_ON(root_drmcg == NULL);<br>
+               return;<br>
+       }<br>
+<br>
+       rcu_read_lock();<br>
+<br>
+       /* process the entire cgroup tree from root to simplify the algorithm */<br>
+       drmcg_calculate_effective_lgpu(dev, dev->drmcg_props.lgpu_slots,<br>
+                       dev->drmcg_props.lgpu_slots, root_drmcg);<br>
+<br>
+       /* apply changes to effective only if there is a change */<br>
+       css_for_each_descendant_pre(pos, &root_drmcg->css) {<br>
+               drmcg = css_to_drmcg(pos);<br>
+               ddr = drmcg->dev_resources[minor];<br>
+<br>
+               if (!bitmap_equal(ddr->lgpu_stg, ddr->lgpu_eff, capacity)) {<br>
+                       bitmap_copy(ddr->lgpu_eff, ddr->lgpu_stg, capacity);<br>
+                       ddr->lgpu_count_eff =<br>
+                               bitmap_weight(ddr->lgpu_eff, capacity);<br>
+               }<br>
+       }<br>
+       rcu_read_unlock();<br>
+}<br>
+<br>
+static void drmcg_apply_effective(enum drmcg_res_type type,<br>
+               struct drm_device *dev, struct drmcg *changed_drmcg)<br>
+{<br>
+       switch (type) {<br>
+       case DRMCG_TYPE_LGPU:<br>
+               drmcg_apply_effective_lgpu(dev);<br>
+               break;<br>
+       default:<br>
+               break;<br>
+       }<br>
+}<br>
+<br>
 /**<br>
  * drmcg_register_dev - register a DRM device for usage in drm cgroup<br>
  * @dev: DRM device<br>
@@ -143,7 +269,13 @@ void drmcg_register_dev(struct drm_device *dev)<br>
        {<br>
                dev->driver->drmcg_custom_init(dev, &dev->drmcg_props);<br>
<br>
+               WARN_ON(dev->drmcg_props.lgpu_capacity !=<br>
+                               bitmap_weight(dev->drmcg_props.lgpu_slots,<br>
+                                       MAX_DRMCG_LGPU_CAPACITY));<br>
+<br>
                drmcg_update_cg_tree(dev);<br>
+<br>
+               drmcg_apply_effective(DRMCG_TYPE_LGPU, dev, root_drmcg);<br>
        }<br>
        mutex_unlock(&drmcg_mutex);<br>
 }<br>
@@ -297,7 +429,8 @@ static void drmcg_print_stats(struct drmcg_device_resource *ddr,<br>
 }<br>
<br>
 static void drmcg_print_limits(struct drmcg_device_resource *ddr,<br>
-               struct seq_file *sf, enum drmcg_res_type type)<br>
+               struct seq_file *sf, enum drmcg_res_type type,<br>
+               struct drm_device *dev)<br>
 {<br>
        if (ddr == NULL) {<br>
                seq_puts(sf, "\n");<br>
@@ -311,6 +444,25 @@ static void drmcg_print_limits(struct drmcg_device_resource *ddr,<br>
        case DRMCG_TYPE_BO_PEAK:<br>
                seq_printf(sf, "%lld\n", ddr->bo_limits_peak_allocated);<br>
                break;<br>
+       case DRMCG_TYPE_LGPU:<br>
+               seq_printf(sf, "%s=%lld %s=%d %s=%*pbl\n",<br>
+                               LGPU_LIMITS_NAME_WEIGHT,<br>
+                               ddr->lgpu_weight_cfg,<br>
+                               LGPU_LIMITS_NAME_COUNT,<br>
+                               bitmap_weight(ddr->lgpu_cfg,<br>
+                                       dev->drmcg_props.lgpu_capacity),<br>
+                               LGPU_LIMITS_NAME_LIST,<br>
+                               dev->drmcg_props.lgpu_capacity,<br>
+                               ddr->lgpu_cfg);<br>
+               break;<br>
+       case DRMCG_TYPE_LGPU_EFF:<br>
+               seq_printf(sf, "%s=%lld %s=%*pbl\n",<br>
+                               LGPU_LIMITS_NAME_COUNT,<br>
+                               ddr->lgpu_count_eff,<br>
+                               LGPU_LIMITS_NAME_LIST,<br>
+                               dev->drmcg_props.lgpu_capacity,<br>
+                               ddr->lgpu_eff);<br>
+               break;<br>
        default:<br>
                seq_puts(sf, "\n");<br>
                break;<br>
@@ -329,6 +481,17 @@ static void drmcg_print_default(struct drmcg_props *props,<br>
                seq_printf(sf, "%lld\n",<br>
                        props->bo_limits_peak_allocated_default);<br>
                break;<br>
+       case DRMCG_TYPE_LGPU:<br>
+               seq_printf(sf, "%s=%d %s=%d %s=%*pbl\n",<br>
+                               LGPU_LIMITS_NAME_WEIGHT,<br>
+                               CGROUP_WEIGHT_DFL,<br>
+                               LGPU_LIMITS_NAME_COUNT,<br>
+                               bitmap_weight(props->lgpu_slots,<br>
+                                       props->lgpu_capacity),<br>
+                               LGPU_LIMITS_NAME_LIST,<br>
+                               props->lgpu_capacity,<br>
+                               props->lgpu_slots);<br>
+               break;<br>
        default:<br>
                seq_puts(sf, "\n");<br>
                break;<br>
@@ -358,7 +521,7 @@ static int drmcg_seq_show_fn(int id, void *ptr, void *data)<br>
                drmcg_print_stats(ddr, sf, type);<br>
                break;<br>
        case DRMCG_FTYPE_LIMIT:<br>
-               drmcg_print_limits(ddr, sf, type);<br>
+               drmcg_print_limits(ddr, sf, type, minor->dev);<br>
                break;<br>
        case DRMCG_FTYPE_DEFAULT:<br>
                drmcg_print_default(&minor->dev->drmcg_props, sf, type);<br>
@@ -415,6 +578,115 @@ static int drmcg_process_limit_s64_val(char *sval, bool is_mem,<br>
        return rc;<br>
 }<br>
<br>
+static void drmcg_nested_limit_parse(struct kernfs_open_file *of,<br>
+               struct drm_device *dev, char *attrs)<br>
+{<br>
+       DECLARE_BITMAP(tmp_bitmap, MAX_DRMCG_LGPU_CAPACITY);<br>
+       DECLARE_BITMAP(chk_bitmap, MAX_DRMCG_LGPU_CAPACITY);<br>
+       enum drmcg_res_type type =<br>
+               DRMCG_CTF_PRIV2RESTYPE(of_cft(of)->private);<br>
+       struct drmcg *drmcg = css_to_drmcg(of_css(of));<br>
+       struct drmcg_props *props = &dev->drmcg_props;<br>
+       char *cft_name = of_cft(of)->name;<br>
+       int minor = dev->primary->index;<br>
+       char *nested = strstrip(attrs);<br>
+       struct drmcg_device_resource *ddr =<br>
+               drmcg->dev_resources[minor];<br>
+       char *attr;<br>
+       char sname[256];<br>
+       char sval[256];<br>
+       s64 val;<br>
+       int rc;<br>
+<br>
+       while (nested != NULL) {<br>
+               attr = strsep(&nested, " ");<br>
+<br>
+               if (sscanf(attr, "%255[^=]=%255[^=]", sname, sval) != 2)<br>
+                       continue;<br>
+<br>
+               switch (type) {<br>
+               case DRMCG_TYPE_LGPU:<br>
+                       if (strncmp(sname, LGPU_LIMITS_NAME_LIST, 256) &&<br>
+                               strncmp(sname, LGPU_LIMITS_NAME_COUNT, 256) &&<br>
+                               strncmp(sname, LGPU_LIMITS_NAME_WEIGHT, 256))<br>
+                               continue;<br>
+<br>
+                       if (strncmp(sname, LGPU_LIMITS_NAME_WEIGHT, 256) &&<br>
+                                       (!strcmp("max", sval) ||<br>
+                                       !strcmp("default", sval))) {<br>
+                               bitmap_copy(ddr->lgpu_cfg, props->lgpu_slots,<br>
+                                               props->lgpu_capacity);<br>
+<br>
+                               continue;<br>
+                       }<br>
+<br>
+                       if (strncmp(sname, LGPU_LIMITS_NAME_WEIGHT, 256) == 0) {<br>
+                               rc = drmcg_process_limit_s64_val(sval,<br>
+                                       false, CGROUP_WEIGHT_DFL,<br>
+                                       CGROUP_WEIGHT_MAX, &val);<br>
+<br>
+                               if (rc || val < CGROUP_WEIGHT_MIN ||<br>
+                                               val > CGROUP_WEIGHT_MAX) {<br>
+                                       drmcg_pr_cft_err(drmcg, rc, cft_name,<br>
+                                                       minor);<br>
+                                       continue;<br>
+                               }<br>
+<br>
+                               ddr->lgpu_weight_cfg = val;<br>
+                               continue;<br>
+                       }<br>
+<br>
+                       if (strncmp(sname, LGPU_LIMITS_NAME_COUNT, 256) == 0) {<br>
+                               rc = drmcg_process_limit_s64_val(sval,<br>
+                                       false, props->lgpu_capacity,<br>
+                                       props->lgpu_capacity, &val);<br>
+<br>
+                               if (rc || val < 0) {<br>
+                                       drmcg_pr_cft_err(drmcg, rc, cft_name,<br>
+                                                       minor);<br>
+                                       continue;<br>
+                               }<br>
+<br>
+                               bitmap_zero(tmp_bitmap,<br>
+                                               MAX_DRMCG_LGPU_CAPACITY);<br>
+                               bitmap_set(tmp_bitmap, 0, val);<br>
+                       }<br>
+<br>
+                       if (strncmp(sname, LGPU_LIMITS_NAME_LIST, 256) == 0) {<br>
+                               rc = bitmap_parselist(sval, tmp_bitmap,<br>
+                                               MAX_DRMCG_LGPU_CAPACITY);<br>
+<br>
+                               if (rc) {<br>
+                                       drmcg_pr_cft_err(drmcg, rc, cft_name,<br>
+                                                       minor);<br>
+                                       continue;<br>
+                               }<br>
+<br>
+                               bitmap_andnot(chk_bitmap, tmp_bitmap,<br>
+                                       props->lgpu_slots,<br>
+                                       MAX_DRMCG_LGPU_CAPACITY);<br>
+<br>
+                               /* user setting does not intersect with<br>
+                                * available lgpu */<br>
+                               if (!bitmap_empty(chk_bitmap,<br>
+                                               MAX_DRMCG_LGPU_CAPACITY)) {<br>
+                                       drmcg_pr_cft_err(drmcg, 0, cft_name,<br>
+                                                       minor);<br>
+                                       continue;<br>
+                               }<br>
+                       }<br>
+<br>
+                       bitmap_copy(ddr->lgpu_cfg, tmp_bitmap,<br>
+                                       props->lgpu_capacity);<br>
+<br>
+                       break; /* DRMCG_TYPE_LGPU */<br>
+               default:<br>
+                       break;<br>
+               } /* switch (type) */<br>
+       }<br>
+}<br>
+<br>
+<br>
 /**<br>
  * drmcg_limit_write - parse cgroup interface files to obtain user config<br>
  *<br>
@@ -499,9 +771,15 @@ static ssize_t drmcg_limit_write(struct kernfs_open_file *of, char *buf,<br>
<br>
                        ddr->bo_limits_peak_allocated = val;<br>
                        break;<br>
+               case DRMCG_TYPE_LGPU:<br>
+                       drmcg_nested_limit_parse(of, dm->dev, sattr);<br>
+                       break;<br>
                default:<br>
                        break;<br>
                }<br>
+<br>
+               drmcg_apply_effective(type, dm->dev, drmcg);<br>
+<br>
                mutex_unlock(&dm->dev->drmcg_mutex);<br>
<br>
                mutex_lock(&drmcg_mutex);<br>
@@ -560,12 +838,51 @@ struct cftype files[] = {<br>
                .private = DRMCG_CTF_PRIV(DRMCG_TYPE_BO_COUNT,<br>
                                                DRMCG_FTYPE_STATS),<br>
        },<br>
+       {<br>
+               .name = "lgpu",<br>
+               .seq_show = drmcg_seq_show,<br>
+               .write = drmcg_limit_write,<br>
+               .private = DRMCG_CTF_PRIV(DRMCG_TYPE_LGPU,<br>
+                                               DRMCG_FTYPE_LIMIT),<br>
+       },<br>
+       {<br>
+               .name = "lgpu.default",<br>
+               .seq_show = drmcg_seq_show,<br>
+               .flags = CFTYPE_ONLY_ON_ROOT,<br>
+               .private = DRMCG_CTF_PRIV(DRMCG_TYPE_LGPU,<br>
+                                               DRMCG_FTYPE_DEFAULT),<br>
+       },<br>
+       {<br>
+               .name = "lgpu.effective",<br>
+               .seq_show = drmcg_seq_show,<br>
+               .private = DRMCG_CTF_PRIV(DRMCG_TYPE_LGPU_EFF,<br>
+                                               DRMCG_FTYPE_LIMIT),<br>
+       },<br>
        { }     /* terminate */<br>
 };<br>
<br>
+static int drmcg_online_fn(int id, void *ptr, void *data)<br>
+{<br>
+       struct drm_minor *minor = ptr;<br>
+       struct drmcg *drmcg = data;<br>
+<br>
+       if (minor->type != DRM_MINOR_PRIMARY)<br>
+               return 0;<br>
+<br>
+       drmcg_apply_effective(DRMCG_TYPE_LGPU, minor->dev, drmcg);<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
+static int drmcg_css_online(struct cgroup_subsys_state *css)<br>
+{<br>
+       return drm_minor_for_each(&drmcg_online_fn, css_to_drmcg(css));<br>
+}<br>
+<br>
 struct cgroup_subsys drm_cgrp_subsys = {<br>
        .css_alloc      = drmcg_css_alloc,<br>
        .css_free       = drmcg_css_free,<br>
+       .css_online     = drmcg_css_online,<br>
        .early_init     = false,<br>
        .legacy_cftypes = files,<br>
        .dfl_cftypes    = files,<br>
@@ -585,6 +902,9 @@ void drmcg_device_early_init(struct drm_device *dev)<br>
        dev->drmcg_props.bo_limits_total_allocated_default = S64_MAX;<br>
        dev->drmcg_props.bo_limits_peak_allocated_default = S64_MAX;<br>
<br>
+       dev->drmcg_props.lgpu_capacity = MAX_DRMCG_LGPU_CAPACITY;<br>
+       bitmap_fill(dev->drmcg_props.lgpu_slots, MAX_DRMCG_LGPU_CAPACITY);<br>
+<br>
        drmcg_update_cg_tree(dev);<br>
 }<br>
 EXPORT_SYMBOL(drmcg_device_early_init);<br>
-- <br>
2.25.0<br>
<br>
_______________________________________________<br>
dri-devel mailing list<br>
<a href="mailto:dri-devel@lists.freedesktop.org" target="_blank">dri-devel@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/dri-devel" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/dri-devel</a><br>
</blockquote></div>