Module parameter only allows default high/low watermarks when batch scheduling. Here we introduce some new debugfs parsing to allow settings per pid. This patch is a bit more than just handling batch params as the code turned out quite ugly as a result. So a bit of cleaning up happens here too, and ideally it would be split out into another commit. Here are some usage examples: Set the globally fair scheduler echo "type=fair,high=10,low=2" > /sys/kernel/debug/dri/0/i915_scheduler Turn off scheduling echo "type=none" > /sys/kernel/debug/dri/0/i915_scheduler Set high watermark of 10, an low of 2 for pid 16603 echo "pidh=16603-10,pidl=16603,2"> /sys/kernel/debug/dri/0/i915_scheduler Change scheduler to batch scheduler echo "type=batch" > /sys/kernel/debug/dri/0/i915_scheduler Set high watermark of 5, an low of 0 while already running batch sched echo "pidh=16603-5,pidl=16603,0"> /sys/kernel/debug/dri/0/i915_scheduler Signed-off-by: Ben Widawsky <ben at bwidawsk.net> --- drivers/gpu/drm/i915/i915_debugfs.c | 152 +++++++++++++++++++++++++++-------- 1 files changed, 117 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 903a004..f4c44fa 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1544,7 +1544,7 @@ static const struct file_operations i915_cache_sharing_fops = { }; static const char *sched_type[I915_SCHEDULER_INVALID] = - {"none", "fair"}; + {"none", "fair", "batch"}; static ssize_t i915_sched_read(struct file *filp, @@ -1555,7 +1555,7 @@ i915_sched_read(struct file *filp, struct drm_device *dev = filp->private_data; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_file_private *temp; - unsigned high, low, type; + unsigned type; size_t len = 0; char *buf, *buf2; struct { @@ -1572,14 +1572,19 @@ i915_sched_read(struct file *filp, return ret; type = dev_priv->scheduler.type; - high = dev_priv->scheduler.high_watermark; - low = dev_priv->scheduler.low_watermark; list_for_each_entry(temp, &dev_priv->i915_client_list, client_link) { struct drm_file *file = temp->drm_file; requests[i].pid = file->pid; spin_lock(&temp->lock); requests[i].outstanding = temp->outstanding_requests; requests[i].forced = temp->forced_throttles; + if (type == I915_SCHEDULER_BATCH) { + requests[i].high = temp->high_watermark; + requests[i].low = temp->low_watermark; + } else { + requests[i].high = dev_priv->scheduler.high_watermark; + requests[i].low = dev_priv->scheduler.low_watermark; + } spin_unlock(&temp->lock); i++; } @@ -1587,21 +1592,22 @@ i915_sched_read(struct file *filp, entries = i; - entry_size = strlen("pid 123456 = XXXX outstanding (XXXXXX forced)\n") + 1; + entry_size = strlen("pid 123456 = XXXX outstanding (XXXXXX forced) " + "h=XXXX l=YYYY\n") + 1; buf2 = drm_malloc_ab(entries, entry_size); memset(buf2, 0, entries * entry_size); for(i = 0; i < entries; i++) { len += sprintf(buf2 + len, - "pid %06d = %06d outstanding (%06d forced)\n", + "pid %06d = %06d outstanding (%06d forced) " + "h=%04d l=%04d\n", requests[i].pid, requests[i].outstanding, - requests[i].forced); + requests[i].forced, requests[i].high, + requests[i].low); } buf = kasprintf(GFP_KERNEL, "%s\n" - "Scheduler = %s\n" - "High watermark = %d buffers\n" - "Low watermark = %d buffers\n", - buf2, sched_type[type], high, low); + "Scheduler = %s\n", + buf2, sched_type[type]); len = strlen(buf); @@ -1612,6 +1618,49 @@ i915_sched_read(struct file *filp, return ret; } +static inline void +test_and_do_idle(struct drm_device *dev, bool *change) +{ + if (*change == false && i915_gpu_idle(dev)) + DRM_ERROR("Couldn't idle GPU before sched changes\n"); + + *change = true; +} + +static void +set_new_water(struct drm_device *dev, pid_t pid, uint32_t watermark, bool high, + bool *change) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_file_private *temp; + + list_for_each_entry(temp, &dev_priv->i915_client_list, client_link) { + struct drm_file *file = temp->drm_file; + if (file->pid == pid) { + uint32_t old = 0; + if (high) { + old = temp->high_watermark; + if (old != watermark) { + temp->high_watermark = watermark; + test_and_do_idle(dev, change); + DRM_INFO("New high watermark for pid " + "%d = %d\n", pid, watermark); + } + break; + } else { + old = temp->low_watermark; + if (old != watermark) { + temp->low_watermark = watermark; + test_and_do_idle(dev, change); + DRM_INFO("New low watermark for pid " + "%d = %d\n", pid, watermark); + } + break; + } + } + } +} + static ssize_t i915_sched_write(struct file *filp, const char __user *ubuf, @@ -1622,59 +1671,92 @@ i915_sched_write(struct file *filp, struct drm_i915_private *dev_priv = dev->dev_private; char *buf, *ptr; uint32_t high, low, type; - int ret; + bool change = false; buf = drm_malloc_ab(cnt + 1, 1); if (!buf) return -E2BIG; - ret = copy_from_user(buf, ubuf, cnt); - if (ret) + if (copy_from_user(buf, ubuf, cnt)) goto out; - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; + if (mutex_lock_interruptible(&dev->struct_mutex)) + goto out; - low = dev_priv->scheduler.low_watermark; - high = dev_priv->scheduler.high_watermark; type = dev_priv->scheduler.type; buf[cnt] = 0; ptr = buf; + /* type=[none, fair, batch] + * pidh=[pid]-[new high watermark] + * pidl=[pid]-[new low watermark] + * low=[new fair low watermark] + * high=[new fair high watermark] + */ while ((ptr != NULL) && *ptr != 0) { if (!strncmp(ptr, "type=", 5)) { char *tmp = strchr(ptr, '='); if (tmp != NULL) tmp += strspn(tmp, "= \t\n"); + else + break; + if (!strncmp("fair", tmp, 4)) type = I915_SCHEDULER_FAIR; + if (!strncmp("batch", tmp, 5)) + type = I915_SCHEDULER_BATCH; if (!strncmp("none", tmp, 4)) type = I915_SCHEDULER_NONE; - } - if (!strncmp(ptr, "high=", 5)) + + if (dev_priv->scheduler.type != type) { + test_and_do_idle(dev, &change); + dev_priv->scheduler.type = type; + DRM_INFO("New scheduler type = %s\n", + sched_type[type]); + } + } else if (!strncmp(ptr, "high=", 5)) { high = simple_strtoul(ptr + 5, NULL, 0); - if (!strncmp(ptr, "low=", 4)) + if (dev_priv->scheduler.high_watermark != high) { + test_and_do_idle(dev, &change); + dev_priv->scheduler.high_watermark = high; + DRM_INFO("New high watermark (fair) = %d\n", + high); + } + } else if (!strncmp(ptr, "low=", 4)) { low = simple_strtoul(ptr + 4, NULL, 0); + if (dev_priv->scheduler.low_watermark != low) { + test_and_do_idle(dev, &change); + dev_priv->scheduler.low_watermark = low; + DRM_INFO("New low watermark (fair) = %d\n", + low); + } + } else if (!strncmp(ptr, "pidh=", 5)) { + pid_t pid = simple_strtoul(ptr + 5, NULL, 10); + ptr = strchr(ptr, '-'); + if (ptr != NULL && ptr[1] != 0) { + high = simple_strtoul(ptr+1, NULL, 10); + set_new_water(dev, pid, high, true, &change); + } else if (ptr == NULL) + break; + } else if (!strncmp(ptr, "pidl=", 5)) { + pid_t pid = simple_strtoul(ptr + 5, NULL, 10); + ptr = strchr(ptr, '-'); + if (ptr != NULL && ptr[1] != 0) { + low = simple_strtoul(ptr+1, NULL, 10); + set_new_water(dev, pid, low, false, &change); + } else if (ptr == NULL) + break; + } else { + /* Assume the whole string is busted, and gtfo */ + DRM_ERROR("Unable to parse command %s\n", ptr); + break; + } ptr = strchr(ptr, ','); if (ptr != NULL) ptr += strspn(ptr, ", \t\n"); } - if (high != dev_priv->scheduler.high_watermark || - (low != dev_priv->scheduler.low_watermark) || - (type != dev_priv->scheduler.type)) { - if (i915_gpu_idle(dev)) - DRM_ERROR("Couldn't idle GPU before sched changes\n"); - DRM_INFO("new type = %d\n", type); - DRM_INFO("new high = %d\n", high); - DRM_INFO("new low = %d\n", low); - dev_priv->scheduler.high_watermark = high; - dev_priv->scheduler.low_watermark = low; - dev_priv->scheduler.type = type; - } - mutex_unlock(&dev->struct_mutex); out: drm_free_large(buf); -- 1.7.7.3