From patchwork Tue Aug 25 22:33:18 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 43819 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n7PN0b1K001765 for ; Tue, 25 Aug 2009 23:00:37 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 676299E942; Tue, 25 Aug 2009 16:00:36 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from fireflyinternet.com (unknown [94.30.43.99]) by gabe.freedesktop.org (Postfix) with ESMTP id 0E2279E88C for ; Tue, 25 Aug 2009 16:00:31 -0700 (PDT) Received: from localhost.localdomain (unverified [78.156.66.37]) by fireflyinternet.com (Firefly Internet SMTP) with ESMTP id 47578667-1948518 for multiple; Tue, 25 Aug 2009 23:33:33 +0100 From: Chris Wilson To: intel-gfx@lists.freedesktop.org Date: Tue, 25 Aug 2009 23:33:18 +0100 Message-Id: <1251239598-20408-2-git-send-email-chris@chris-wilson.co.uk> X-Mailer: git-send-email 1.6.3.3 In-Reply-To: <1251239598-20408-1-git-send-email-chris@chris-wilson.co.uk> References: <1251239598-20408-1-git-send-email-chris@chris-wilson.co.uk> X-Originating-IP: 78.156.66.37 Subject: [Intel-gfx] [PATCH 2/2] drm/i915: i915_trace X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.9 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Mime-version: 1.0 Sender: intel-gfx-bounces@lists.freedesktop.org Errors-To: intel-gfx-bounces@lists.freedesktop.org Record all object and requests independent of ftrace and other system-wide tracers. Signed-off-by: Chris Wilson --- drivers/gpu/drm/Kconfig | 9 + drivers/gpu/drm/i915/Makefile | 7 +- drivers/gpu/drm/i915/i915_dma.c | 5 + drivers/gpu/drm/i915/i915_drv.h | 13 + drivers/gpu/drm/i915/i915_trace.c | 559 +++++++++++++++++++++++++++++++++++++ include/drm/i915_drm.h | 36 +++ 6 files changed, 626 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/i915/i915_trace.c diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 39b393d..1a138d6 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -111,6 +111,15 @@ config DRM_I915_KMS the driver to bind to PCI devices, which precludes loading things like intelfb. +config DRM_I915_TRACE + bool "i915 trace" + depends on DRM_I915 + select GENERIC_TRACER + help + Choose this option if you want to enable event tracing in the + i915 driver. This is used to identify performance problems + within the driver and applications. If unsure, say N. + endchoice config DRM_MGA diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 30d6b99..3141553 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -28,7 +28,8 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ dvo_tfp410.o \ dvo_sil164.o -i915-$(CONFIG_ACPI) += i915_opregion.o -i915-$(CONFIG_COMPAT) += i915_ioc32.o +i915-$(CONFIG_ACPI) += i915_opregion.o +i915-$(CONFIG_COMPAT) += i915_ioc32.o +i915-$(CONFIG_DRM_I915_TRACE) += i915_trace.o -obj-$(CONFIG_DRM_I915) += i915.o +obj-$(CONFIG_DRM_I915) += i915.o diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 50d1f78..718ee5e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -32,6 +32,7 @@ #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" +#include "i915_trace.h" #define I915_DRV "i915_drv" @@ -1261,6 +1262,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (!IS_IGDNG(dev)) intel_opregion_init(dev, 0); + i915_trace_init(dev); + return 0; out_workqueue_free: @@ -1278,6 +1281,8 @@ int i915_driver_unload(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + i915_trace_cleanup(dev); + destroy_workqueue(dev_priv->wq); io_mapping_free(dev_priv->mm.gtt_mapping); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7537f57..2f0ca4f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -149,6 +149,8 @@ struct drm_i915_error_state { struct timeval time; }; +struct i915_trace; + typedef struct drm_i915_private { struct drm_device *dev; @@ -439,6 +441,8 @@ typedef struct drm_i915_private { struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; } mm; struct sdvo_device_mapping sdvo_mappings[2]; + + struct i915_trace *trace; } drm_i915_private_t; /** driver private structure attached to each drm_gem_object */ @@ -734,6 +738,15 @@ extern int i915_restore_state(struct drm_device *dev); extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); +#if CONFIG_DRM_I915_TRACE +/* i915_trace.c */ +extern int i915_trace_init(struct drm_device *dev); +extern void i915_trace_cleanup(struct drm_device *dev); +#else +static inline int i915_trace_init(struct drm_device *dev) { return 0 } +static inline void i915_trace_cleanup(struct drm_device *dev) { } +#endif + #ifdef CONFIG_ACPI /* i915_opregion.c */ extern int intel_opregion_init(struct drm_device *dev, int resume); diff --git a/drivers/gpu/drm/i915/i915_trace.c b/drivers/gpu/drm/i915/i915_trace.c new file mode 100644 index 0000000..31ef892 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_trace.c @@ -0,0 +1,559 @@ +/* + * Copyright © 2009 Chris Wilson + * + * Tracing infrastructure for i915 performance monitoring + * + * See intel-gpu-tools/trace + */ + +#include +#include +#include + +#include "drm/drmP.h" +#include "drm/i915_drm.h" +#include "i915_drv.h" + +#define CREATE_TRACE_POINTS +#include "i915_trace.h" + +#define RING_BUFFER_SIZE (16*4096) + +struct i915_trace { + struct kref kref; + struct drm_device *dev; + struct dentry *dentry; + struct ring_buffer *ring_buffer; + atomic_t event_count; + + unsigned long flags; + wait_queue_head_t wait; + struct delayed_work work; +}; + +enum { + TRACE_ACTIVE_FLAG, + TRACE_PROBED_FLAG, + TRACE_IRQ_FLAG +}; + +static void i915_trace_kref_release(struct kref *kref); + +static void +i915_trace_header(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_trace *trace = dev_priv->trace; + struct i915_trace_event event; + + event.time = ktime_to_ns(ktime_get()); + event.minor = dev->primary->index; + event.seqno = I915_TRACE_MAGIC; + event.id = I915_TRACE_HEADER; + event.arg1 = I915_TRACE_VERSION; + event.arg2 = sizeof(event); + + ring_buffer_write(trace->ring_buffer, sizeof(event), &event); +} + +/* objects */ +static void +i915_trace_object(struct drm_device *dev, + int event_id, + struct drm_gem_object *obj, + int arg1, + int arg2) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_trace *trace = dev_priv->trace; + struct i915_trace_event event; + + if (!test_bit(TRACE_ACTIVE_FLAG, &trace->flags)) + return; + + event.time = ktime_to_ns(ktime_get()); + event.minor = dev->primary->index; + event.obj = (u64) (uintptr_t) obj; + event.id = event_id; + event.arg1 = arg1; + event.arg2 = arg2; + + if (ring_buffer_write(trace->ring_buffer, sizeof(event), &event) == 0 && + atomic_add_negative(-1, &trace->event_count)) + wake_up_interruptible(&trace->wait); + queue_delayed_work(dev_priv->wq, &trace->work, HZ); +} + +static void +i915_trace_gem_object_create(struct drm_gem_object *obj) +{ + i915_trace_object(obj->dev, + I915_TRACE_OBJECT_CREATE, obj, obj->size, 0); +} + +static void +i915_trace_gem_object_bind(struct drm_gem_object *obj, u32 gtt_offset) +{ + i915_trace_object(obj->dev, + I915_TRACE_OBJECT_BIND, obj, gtt_offset, 0); +} + +static void +i915_trace_gem_object_clflush(struct drm_gem_object *obj) +{ + i915_trace_object(obj->dev, + I915_TRACE_OBJECT_CLFLUSH, obj, 0, 0); +} + +static void +i915_trace_gem_object_change_domain(struct drm_gem_object *obj, + uint32_t old_read_domains, + uint32_t old_write_domain) +{ + /* Would prefer to filter these out at source */ + if (old_read_domains == obj->read_domains && + old_write_domain == obj->write_domain) + return; + + i915_trace_object(obj->dev, + I915_TRACE_OBJECT_CHANGE_DOMAIN, obj, + obj->read_domains | (old_read_domains << 16), + obj->write_domain | (old_write_domain << 16)); +} + +static void +i915_trace_gem_object_get_fence(struct drm_gem_object *obj, + int fence, + int tiling_mode) +{ + i915_trace_object(obj->dev, + I915_TRACE_OBJECT_GET_FENCE, obj, + fence, tiling_mode); +} + +static void +i915_trace_gem_object_unbind(struct drm_gem_object *obj) +{ + i915_trace_object(obj->dev, + I915_TRACE_OBJECT_UNBIND, obj, 0, 0); +} + +static void +i915_trace_gem_object_destroy(struct drm_gem_object *obj) +{ + i915_trace_object(obj->dev, + I915_TRACE_OBJECT_DESTROY, obj, 0, 0); +} + +/* requests */ +static void +i915_trace_request(struct drm_device *dev, + int event_id, u32 seqno, + u32 arg1, u32 arg2) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_trace *trace = dev_priv->trace; + struct i915_trace_event event; + + if (!test_bit(TRACE_ACTIVE_FLAG, &trace->flags)) + return; + + event.time = ktime_to_ns(ktime_get()); + event.minor = dev->primary->index; + event.seqno = seqno; + event.id = event_id; + event.arg1 = arg1; + event.arg2 = arg2; + + if (ring_buffer_write(trace->ring_buffer, sizeof(event), &event) == 0 && + atomic_add_negative(-1, &trace->event_count)) + wake_up_interruptible(&trace->wait); + queue_delayed_work(dev_priv->wq, &trace->work, HZ); +} + +static void +i915_trace_gem_request_submit(struct drm_device *dev, u32 seqno) +{ + i915_trace_request(dev, I915_TRACE_REQUEST_SUBMIT, seqno, 0, 0); +} + +static void +i915_trace_gem_request_flush(struct drm_device *dev, u32 seqno, + u32 flush_domains, u32 invalidate_domains) +{ + i915_trace_request(dev, I915_TRACE_REQUEST_FLUSH, seqno, + flush_domains, invalidate_domains); +} + +static void +i915_trace_gem_request_complete(struct drm_device *dev, u32 seqno) +{ + i915_trace_request(dev, I915_TRACE_REQUEST_COMPLETE, seqno, 0, 0); +} + +static void +i915_trace_gem_request_retire(struct drm_device *dev, u32 seqno) +{ + i915_trace_request(dev, I915_TRACE_REQUEST_RETIRE, seqno, 0, 0); +} + +static void +i915_trace_gem_request_wait_begin(struct drm_device *dev, u32 seqno) +{ + i915_trace_request(dev, I915_TRACE_REQUEST_WAIT_BEGIN, seqno, 0, 0); + +} + +static void +i915_trace_gem_request_wait_end(struct drm_device *dev, u32 seqno) +{ + i915_trace_request(dev, I915_TRACE_REQUEST_WAIT_END, seqno, 0, 0); +} + +/* debugfs interface */ +static DEFINE_SPINLOCK(probes_lock); +static int probes_refcnt; + +static int +i915_probes_register(void) +{ +#define R(x) ret |= register_trace_i915_##x(i915_trace_##x) + int ret = 0; + + spin_lock(&probes_lock); + if (probes_refcnt++ == 0) { + R(gem_object_create); + R(gem_object_bind); + R(gem_object_clflush); + R(gem_object_change_domain); + R(gem_object_get_fence); + R(gem_object_unbind); + R(gem_object_destroy); + + R(gem_request_submit); + R(gem_request_flush); + R(gem_request_complete); + R(gem_request_retire); + R(gem_request_wait_begin); + R(gem_request_wait_end); + } + spin_unlock(&probes_lock); + + return ret; +#undef R +} + +static void +i915_probes_unregister(void) +{ +#define U(x) unregister_trace_i915_##x(i915_trace_##x) + spin_lock(&probes_lock); + if (--probes_refcnt == 0) { + U(gem_object_create); + U(gem_object_bind); + U(gem_object_clflush); + U(gem_object_change_domain); + U(gem_object_get_fence); + U(gem_object_unbind); + U(gem_object_destroy); + + U(gem_request_submit); + U(gem_request_flush); + U(gem_request_complete); + U(gem_request_retire); + U(gem_request_wait_begin); + U(gem_request_wait_end); + + tracepoint_synchronize_unregister(); + } + spin_unlock(&probes_lock); +#undef U +} + +static int +i915_trace_open(struct inode *inode, struct file *filp) +{ + struct i915_trace *trace = inode->i_private; + int err; + + kref_get (&trace->kref); + filp->private_data = trace; + + if (test_and_set_bit_lock(TRACE_ACTIVE_FLAG, &trace->flags)) + return -EBUSY; + + if (!test_and_set_bit(TRACE_PROBED_FLAG, &trace->flags)) { + err = i915_probes_register(); + if (err) + goto fail; + } + + if (!test_and_set_bit(TRACE_IRQ_FLAG, &trace->flags)) + i915_user_irq_get(trace->dev); + + ring_buffer_reset(trace->ring_buffer); + atomic_set(&trace->event_count, + PAGE_SIZE / sizeof(struct i915_trace_event)); + i915_trace_header(trace->dev); + + return 0; + +fail: + __clear_bit_unlock(TRACE_ACTIVE_FLAG, &trace->flags); + return err; +} + +static struct ring_buffer_event * +_ring_buffer_consume_next(struct ring_buffer *ring_buffer, u64 *ts_out) +{ + int cpu, next_cpu = -1; + struct ring_buffer_event *event; + u64 next_ts = (u64) - 1, ts; + + for_each_possible_cpu(cpu) { + event = ring_buffer_peek(ring_buffer, cpu, &ts); + if (event == NULL) + continue; + + if (ts < next_ts) { + next_cpu = cpu; + next_ts = ts; + } + } + + if (next_cpu < 0) + return NULL; + + return ring_buffer_consume(ring_buffer, next_cpu, ts_out); +} + +static ssize_t +i915_trace_read(struct file *filp, char __user *ubuf, + size_t max, loff_t *ppos) +{ + struct i915_trace *trace = filp->private_data; + u64 ts; + size_t copied; + + /* ignore partial reads */ + if (*ppos || max < sizeof(struct i915_trace_event)) + return -EINVAL; + if (trace->dev == NULL) + return 0; + + wait_event_interruptible(trace->wait, + !ring_buffer_empty(trace->ring_buffer)); + + if (signal_pending(current)) + return -EINTR; + + copied = 0; + do { + struct ring_buffer_event *rb_event; + + rb_event = _ring_buffer_consume_next(trace->ring_buffer, &ts); + if (rb_event == NULL) + break; + + if (copy_to_user(ubuf + copied, + ring_buffer_event_data(rb_event), + sizeof(struct i915_trace_event))) + return -EFAULT; + + copied += sizeof(struct i915_trace_event); + } while (copied + sizeof(struct i915_trace_event) <= max); + atomic_add(copied / sizeof(struct i915_trace_event), + &trace->event_count); + + return copied; +} + +static ssize_t +i915_trace_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + size_t read = 0; + int i, set = 1; + ssize_t ret; + char buf[128]; + char *event; + char ch; + + if (!cnt || cnt < 0) + return 0; + + ret = get_user(ch, ubuf++); + if (ret) + return ret; + read++; + cnt--; + + /* skip white space */ + while (cnt && isspace(ch)) { + ret = get_user(ch, ubuf++); + if (ret) + return ret; + + read++; + cnt--; + } + if (cnt == 0) { + filp->f_pos += read; + return read; + } + + i = 0; + while (cnt && !isspace(ch)) { + if (!i && ch == '!') + set = 0; + else + buf[i++] = ch; + + ret = get_user(ch, ubuf++); + if (ret) + return ret; + + read++; + cnt--; + + if (i == sizeof (buf) - 1) + break; + } + buf[i] = 0; + + event = buf; + if (i == 0 || (i == 1 && buf[0] == '*')) + event = NULL; + + ret = trace_set_clr_event(TRACE_SYSTEM_STRING, event, set); + if (ret) + return ret; + + filp->f_pos += read; + return read; +} + +static unsigned int +i915_trace_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct i915_trace *trace = filp->private_data; + unsigned int mask = POLLOUT | POLLWRNORM; + + if (atomic_read(&trace->event_count) <= 0) + return mask | POLLIN | POLLRDNORM; + + if (trace->dev == NULL) + return POLLHUP; + + poll_wait(filp, &trace->wait, wait); + + if (atomic_read(&trace->event_count) <= 0) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static int +i915_trace_release(struct inode *inode, struct file *filp) +{ + struct i915_trace *trace = filp->private_data; + + if (test_and_clear_bit(TRACE_IRQ_FLAG, &trace->flags)) + i915_user_irq_put(trace->dev); + + __clear_bit_unlock(TRACE_ACTIVE_FLAG, &trace->flags); + kref_put(&trace->kref, i915_trace_kref_release); + + return 0; +} + +static const struct file_operations i915_trace_fops = { + .open = i915_trace_open, + .read = i915_trace_read, + .write = i915_trace_write, + .poll = i915_trace_poll, + .release = i915_trace_release, +}; + +static void +i915_trace_kref_release(struct kref *kref) +{ + struct i915_trace *trace = container_of(kref, struct i915_trace, kref); + + if (test_bit(TRACE_PROBED_FLAG, &trace->flags)) + i915_probes_unregister(); + + ring_buffer_free(trace->ring_buffer); + kfree(trace); +} + +static void +i915_trace_flush(struct work_struct *work) +{ + struct i915_trace *trace = container_of(work, + struct i915_trace, work.work); + wake_up_interruptible(&trace->wait); +} + +int +i915_trace_init(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_trace *trace; + + trace = kcalloc(1, sizeof(*trace), GFP_KERNEL); + if (trace == NULL) + return -ENOMEM; + + kref_init(&trace->kref); + trace->dev = dev; + trace->flags = 0; + + init_waitqueue_head(&trace->wait); + INIT_DELAYED_WORK(&trace->work, i915_trace_flush); + + trace->ring_buffer = ring_buffer_alloc(RING_BUFFER_SIZE, 0); + if (trace->ring_buffer == NULL) { + kfree(trace); + return -ENOMEM; + } + + trace->dentry = debugfs_create_file("i915_trace", S_IRUGO | S_IWUGO, + dev->primary->debugfs_root, + trace, + &i915_trace_fops); + if (IS_ERR(trace->dentry)) { + int err = PTR_ERR(trace->dentry); + ring_buffer_free(trace->ring_buffer); + kfree(trace); + return err; + } + + dev_priv->trace = trace; + + return 0; +} + +void +i915_trace_cleanup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_trace *trace = dev_priv->trace; + + if (trace == NULL) + return; + + dev_priv->trace = NULL; + + if (test_and_clear_bit(TRACE_IRQ_FLAG, &trace->flags)) + i915_user_irq_put(trace->dev); + + clear_bit(TRACE_ACTIVE_FLAG, &trace->flags); + debugfs_remove(trace->dentry); + trace->dev = NULL; + + cancel_delayed_work_sync(&trace->work); + wake_up(&trace->wait); + + kref_put(&trace->kref, i915_trace_kref_release); +} diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 8e1e925..8c716dc 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -667,4 +667,40 @@ struct drm_i915_get_pipe_from_crtc_id { __u32 pipe; }; +/* i915 tracing interface */ + +enum { + I915_TRACE_HEADER, + + I915_TRACE_OBJECT_CREATE, + I915_TRACE_OBJECT_BIND, + I915_TRACE_OBJECT_CLFLUSH, + I915_TRACE_OBJECT_CHANGE_DOMAIN, + I915_TRACE_OBJECT_GET_FENCE, + I915_TRACE_OBJECT_UNBIND, + I915_TRACE_OBJECT_DESTROY, + + I915_TRACE_REQUEST_SUBMIT, + I915_TRACE_REQUEST_FLUSH, + I915_TRACE_REQUEST_COMPLETE, + I915_TRACE_REQUEST_RETIRE, + I915_TRACE_REQUEST_WAIT_BEGIN, + I915_TRACE_REQUEST_WAIT_END, +}; + +#define I915_TRACE_VERSION 0 +#define I915_TRACE_MAGIC 0xdeadbeef + +struct i915_trace_event { + __u64 time; + __u32 minor; + __u32 id; + union { + __u64 obj; + __u32 seqno; + }; + __u32 arg1; + __u32 arg2; +}; + #endif /* _I915_DRM_H_ */