Message ID | 1418328792-28072-2-git-send-email-michael.h.nguyen@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
> -----Original Message----- > From: Nguyen, Michael H > Sent: Thursday, December 11, 2014 8:13 PM > To: intel-gfx@lists.freedesktop.org > Cc: Bloomfield, Jon; Brad Volkin > Subject: [PATCH v7 1/5] drm/i915: Implement a framework for batch buffer > pools > > From: Brad Volkin <bradley.d.volkin@intel.com> > > This adds a small module for managing a pool of batch buffers. > The only current use case is for the command parser, as described in the > kerneldoc in the patch. The code is simple, but separating it out makes it > easier to change the underlying algorithms and to extend to future use cases > should they arise. > > The interface is simple: init to create an empty pool, fini to clean it up, get to > obtain a new buffer. Note that all buffers are expected to be inactive before > cleaning up the pool. > > Locking is currently based on the caller holding the struct_mutex. > We already do that in the places where we will use the batch pool for the > command parser. > > v2: > - s/BUG_ON/WARN_ON/ for locking assertions > - Remove the cap on pool size > - Switch from alloc/free to init/fini > > v3: > - Idiomatic looping structure in _fini > - Correct handling of purged objects > - Don't return a buffer that's too much larger than needed > > v4: > - Rebased to latest -nightly > > v5: > - Remove _put() function and clean up comments to match > > v6: > - Move purged check inside the loop (danvet, from v4 1/7 feedback) > > v7: > - Use single list instead of two. (Chris W) > - s/active_list/cache_list > - Squashed in debug patches (Chris W) > drm/i915: Add a batch pool debugfs file > > It provides some useful information about the buffers in > the global command parser batch pool. > > v2: rebase on global pool instead of per-ring pools > v3: rebase > > drm/i915: Add batch pool details to i915_gem_objects debugfs > > To better account for the potentially large memory consumption > of the batch pool. > > v8: > - Keep cache in LRU order (danvet, from v6 1/5 feedback) > > Issue: VIZ-4719 > Signed-off-by: Brad Volkin <bradley.d.volkin@intel.com> > --- > Documentation/DocBook/drm.tmpl | 5 ++ > drivers/gpu/drm/i915/Makefile | 1 + > drivers/gpu/drm/i915/i915_debugfs.c | 71 +++++++++++++-- > drivers/gpu/drm/i915/i915_drv.h | 21 +++++ > drivers/gpu/drm/i915/i915_gem.c | 1 + > drivers/gpu/drm/i915/i915_gem_batch_pool.c | 135 > +++++++++++++++++++++++++++++ > 6 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 > drivers/gpu/drm/i915/i915_gem_batch_pool.c > > diff --git a/Documentation/DocBook/drm.tmpl > b/Documentation/DocBook/drm.tmpl index 25c23ca..21cbcdb 100644 > --- a/Documentation/DocBook/drm.tmpl > +++ b/Documentation/DocBook/drm.tmpl > @@ -4059,6 +4059,11 @@ int num_ioctls;</synopsis> > !Idrivers/gpu/drm/i915/i915_cmd_parser.c > </sect2> > <sect2> > + <title>Batchbuffer Pools</title> > +!Pdrivers/gpu/drm/i915/i915_gem_batch_pool.c batch pool > +!Idrivers/gpu/drm/i915/i915_gem_batch_pool.c > + </sect2> > + <sect2> > <title>Logical Rings, Logical Ring Contexts and Execlists</title> > !Pdrivers/gpu/drm/i915/intel_lrc.c Logical Rings, Logical Ring Contexts and > Execlists !Idrivers/gpu/drm/i915/intel_lrc.c > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile > index 3cf70a6..1849ffa 100644 > --- a/drivers/gpu/drm/i915/Makefile > +++ b/drivers/gpu/drm/i915/Makefile > @@ -19,6 +19,7 @@ i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o > > # GEM code > i915-y += i915_cmd_parser.o \ > + i915_gem_batch_pool.o \ > i915_gem_context.o \ > i915_gem_render_state.o \ > i915_gem_debug.o \ > diff --git a/drivers/gpu/drm/i915/i915_debugfs.c > b/drivers/gpu/drm/i915/i915_debugfs.c > index 479e0c1..5d96d94 100644 > --- a/drivers/gpu/drm/i915/i915_debugfs.c > +++ b/drivers/gpu/drm/i915/i915_debugfs.c > @@ -359,6 +359,33 @@ static int per_file_stats(int id, void *ptr, void *data) > return 0; > } > > +#define print_file_stats(m, name, stats) \ > + seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, > %zu global, %zu shared, %zu unbound)\n", \ > + name, \ > + stats.count, \ > + stats.total, \ > + stats.active, \ > + stats.inactive, \ > + stats.global, \ > + stats.shared, \ > + stats.unbound) > + > +static void print_batch_pool_stats(struct seq_file *m, > + struct drm_i915_private *dev_priv) { > + struct drm_i915_gem_object *obj; > + struct file_stats stats; > + > + memset(&stats, 0, sizeof(stats)); > + > + list_for_each_entry(obj, > + &dev_priv->mm.batch_pool.cache_list, > + batch_pool_list) > + per_file_stats(0, obj, &stats); > + > + print_file_stats(m, "batch pool", stats); } > + > #define count_vmas(list, member) do { \ > list_for_each_entry(vma, list, member) { \ > size += i915_gem_obj_ggtt_size(vma->obj); \ @@ -441,6 > +468,9 @@ static int i915_gem_object_info(struct seq_file *m, void* data) > dev_priv->gtt.mappable_end - dev_priv->gtt.base.start); > > seq_putc(m, '\n'); > + print_batch_pool_stats(m, dev_priv); > + > + seq_putc(m, '\n'); > list_for_each_entry_reverse(file, &dev->filelist, lhead) { > struct file_stats stats; > struct task_struct *task; > @@ -458,15 +488,7 @@ static int i915_gem_object_info(struct seq_file *m, > void* data) > */ > rcu_read_lock(); > task = pid_task(file->pid, PIDTYPE_PID); > - seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu > inactive, %zu global, %zu shared, %zu unbound)\n", > - task ? task->comm : "<unknown>", > - stats.count, > - stats.total, > - stats.active, > - stats.inactive, > - stats.global, > - stats.shared, > - stats.unbound); > + print_file_stats(m, task ? task->comm : "<unknown>", stats); > rcu_read_unlock(); > } > > @@ -583,6 +605,36 @@ static int i915_gem_pageflip_info(struct seq_file *m, > void *data) > return 0; > } > > +static int i915_gem_batch_pool_info(struct seq_file *m, void *data) { > + struct drm_info_node *node = m->private; > + struct drm_device *dev = node->minor->dev; > + struct drm_i915_private *dev_priv = dev->dev_private; > + struct drm_i915_gem_object *obj; > + int count = 0; > + int ret; > + > + ret = mutex_lock_interruptible(&dev->struct_mutex); > + if (ret) > + return ret; > + > + seq_puts(m, "cache:\n"); > + list_for_each_entry(obj, > + &dev_priv->mm.batch_pool.cache_list, > + batch_pool_list) { > + seq_puts(m, " "); > + describe_obj(m, obj); > + seq_putc(m, '\n'); > + count++; > + } > + > + seq_printf(m, "total: %d\n", count); > + > + mutex_unlock(&dev->struct_mutex); > + > + return 0; > +} > + > static int i915_gem_request_info(struct seq_file *m, void *data) { > struct drm_info_node *node = m->private; @@ -4356,6 +4408,7 @@ > static const struct drm_info_list i915_debugfs_list[] = { > {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS}, > {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, > {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, > + {"i915_gem_batch_pool", i915_gem_batch_pool_info, 0}, > {"i915_frequency_info", i915_frequency_info, 0}, > {"i915_drpc_info", i915_drpc_info, 0}, > {"i915_emon_status", i915_emon_status, 0}, diff --git > a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h > index d68c75f..e1e503a 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -1142,6 +1142,11 @@ struct intel_l3_parity { > int which_slice; > }; > > +struct i915_gem_batch_pool { > + struct drm_device *dev; > + struct list_head cache_list; > +}; > + > struct i915_gem_mm { > /** Memory allocator for GTT stolen memory */ > struct drm_mm stolen; > @@ -1155,6 +1160,13 @@ struct i915_gem_mm { > */ > struct list_head unbound_list; > > + /* > + * A pool of objects to use as shadow copies of client batch buffers > + * when the command parser is enabled. Prevents the client from > + * modifying the batch contents after software parsing. > + */ > + struct i915_gem_batch_pool batch_pool; > + > /** Usable portion of the GTT for GEM */ > unsigned long stolen_base; /* limited to low memory (32-bit) */ > > @@ -1885,6 +1897,8 @@ struct drm_i915_gem_object { > /** Used in execbuf to temporarily hold a ref */ > struct list_head obj_exec_link; > > + struct list_head batch_pool_list; > + > /** > * This is set if the object is on the active lists (has pending > * rendering and so a non-zero seqno), and is not set if it i s on @@ - > 2889,6 +2903,13 @@ void i915_destroy_error_state(struct drm_device > *dev); void i915_get_extra_instdone(struct drm_device *dev, uint32_t > *instdone); const char *i915_cache_level_str(struct drm_i915_private *i915, > int type); > > +/* i915_gem_batch_pool.c */ > +void i915_gem_batch_pool_init(struct drm_device *dev, > + struct i915_gem_batch_pool *pool); void > +i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool); struct > +drm_i915_gem_object* i915_gem_batch_pool_get(struct > i915_gem_batch_pool > +*pool, size_t size); > + > /* i915_cmd_parser.c */ > int i915_cmd_parser_get_version(void); > int i915_cmd_parser_init_ring(struct intel_engine_cs *ring); diff --git > a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c > index de241eb..2f14ae1 100644 > --- a/drivers/gpu/drm/i915/i915_gem.c > +++ b/drivers/gpu/drm/i915/i915_gem.c > @@ -4384,6 +4384,7 @@ void i915_gem_object_init(struct > drm_i915_gem_object *obj, > INIT_LIST_HEAD(&obj->ring_list); > INIT_LIST_HEAD(&obj->obj_exec_link); > INIT_LIST_HEAD(&obj->vma_list); > + INIT_LIST_HEAD(&obj->batch_pool_list); > > obj->ops = ops; > > diff --git a/drivers/gpu/drm/i915/i915_gem_batch_pool.c > b/drivers/gpu/drm/i915/i915_gem_batch_pool.c > new file mode 100644 > index 0000000..6016125 > --- /dev/null > +++ b/drivers/gpu/drm/i915/i915_gem_batch_pool.c > @@ -0,0 +1,135 @@ > +/* > + * Copyright © 2014 Intel Corporation > + * > + * Permission is hereby granted, free of charge, to any person > +obtaining a > + * copy of this software and associated documentation files (the > +"Software"), > + * to deal in the Software without restriction, including without > +limitation > + * the rights to use, copy, modify, merge, publish, distribute, > +sublicense, > + * and/or sell copies of the Software, and to permit persons to whom > +the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the > +next > + * paragraph) shall be included in all copies or substantial portions > +of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > +EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > +MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO > EVENT > +SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, > DAMAGES OR > +OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > +ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > OTHER > +DEALINGS > + * IN THE SOFTWARE. > + * > + */ > + > +#include "i915_drv.h" > + > +/** > + * DOC: batch pool > + * > + * In order to submit batch buffers as 'secure', the software command > +parser > + * must ensure that a batch buffer cannot be modified after parsing. It > +does > + * this by copying the user provided batch buffer contents to a kernel > +owned > + * buffer from which the hardware will actually execute, and by > +carefully > + * managing the address space bindings for such buffers. > + * > + * The batch pool framework provides a mechanism for the driver to > +manage a > + * set of scratch buffers to use for this purpose. The framework can be > + * extended to support other uses cases should they arise. > + */ > + > +/** > + * i915_gem_batch_pool_init() - initialize a batch buffer pool > + * @dev: the drm device > + * @pool: the batch buffer pool > + */ > +void i915_gem_batch_pool_init(struct drm_device *dev, > + struct i915_gem_batch_pool *pool) { > + pool->dev = dev; > + INIT_LIST_HEAD(&pool->cache_list); > +} > + > +/** > + * i915_gem_batch_pool_fini() - clean up a batch buffer pool > + * @pool: the pool to clean up > + * > + * Note: Callers must hold the struct_mutex. > + */ > +void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool) { > + WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex)); > + > + while (!list_empty(&pool->cache_list)) { > + struct drm_i915_gem_object *obj = > + list_first_entry(&pool->cache_list, > + struct drm_i915_gem_object, > + batch_pool_list); > + > + WARN_ON(obj->active); > + > + list_del_init(&obj->batch_pool_list); > + drm_gem_object_unreference(&obj->base); > + } > +} > + > +/** > + * i915_gem_batch_pool_get() - select a buffer from the pool > + * @pool: the batch buffer pool > + * @size: the minimum desired size of the returned buffer > + * > + * Finds or allocates a batch buffer in the pool with at least the > +requested > + * size. The caller is responsible for any domain, active/inactive, or > + * purgeability management for the returned buffer. > + * > + * Note: Callers must hold the struct_mutex > + * > + * Return: the selected batch buffer object */ struct > +drm_i915_gem_object * i915_gem_batch_pool_get(struct > +i915_gem_batch_pool *pool, > + size_t size) > +{ > + struct drm_i915_gem_object *obj = NULL; > + struct drm_i915_gem_object *tmp, *next; > + > + WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex)); > + > + list_for_each_entry_safe(tmp, next, > + &pool->cache_list, batch_pool_list) { > + > + if (tmp->active) > + continue; > + > + /* While we're looping, do some clean up */ > + if (tmp->madv == __I915_MADV_PURGED) { > + list_del(&tmp->batch_pool_list); > + drm_gem_object_unreference(&tmp->base); > + continue; > + } > + > + /* > + * Select a buffer that is at least as big as needed > + * but not 'too much' bigger. A better way to do this > + * might be to bucket the pool objects based on size. > + */ > + if (tmp->base.size >= size && > + tmp->base.size <= (2 * size)) { > + obj = tmp; > + break; > + } > + } > + > + if (!obj) { > + obj = i915_gem_alloc_object(pool->dev, size); > + if (!obj) > + return ERR_PTR(-ENOMEM); > + > + list_add_tail(&obj->batch_pool_list, &pool->cache_list); > + } > + else > + /* Keep list in LRU order */ > + list_move_tail(&obj->batch_pool_list, &pool->cache_list); > + > + return obj; > +} Reviewed-By: Jon Bloomfield <jon.bloomfield@intel.com> > -- > 1.9.1
diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 25c23ca..21cbcdb 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -4059,6 +4059,11 @@ int num_ioctls;</synopsis> !Idrivers/gpu/drm/i915/i915_cmd_parser.c </sect2> <sect2> + <title>Batchbuffer Pools</title> +!Pdrivers/gpu/drm/i915/i915_gem_batch_pool.c batch pool +!Idrivers/gpu/drm/i915/i915_gem_batch_pool.c + </sect2> + <sect2> <title>Logical Rings, Logical Ring Contexts and Execlists</title> !Pdrivers/gpu/drm/i915/intel_lrc.c Logical Rings, Logical Ring Contexts and Execlists !Idrivers/gpu/drm/i915/intel_lrc.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 3cf70a6..1849ffa 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -19,6 +19,7 @@ i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o # GEM code i915-y += i915_cmd_parser.o \ + i915_gem_batch_pool.o \ i915_gem_context.o \ i915_gem_render_state.o \ i915_gem_debug.o \ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 479e0c1..5d96d94 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -359,6 +359,33 @@ static int per_file_stats(int id, void *ptr, void *data) return 0; } +#define print_file_stats(m, name, stats) \ + seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu shared, %zu unbound)\n", \ + name, \ + stats.count, \ + stats.total, \ + stats.active, \ + stats.inactive, \ + stats.global, \ + stats.shared, \ + stats.unbound) + +static void print_batch_pool_stats(struct seq_file *m, + struct drm_i915_private *dev_priv) +{ + struct drm_i915_gem_object *obj; + struct file_stats stats; + + memset(&stats, 0, sizeof(stats)); + + list_for_each_entry(obj, + &dev_priv->mm.batch_pool.cache_list, + batch_pool_list) + per_file_stats(0, obj, &stats); + + print_file_stats(m, "batch pool", stats); +} + #define count_vmas(list, member) do { \ list_for_each_entry(vma, list, member) { \ size += i915_gem_obj_ggtt_size(vma->obj); \ @@ -441,6 +468,9 @@ static int i915_gem_object_info(struct seq_file *m, void* data) dev_priv->gtt.mappable_end - dev_priv->gtt.base.start); seq_putc(m, '\n'); + print_batch_pool_stats(m, dev_priv); + + seq_putc(m, '\n'); list_for_each_entry_reverse(file, &dev->filelist, lhead) { struct file_stats stats; struct task_struct *task; @@ -458,15 +488,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) */ rcu_read_lock(); task = pid_task(file->pid, PIDTYPE_PID); - seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu shared, %zu unbound)\n", - task ? task->comm : "<unknown>", - stats.count, - stats.total, - stats.active, - stats.inactive, - stats.global, - stats.shared, - stats.unbound); + print_file_stats(m, task ? task->comm : "<unknown>", stats); rcu_read_unlock(); } @@ -583,6 +605,36 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) return 0; } +static int i915_gem_batch_pool_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + int count = 0; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + seq_puts(m, "cache:\n"); + list_for_each_entry(obj, + &dev_priv->mm.batch_pool.cache_list, + batch_pool_list) { + seq_puts(m, " "); + describe_obj(m, obj); + seq_putc(m, '\n'); + count++; + } + + seq_printf(m, "total: %d\n", count); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + static int i915_gem_request_info(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; @@ -4356,6 +4408,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS}, {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, + {"i915_gem_batch_pool", i915_gem_batch_pool_info, 0}, {"i915_frequency_info", i915_frequency_info, 0}, {"i915_drpc_info", i915_drpc_info, 0}, {"i915_emon_status", i915_emon_status, 0}, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d68c75f..e1e503a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1142,6 +1142,11 @@ struct intel_l3_parity { int which_slice; }; +struct i915_gem_batch_pool { + struct drm_device *dev; + struct list_head cache_list; +}; + struct i915_gem_mm { /** Memory allocator for GTT stolen memory */ struct drm_mm stolen; @@ -1155,6 +1160,13 @@ struct i915_gem_mm { */ struct list_head unbound_list; + /* + * A pool of objects to use as shadow copies of client batch buffers + * when the command parser is enabled. Prevents the client from + * modifying the batch contents after software parsing. + */ + struct i915_gem_batch_pool batch_pool; + /** Usable portion of the GTT for GEM */ unsigned long stolen_base; /* limited to low memory (32-bit) */ @@ -1885,6 +1897,8 @@ struct drm_i915_gem_object { /** Used in execbuf to temporarily hold a ref */ struct list_head obj_exec_link; + struct list_head batch_pool_list; + /** * This is set if the object is on the active lists (has pending * rendering and so a non-zero seqno), and is not set if it i s on @@ -2889,6 +2903,13 @@ void i915_destroy_error_state(struct drm_device *dev); void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone); const char *i915_cache_level_str(struct drm_i915_private *i915, int type); +/* i915_gem_batch_pool.c */ +void i915_gem_batch_pool_init(struct drm_device *dev, + struct i915_gem_batch_pool *pool); +void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool); +struct drm_i915_gem_object* +i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool, size_t size); + /* i915_cmd_parser.c */ int i915_cmd_parser_get_version(void); int i915_cmd_parser_init_ring(struct intel_engine_cs *ring); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index de241eb..2f14ae1 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4384,6 +4384,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, INIT_LIST_HEAD(&obj->ring_list); INIT_LIST_HEAD(&obj->obj_exec_link); INIT_LIST_HEAD(&obj->vma_list); + INIT_LIST_HEAD(&obj->batch_pool_list); obj->ops = ops; diff --git a/drivers/gpu/drm/i915/i915_gem_batch_pool.c b/drivers/gpu/drm/i915/i915_gem_batch_pool.c new file mode 100644 index 0000000..6016125 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_batch_pool.c @@ -0,0 +1,135 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "i915_drv.h" + +/** + * DOC: batch pool + * + * In order to submit batch buffers as 'secure', the software command parser + * must ensure that a batch buffer cannot be modified after parsing. It does + * this by copying the user provided batch buffer contents to a kernel owned + * buffer from which the hardware will actually execute, and by carefully + * managing the address space bindings for such buffers. + * + * The batch pool framework provides a mechanism for the driver to manage a + * set of scratch buffers to use for this purpose. The framework can be + * extended to support other uses cases should they arise. + */ + +/** + * i915_gem_batch_pool_init() - initialize a batch buffer pool + * @dev: the drm device + * @pool: the batch buffer pool + */ +void i915_gem_batch_pool_init(struct drm_device *dev, + struct i915_gem_batch_pool *pool) +{ + pool->dev = dev; + INIT_LIST_HEAD(&pool->cache_list); +} + +/** + * i915_gem_batch_pool_fini() - clean up a batch buffer pool + * @pool: the pool to clean up + * + * Note: Callers must hold the struct_mutex. + */ +void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool) +{ + WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex)); + + while (!list_empty(&pool->cache_list)) { + struct drm_i915_gem_object *obj = + list_first_entry(&pool->cache_list, + struct drm_i915_gem_object, + batch_pool_list); + + WARN_ON(obj->active); + + list_del_init(&obj->batch_pool_list); + drm_gem_object_unreference(&obj->base); + } +} + +/** + * i915_gem_batch_pool_get() - select a buffer from the pool + * @pool: the batch buffer pool + * @size: the minimum desired size of the returned buffer + * + * Finds or allocates a batch buffer in the pool with at least the requested + * size. The caller is responsible for any domain, active/inactive, or + * purgeability management for the returned buffer. + * + * Note: Callers must hold the struct_mutex + * + * Return: the selected batch buffer object + */ +struct drm_i915_gem_object * +i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool, + size_t size) +{ + struct drm_i915_gem_object *obj = NULL; + struct drm_i915_gem_object *tmp, *next; + + WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex)); + + list_for_each_entry_safe(tmp, next, + &pool->cache_list, batch_pool_list) { + + if (tmp->active) + continue; + + /* While we're looping, do some clean up */ + if (tmp->madv == __I915_MADV_PURGED) { + list_del(&tmp->batch_pool_list); + drm_gem_object_unreference(&tmp->base); + continue; + } + + /* + * Select a buffer that is at least as big as needed + * but not 'too much' bigger. A better way to do this + * might be to bucket the pool objects based on size. + */ + if (tmp->base.size >= size && + tmp->base.size <= (2 * size)) { + obj = tmp; + break; + } + } + + if (!obj) { + obj = i915_gem_alloc_object(pool->dev, size); + if (!obj) + return ERR_PTR(-ENOMEM); + + list_add_tail(&obj->batch_pool_list, &pool->cache_list); + } + else + /* Keep list in LRU order */ + list_move_tail(&obj->batch_pool_list, &pool->cache_list); + + return obj; +}