aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nouveau_channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_channel.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_channel.c55
1 files changed, 40 insertions, 15 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index 38929fd3f4e..76033c509d3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -125,7 +125,8 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
125 chan->vram_handle = vram_handle; 125 chan->vram_handle = vram_handle;
126 chan->gart_handle = gart_handle; 126 chan->gart_handle = gart_handle;
127 127
128 atomic_set(&chan->refcount, 1); 128 kref_init(&chan->ref);
129 atomic_set(&chan->users, 1);
129 mutex_init(&chan->mutex); 130 mutex_init(&chan->mutex);
130 mutex_lock(&chan->mutex); 131 mutex_lock(&chan->mutex);
131 132
@@ -133,7 +134,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
133 spin_lock_irqsave(&dev_priv->channels.lock, flags); 134 spin_lock_irqsave(&dev_priv->channels.lock, flags);
134 for (chan->id = 0; chan->id < pfifo->channels; chan->id++) { 135 for (chan->id = 0; chan->id < pfifo->channels; chan->id++) {
135 if (!dev_priv->channels.ptr[chan->id]) { 136 if (!dev_priv->channels.ptr[chan->id]) {
136 dev_priv->channels.ptr[chan->id] = chan; 137 nouveau_channel_ref(chan, &dev_priv->channels.ptr[chan->id]);
137 break; 138 break;
138 } 139 }
139 } 140 }
@@ -240,10 +241,12 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
240struct nouveau_channel * 241struct nouveau_channel *
241nouveau_channel_get_unlocked(struct nouveau_channel *ref) 242nouveau_channel_get_unlocked(struct nouveau_channel *ref)
242{ 243{
243 if (likely(ref && atomic_inc_not_zero(&ref->refcount))) 244 struct nouveau_channel *chan = NULL;
244 return ref;
245 245
246 return NULL; 246 if (likely(ref && atomic_inc_not_zero(&ref->users)))
247 nouveau_channel_ref(ref, &chan);
248
249 return chan;
247} 250}
248 251
249struct nouveau_channel * 252struct nouveau_channel *
@@ -281,15 +284,14 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
281 int ret; 284 int ret;
282 285
283 /* decrement the refcount, and we're done if there's still refs */ 286 /* decrement the refcount, and we're done if there's still refs */
284 if (likely(!atomic_dec_and_test(&chan->refcount))) { 287 if (likely(!atomic_dec_and_test(&chan->users))) {
285 *pchan = NULL; 288 nouveau_channel_ref(NULL, pchan);
286 return; 289 return;
287 } 290 }
288 291
289 /* noone wants the channel anymore */ 292 /* noone wants the channel anymore */
290 NV_DEBUG(dev, "freeing channel %d\n", chan->id); 293 NV_DEBUG(dev, "freeing channel %d\n", chan->id);
291 nouveau_debugfs_channel_fini(chan); 294 nouveau_debugfs_channel_fini(chan);
292 *pchan = NULL;
293 295
294 /* give it chance to idle */ 296 /* give it chance to idle */
295 nouveau_fence_update(chan); 297 nouveau_fence_update(chan);
@@ -333,7 +335,7 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
333 * remove it from the channel list 335 * remove it from the channel list
334 */ 336 */
335 spin_lock_irqsave(&dev_priv->channels.lock, flags); 337 spin_lock_irqsave(&dev_priv->channels.lock, flags);
336 dev_priv->channels.ptr[chan->id] = NULL; 338 nouveau_channel_ref(NULL, &dev_priv->channels.ptr[chan->id]);
337 spin_unlock_irqrestore(&dev_priv->channels.lock, flags); 339 spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
338 340
339 /* destroy any resources the channel owned */ 341 /* destroy any resources the channel owned */
@@ -345,10 +347,8 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
345 } 347 }
346 nouveau_gpuobj_channel_takedown(chan); 348 nouveau_gpuobj_channel_takedown(chan);
347 nouveau_notifier_takedown_channel(chan); 349 nouveau_notifier_takedown_channel(chan);
348 if (chan->user)
349 iounmap(chan->user);
350 350
351 kfree(chan); 351 nouveau_channel_ref(NULL, pchan);
352} 352}
353 353
354void 354void
@@ -358,6 +358,31 @@ nouveau_channel_put(struct nouveau_channel **pchan)
358 nouveau_channel_put_unlocked(pchan); 358 nouveau_channel_put_unlocked(pchan);
359} 359}
360 360
361static void
362nouveau_channel_del(struct kref *ref)
363{
364 struct nouveau_channel *chan =
365 container_of(ref, struct nouveau_channel, ref);
366
367 if (chan->user)
368 iounmap(chan->user);
369
370 kfree(chan);
371}
372
373void
374nouveau_channel_ref(struct nouveau_channel *chan,
375 struct nouveau_channel **pchan)
376{
377 if (chan)
378 kref_get(&chan->ref);
379
380 if (*pchan)
381 kref_put(&(*pchan)->ref, nouveau_channel_del);
382
383 *pchan = chan;
384}
385
361/* cleans up all the fifos from file_priv */ 386/* cleans up all the fifos from file_priv */
362void 387void
363nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv) 388nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
@@ -373,7 +398,7 @@ nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
373 if (IS_ERR(chan)) 398 if (IS_ERR(chan))
374 continue; 399 continue;
375 400
376 atomic_dec(&chan->refcount); 401 atomic_dec(&chan->users);
377 nouveau_channel_put(&chan); 402 nouveau_channel_put(&chan);
378 } 403 }
379} 404}
@@ -427,7 +452,7 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
427 &init->notifier_handle); 452 &init->notifier_handle);
428 453
429 if (ret == 0) 454 if (ret == 0)
430 atomic_inc(&chan->refcount); /* userspace reference */ 455 atomic_inc(&chan->users); /* userspace reference */
431 nouveau_channel_put(&chan); 456 nouveau_channel_put(&chan);
432 return ret; 457 return ret;
433} 458}
@@ -443,7 +468,7 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
443 if (IS_ERR(chan)) 468 if (IS_ERR(chan))
444 return PTR_ERR(chan); 469 return PTR_ERR(chan);
445 470
446 atomic_dec(&chan->refcount); 471 atomic_dec(&chan->users);
447 nouveau_channel_put(&chan); 472 nouveau_channel_put(&chan);
448 return 0; 473 return 0;
449} 474}