summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRik Faith <faith@alephnull.com>2000-08-18 18:57:56 +0000
committerRik Faith <faith@alephnull.com>2000-08-18 18:57:56 +0000
commit364d44a24cb7a32ab7ac860e9dde0cd5d082fcd7 (patch)
tree04abfc9f7e3c0908dba82b438f1b354db51e8e92
parentf0f6509a72abf3a3a0a8f26a35b7a8f3d96cbb9b (diff)
downloaddrm-364d44a24cb7a32ab7ac860e9dde0cd5d082fcd7.tar.gz
Fix ABA problem in drm_freelist_{put,try}
-rw-r--r--linux-core/drmP.h1
-rw-r--r--linux/drmP.h1
-rw-r--r--linux/lists.c41
3 files changed, 18 insertions, 25 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index 0fa20571..aa9bc01a 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -328,6 +328,7 @@ typedef struct drm_freelist {
int low_mark; /* Low water mark */
int high_mark; /* High water mark */
atomic_t wfh; /* If waiting for high mark */
+ spinlock_t lock;
} drm_freelist_t;
typedef struct drm_buf_entry {
diff --git a/linux/drmP.h b/linux/drmP.h
index 0fa20571..aa9bc01a 100644
--- a/linux/drmP.h
+++ b/linux/drmP.h
@@ -328,6 +328,7 @@ typedef struct drm_freelist {
int low_mark; /* Low water mark */
int high_mark; /* High water mark */
atomic_t wfh; /* If waiting for high mark */
+ spinlock_t lock;
} drm_freelist_t;
typedef struct drm_buf_entry {
diff --git a/linux/lists.c b/linux/lists.c
index f62495aa..5da7cc6c 100644
--- a/linux/lists.c
+++ b/linux/lists.c
@@ -116,6 +116,7 @@ int drm_freelist_create(drm_freelist_t *bl, int count)
bl->low_mark = 0;
bl->high_mark = 0;
atomic_set(&bl->wfh, 0);
+ bl->lock = SPIN_LOCK_UNLOCKED;
++bl->initialized;
return 0;
}
@@ -130,8 +131,6 @@ int drm_freelist_destroy(drm_freelist_t *bl)
int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
{
- drm_buf_t *old, *prev;
- int count = 0;
drm_device_dma_t *dma = dev->dma;
if (!dma) {
@@ -152,15 +151,12 @@ int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
drm_histogram_compute(dev, buf);
#endif
buf->list = DRM_LIST_FREE;
- do {
- old = bl->next;
- buf->next = old;
- prev = cmpxchg(&bl->next, old, buf);
- if (++count > DRM_LOOPING_LIMIT) {
- DRM_ERROR("Looping\n");
- return 1;
- }
- } while (prev != old);
+
+ spin_lock(&bl->lock);
+ buf->next = bl->next;
+ bl->next = buf;
+ spin_unlock(&bl->lock);
+
atomic_inc(&bl->count);
if (atomic_read(&bl->count) > dma->buf_count) {
DRM_ERROR("%d of %d buffers free after addition of %d\n",
@@ -177,26 +173,21 @@ int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
static drm_buf_t *drm_freelist_try(drm_freelist_t *bl)
{
- drm_buf_t *old, *new, *prev;
drm_buf_t *buf;
- int count = 0;
if (!bl) return NULL;
/* Get buffer */
- do {
- old = bl->next;
- if (!old) return NULL;
- new = bl->next->next;
- prev = cmpxchg(&bl->next, old, new);
- if (++count > DRM_LOOPING_LIMIT) {
- DRM_ERROR("Looping\n");
- return NULL;
- }
- } while (prev != old);
- atomic_dec(&bl->count);
+ spin_lock(&bl->lock);
+ if (!bl->next) {
+ spin_unlock(&bl->lock);
+ return NULL;
+ }
+ buf = bl->next;
+ bl->next = bl->next->next;
+ spin_unlock(&bl->lock);
- buf = old;
+ atomic_dec(&bl->count);
buf->next = NULL;
buf->list = DRM_LIST_NONE;
DRM_DEBUG("%d, count = %d, wfh = %d, w%d, p%d\n",