aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2008-10-17 00:18:27 -0400
committerDave Airlie <airlied@redhat.com>2008-10-22 23:45:56 -0400
commit9e44af790f8bf8c3aa8a3101fd4f9bca2e932baa (patch)
treea83337632a96be5da2078bebbb24c0cbb6d82b6a /drivers/gpu
parentfe8133dc07e613587f8e667bce769edce8490f2a (diff)
drm/i915: hold dev->struct_mutex and DRM lock during vblank ring operations
To synchronize clip lists with the X server, the DRM lock must be held while looking at drawable clip lists. To synchronize with other ring access, the ring mutex must be held while inserting commands into the ring. Failure to do the first resulted in easy visual corruption when moving windows, and the second could have corrupted the ring with DRI2. Grabbing the DRM lock involves using the DRM tasklet mechanism, grabbing the ring mutex means potentially sleeping. Deal with both of these by always running the tasklet from a work handler. Also, protect from clip list changes since the vblank request was queued by making sure the window has at least one rectangle while looking inside, preventing oopses . Signed-off-by: Keith Packard <keithp@keithp.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/drm_lock.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h10
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c2
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c138
4 files changed, 92 insertions, 60 deletions
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index a4caf95485d7..888159e03d26 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -232,6 +232,7 @@ int drm_lock_take(struct drm_lock_data *lock_data,
232 } 232 }
233 return 0; 233 return 0;
234} 234}
235EXPORT_SYMBOL(drm_lock_take);
235 236
236/** 237/**
237 * This takes a lock forcibly and hands it to context. Should ONLY be used 238 * This takes a lock forcibly and hands it to context. Should ONLY be used
@@ -299,6 +300,7 @@ int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context)
299 wake_up_interruptible(&lock_data->lock_queue); 300 wake_up_interruptible(&lock_data->lock_queue);
300 return 0; 301 return 0;
301} 302}
303EXPORT_SYMBOL(drm_lock_free);
302 304
303/** 305/**
304 * If we get here, it means that the process has called DRM_IOCTL_LOCK 306 * If we get here, it means that the process has called DRM_IOCTL_LOCK
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index eae4ed3956e0..f20ffe17df71 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -90,7 +90,7 @@ struct mem_block {
90typedef struct _drm_i915_vbl_swap { 90typedef struct _drm_i915_vbl_swap {
91 struct list_head head; 91 struct list_head head;
92 drm_drawable_t drw_id; 92 drm_drawable_t drw_id;
93 unsigned int plane; 93 unsigned int pipe;
94 unsigned int sequence; 94 unsigned int sequence;
95} drm_i915_vbl_swap_t; 95} drm_i915_vbl_swap_t;
96 96
@@ -240,6 +240,9 @@ typedef struct drm_i915_private {
240 u8 saveDACDATA[256*3]; /* 256 3-byte colors */ 240 u8 saveDACDATA[256*3]; /* 256 3-byte colors */
241 u8 saveCR[37]; 241 u8 saveCR[37];
242 242
243 /** Work task for vblank-related ring access */
244 struct work_struct vblank_work;
245
243 struct { 246 struct {
244 struct drm_mm gtt_space; 247 struct drm_mm gtt_space;
245 248
@@ -285,9 +288,6 @@ typedef struct drm_i915_private {
285 */ 288 */
286 struct delayed_work retire_work; 289 struct delayed_work retire_work;
287 290
288 /** Work task for vblank-related ring access */
289 struct work_struct vblank_work;
290
291 uint32_t next_gem_seqno; 291 uint32_t next_gem_seqno;
292 292
293 /** 293 /**
@@ -441,7 +441,7 @@ extern int i915_irq_wait(struct drm_device *dev, void *data,
441void i915_user_irq_get(struct drm_device *dev); 441void i915_user_irq_get(struct drm_device *dev);
442void i915_user_irq_put(struct drm_device *dev); 442void i915_user_irq_put(struct drm_device *dev);
443 443
444extern void i915_gem_vblank_work_handler(struct work_struct *work); 444extern void i915_vblank_work_handler(struct work_struct *work);
445extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); 445extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
446extern void i915_driver_irq_preinstall(struct drm_device * dev); 446extern void i915_driver_irq_preinstall(struct drm_device * dev);
447extern int i915_driver_irq_postinstall(struct drm_device *dev); 447extern int i915_driver_irq_postinstall(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 9ac73dd1b422..92550883d191 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2550,8 +2550,6 @@ i915_gem_load(struct drm_device *dev)
2550 INIT_LIST_HEAD(&dev_priv->mm.request_list); 2550 INIT_LIST_HEAD(&dev_priv->mm.request_list);
2551 INIT_DELAYED_WORK(&dev_priv->mm.retire_work, 2551 INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
2552 i915_gem_retire_work_handler); 2552 i915_gem_retire_work_handler);
2553 INIT_WORK(&dev_priv->mm.vblank_work,
2554 i915_gem_vblank_work_handler);
2555 dev_priv->mm.next_gem_seqno = 1; 2553 dev_priv->mm.next_gem_seqno = 1;
2556 2554
2557 i915_gem_detect_bit_6_swizzle(dev); 2555 i915_gem_detect_bit_6_swizzle(dev);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index ca55c40353a5..862375694431 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -121,6 +121,9 @@ i915_pipe_enabled(struct drm_device *dev, int pipe)
121 * Emit blits for scheduled buffer swaps. 121 * Emit blits for scheduled buffer swaps.
122 * 122 *
123 * This function will be called with the HW lock held. 123 * This function will be called with the HW lock held.
124 * Because this function must grab the ring mutex (dev->struct_mutex),
125 * it can no longer run at soft irq time. We'll fix this when we do
126 * the DRI2 swap buffer work.
124 */ 127 */
125static void i915_vblank_tasklet(struct drm_device *dev) 128static void i915_vblank_tasklet(struct drm_device *dev)
126{ 129{
@@ -141,6 +144,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)
141 u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); 144 u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24);
142 RING_LOCALS; 145 RING_LOCALS;
143 146
147 mutex_lock(&dev->struct_mutex);
148
144 if (IS_I965G(dev) && sarea_priv->front_tiled) { 149 if (IS_I965G(dev) && sarea_priv->front_tiled) {
145 cmd |= XY_SRC_COPY_BLT_DST_TILED; 150 cmd |= XY_SRC_COPY_BLT_DST_TILED;
146 dst_pitch >>= 2; 151 dst_pitch >>= 2;
@@ -150,8 +155,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)
150 src_pitch >>= 2; 155 src_pitch >>= 2;
151 } 156 }
152 157
153 counter[0] = drm_vblank_count(dev, 0); 158 counter[0] = drm_vblank_count(dev, i915_get_plane(dev, 0));
154 counter[1] = drm_vblank_count(dev, 1); 159 counter[1] = drm_vblank_count(dev, i915_get_plane(dev, 1));
155 160
156 DRM_DEBUG("\n"); 161 DRM_DEBUG("\n");
157 162
@@ -165,7 +170,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
165 list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { 170 list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {
166 drm_i915_vbl_swap_t *vbl_swap = 171 drm_i915_vbl_swap_t *vbl_swap =
167 list_entry(list, drm_i915_vbl_swap_t, head); 172 list_entry(list, drm_i915_vbl_swap_t, head);
168 int pipe = i915_get_pipe(dev, vbl_swap->plane); 173 int pipe = vbl_swap->pipe;
169 174
170 if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) 175 if ((counter[pipe] - vbl_swap->sequence) > (1<<23))
171 continue; 176 continue;
@@ -179,20 +184,19 @@ static void i915_vblank_tasklet(struct drm_device *dev)
179 184
180 drw = drm_get_drawable_info(dev, vbl_swap->drw_id); 185 drw = drm_get_drawable_info(dev, vbl_swap->drw_id);
181 186
182 if (!drw) {
183 spin_unlock(&dev->drw_lock);
184 drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
185 spin_lock(&dev_priv->swaps_lock);
186 continue;
187 }
188
189 list_for_each(hit, &hits) { 187 list_for_each(hit, &hits) {
190 drm_i915_vbl_swap_t *swap_cmp = 188 drm_i915_vbl_swap_t *swap_cmp =
191 list_entry(hit, drm_i915_vbl_swap_t, head); 189 list_entry(hit, drm_i915_vbl_swap_t, head);
192 struct drm_drawable_info *drw_cmp = 190 struct drm_drawable_info *drw_cmp =
193 drm_get_drawable_info(dev, swap_cmp->drw_id); 191 drm_get_drawable_info(dev, swap_cmp->drw_id);
194 192
195 if (drw_cmp && 193 /* Make sure both drawables are still
194 * around and have some rectangles before
195 * we look inside to order them for the
196 * blts below.
197 */
198 if (drw_cmp && drw_cmp->num_rects > 0 &&
199 drw && drw->num_rects > 0 &&
196 drw_cmp->rects[0].y1 > drw->rects[0].y1) { 200 drw_cmp->rects[0].y1 > drw->rects[0].y1) {
197 list_add_tail(list, hit); 201 list_add_tail(list, hit);
198 break; 202 break;
@@ -212,6 +216,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
212 216
213 if (nhits == 0) { 217 if (nhits == 0) {
214 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); 218 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
219 mutex_unlock(&dev->struct_mutex);
215 return; 220 return;
216 } 221 }
217 222
@@ -265,18 +270,21 @@ static void i915_vblank_tasklet(struct drm_device *dev)
265 drm_i915_vbl_swap_t *swap_hit = 270 drm_i915_vbl_swap_t *swap_hit =
266 list_entry(hit, drm_i915_vbl_swap_t, head); 271 list_entry(hit, drm_i915_vbl_swap_t, head);
267 struct drm_clip_rect *rect; 272 struct drm_clip_rect *rect;
268 int num_rects, plane; 273 int num_rects, pipe;
269 unsigned short top, bottom; 274 unsigned short top, bottom;
270 275
271 drw = drm_get_drawable_info(dev, swap_hit->drw_id); 276 drw = drm_get_drawable_info(dev, swap_hit->drw_id);
272 277
278 /* The drawable may have been destroyed since
279 * the vblank swap was queued
280 */
273 if (!drw) 281 if (!drw)
274 continue; 282 continue;
275 283
276 rect = drw->rects; 284 rect = drw->rects;
277 plane = swap_hit->plane; 285 pipe = swap_hit->pipe;
278 top = upper[plane]; 286 top = upper[pipe];
279 bottom = lower[plane]; 287 bottom = lower[pipe];
280 288
281 for (num_rects = drw->num_rects; num_rects--; rect++) { 289 for (num_rects = drw->num_rects; num_rects--; rect++) {
282 int y1 = max(rect->y1, top); 290 int y1 = max(rect->y1, top);
@@ -302,6 +310,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
302 } 310 }
303 311
304 spin_unlock_irqrestore(&dev->drw_lock, irqflags); 312 spin_unlock_irqrestore(&dev->drw_lock, irqflags);
313 mutex_unlock(&dev->struct_mutex);
305 314
306 list_for_each_safe(hit, tmp, &hits) { 315 list_for_each_safe(hit, tmp, &hits) {
307 drm_i915_vbl_swap_t *swap_hit = 316 drm_i915_vbl_swap_t *swap_hit =
@@ -350,18 +359,37 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
350} 359}
351 360
352void 361void
353i915_gem_vblank_work_handler(struct work_struct *work) 362i915_vblank_work_handler(struct work_struct *work)
354{ 363{
355 drm_i915_private_t *dev_priv; 364 drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
356 struct drm_device *dev; 365 vblank_work);
366 struct drm_device *dev = dev_priv->dev;
367 unsigned long irqflags;
368
369 if (dev->lock.hw_lock == NULL) {
370 i915_vblank_tasklet(dev);
371 return;
372 }
357 373
358 dev_priv = container_of(work, drm_i915_private_t, 374 spin_lock_irqsave(&dev->tasklet_lock, irqflags);
359 mm.vblank_work); 375 dev->locked_tasklet_func = i915_vblank_tasklet;
360 dev = dev_priv->dev; 376 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
377
378 /* Try to get the lock now, if this fails, the lock
379 * holder will execute the tasklet during unlock
380 */
381 if (!drm_lock_take(&dev->lock, DRM_KERNEL_CONTEXT))
382 return;
383
384 dev->lock.lock_time = jiffies;
385 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
386
387 spin_lock_irqsave(&dev->tasklet_lock, irqflags);
388 dev->locked_tasklet_func = NULL;
389 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
361 390
362 mutex_lock(&dev->struct_mutex);
363 i915_vblank_tasklet(dev); 391 i915_vblank_tasklet(dev);
364 mutex_unlock(&dev->struct_mutex); 392 drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT);
365} 393}
366 394
367irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) 395irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
@@ -441,12 +469,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
441 if (iir & I915_ASLE_INTERRUPT) 469 if (iir & I915_ASLE_INTERRUPT)
442 opregion_asle_intr(dev); 470 opregion_asle_intr(dev);
443 471
444 if (vblank && dev_priv->swaps_pending > 0) { 472 if (vblank && dev_priv->swaps_pending > 0)
445 if (dev_priv->ring.ring_obj == NULL) 473 schedule_work(&dev_priv->vblank_work);
446 drm_locked_tasklet(dev, i915_vblank_tasklet);
447 else
448 schedule_work(&dev_priv->mm.vblank_work);
449 }
450 474
451 return IRQ_HANDLED; 475 return IRQ_HANDLED;
452} 476}
@@ -706,7 +730,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
706{ 730{
707 drm_i915_private_t *dev_priv = dev->dev_private; 731 drm_i915_private_t *dev_priv = dev->dev_private;
708 drm_i915_vblank_swap_t *swap = data; 732 drm_i915_vblank_swap_t *swap = data;
709 drm_i915_vbl_swap_t *vbl_swap; 733 drm_i915_vbl_swap_t *vbl_swap, *vbl_old;
710 unsigned int pipe, seqtype, curseq, plane; 734 unsigned int pipe, seqtype, curseq, plane;
711 unsigned long irqflags; 735 unsigned long irqflags;
712 struct list_head *list; 736 struct list_head *list;
@@ -770,45 +794,52 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
770 } 794 }
771 } 795 }
772 796
797 vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER);
798
799 if (!vbl_swap) {
800 DRM_ERROR("Failed to allocate memory to queue swap\n");
801 drm_vblank_put(dev, pipe);
802 return -ENOMEM;
803 }
804
805 vbl_swap->drw_id = swap->drawable;
806 vbl_swap->pipe = pipe;
807 vbl_swap->sequence = swap->sequence;
808
773 spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); 809 spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
774 810
775 list_for_each(list, &dev_priv->vbl_swaps.head) { 811 list_for_each(list, &dev_priv->vbl_swaps.head) {
776 vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); 812 vbl_old = list_entry(list, drm_i915_vbl_swap_t, head);
777 813
778 if (vbl_swap->drw_id == swap->drawable && 814 if (vbl_old->drw_id == swap->drawable &&
779 vbl_swap->plane == plane && 815 vbl_old->pipe == pipe &&
780 vbl_swap->sequence == swap->sequence) { 816 vbl_old->sequence == swap->sequence) {
781 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); 817 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
782 drm_vblank_put(dev, pipe); 818 drm_vblank_put(dev, pipe);
819 drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
783 DRM_DEBUG("Already scheduled\n"); 820 DRM_DEBUG("Already scheduled\n");
784 return 0; 821 return 0;
785 } 822 }
786 } 823 }
787 824
788 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); 825 if (dev_priv->swaps_pending >= 10) {
789
790 if (dev_priv->swaps_pending >= 100) {
791 DRM_DEBUG("Too many swaps queued\n"); 826 DRM_DEBUG("Too many swaps queued\n");
827 DRM_DEBUG(" pipe 0: %d pipe 1: %d\n",
828 drm_vblank_count(dev, i915_get_plane(dev, 0)),
829 drm_vblank_count(dev, i915_get_plane(dev, 1)));
830
831 list_for_each(list, &dev_priv->vbl_swaps.head) {
832 vbl_old = list_entry(list, drm_i915_vbl_swap_t, head);
833 DRM_DEBUG("\tdrw %x pipe %d seq %x\n",
834 vbl_old->drw_id, vbl_old->pipe,
835 vbl_old->sequence);
836 }
837 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
792 drm_vblank_put(dev, pipe); 838 drm_vblank_put(dev, pipe);
839 drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
793 return -EBUSY; 840 return -EBUSY;
794 } 841 }
795 842
796 vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER);
797
798 if (!vbl_swap) {
799 DRM_ERROR("Failed to allocate memory to queue swap\n");
800 drm_vblank_put(dev, pipe);
801 return -ENOMEM;
802 }
803
804 DRM_DEBUG("\n");
805
806 vbl_swap->drw_id = swap->drawable;
807 vbl_swap->plane = plane;
808 vbl_swap->sequence = swap->sequence;
809
810 spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
811
812 list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head); 843 list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head);
813 dev_priv->swaps_pending++; 844 dev_priv->swaps_pending++;
814 845
@@ -835,6 +866,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
835 866
836 spin_lock_init(&dev_priv->swaps_lock); 867 spin_lock_init(&dev_priv->swaps_lock);
837 INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); 868 INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
869 INIT_WORK(&dev_priv->vblank_work, i915_vblank_work_handler);
838 dev_priv->swaps_pending = 0; 870 dev_priv->swaps_pending = 0;
839 871
840 /* Set initial unmasked IRQs to just the selected vblank pipes. */ 872 /* Set initial unmasked IRQs to just the selected vblank pipes. */