diff options
author | Francisco Jerez <currojerez@riseup.net> | 2010-10-17 21:55:48 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-12-03 00:06:42 -0500 |
commit | f091a3d403065416b7d27221bbeb956481132ffd (patch) | |
tree | 9a6f39cbf15fbc7f4b45379c628f207590641b12 /drivers/gpu | |
parent | 36c952e8b3bad911ef13111058f0a5c4b361027e (diff) |
drm/nouveau: Implement weak channel references.
nouveau_channel_ref() takes a "weak" channel reference that doesn't
prevent the hardware channel resources from being released, it just
keeps the channel data structure alive.
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_channel.c | 55 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 8 |
2 files changed, 47 insertions, 16 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, | |||
240 | struct nouveau_channel * | 241 | struct nouveau_channel * |
241 | nouveau_channel_get_unlocked(struct nouveau_channel *ref) | 242 | nouveau_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 | ||
249 | struct nouveau_channel * | 252 | struct 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 | ||
354 | void | 354 | void |
@@ -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 | ||
361 | static void | ||
362 | nouveau_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 | |||
373 | void | ||
374 | nouveau_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 */ |
362 | void | 387 | void |
363 | nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv) | 388 | nouveau_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 | } |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index e6708be58bc..d152bc334fd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -165,7 +165,11 @@ struct nouveau_channel { | |||
165 | struct drm_device *dev; | 165 | struct drm_device *dev; |
166 | int id; | 166 | int id; |
167 | 167 | ||
168 | atomic_t refcount; | 168 | /* references to the channel data structure */ |
169 | struct kref ref; | ||
170 | /* users of the hardware channel resources, the hardware | ||
171 | * context will be kicked off when it reaches zero. */ | ||
172 | atomic_t users; | ||
169 | struct mutex mutex; | 173 | struct mutex mutex; |
170 | 174 | ||
171 | /* owner of this fifo */ | 175 | /* owner of this fifo */ |
@@ -808,6 +812,8 @@ extern struct nouveau_channel * | |||
808 | nouveau_channel_get(struct drm_device *, struct drm_file *, int id); | 812 | nouveau_channel_get(struct drm_device *, struct drm_file *, int id); |
809 | extern void nouveau_channel_put_unlocked(struct nouveau_channel **); | 813 | extern void nouveau_channel_put_unlocked(struct nouveau_channel **); |
810 | extern void nouveau_channel_put(struct nouveau_channel **); | 814 | extern void nouveau_channel_put(struct nouveau_channel **); |
815 | extern void nouveau_channel_ref(struct nouveau_channel *chan, | ||
816 | struct nouveau_channel **pchan); | ||
811 | 817 | ||
812 | /* nouveau_object.c */ | 818 | /* nouveau_object.c */ |
813 | extern int nouveau_gpuobj_early_init(struct drm_device *); | 819 | extern int nouveau_gpuobj_early_init(struct drm_device *); |