1 /* lists.c -- Buffer list handling routines -*- linux-c -*-
2  * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
3  *
4  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6  * All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the next
16  * paragraph) shall be included in all copies or substantial portions of the
17  * Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  *
27  * Authors:
28  *    Rickard E. (Rik) Faith <faith@valinux.com>
29  *
30  */
31 
32 #define __NO_VERSION__
33 #include "drmP.h"
34 
drm_waitlist_create(drm_waitlist_t * bl,int count)35 int drm_waitlist_create(drm_waitlist_t *bl, int count)
36 {
37 	if (bl->count) return -EINVAL;
38 
39 	bl->count      = count;
40 	bl->bufs       = drm_alloc((bl->count + 2) * sizeof(*bl->bufs),
41 				   DRM_MEM_BUFLISTS);
42 	bl->rp	       = bl->bufs;
43 	bl->wp	       = bl->bufs;
44 	bl->end	       = &bl->bufs[bl->count+1];
45 	bl->write_lock = SPIN_LOCK_UNLOCKED;
46 	bl->read_lock  = SPIN_LOCK_UNLOCKED;
47 	return 0;
48 }
49 
drm_waitlist_destroy(drm_waitlist_t * bl)50 int drm_waitlist_destroy(drm_waitlist_t *bl)
51 {
52 	if (bl->rp != bl->wp) return -EINVAL;
53 	if (bl->bufs) drm_free(bl->bufs,
54 			       (bl->count + 2) * sizeof(*bl->bufs),
55 			       DRM_MEM_BUFLISTS);
56 	bl->count = 0;
57 	bl->bufs  = NULL;
58 	bl->rp	  = NULL;
59 	bl->wp	  = NULL;
60 	bl->end	  = NULL;
61 	return 0;
62 }
63 
drm_waitlist_put(drm_waitlist_t * bl,drm_buf_t * buf)64 int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf)
65 {
66 	int	      left;
67 	unsigned long flags;
68 
69 	left = DRM_LEFTCOUNT(bl);
70 	if (!left) {
71 		DRM_ERROR("Overflow while adding buffer %d from pid %d\n",
72 			  buf->idx, buf->pid);
73 		return -EINVAL;
74 	}
75 #if DRM_DMA_HISTOGRAM
76 	buf->time_queued = get_cycles();
77 #endif
78 	buf->list	 = DRM_LIST_WAIT;
79 
80 	spin_lock_irqsave(&bl->write_lock, flags);
81 	*bl->wp = buf;
82 	if (++bl->wp >= bl->end) bl->wp = bl->bufs;
83 	spin_unlock_irqrestore(&bl->write_lock, flags);
84 
85 	return 0;
86 }
87 
drm_waitlist_get(drm_waitlist_t * bl)88 drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl)
89 {
90 	drm_buf_t     *buf;
91 	unsigned long flags;
92 
93 	spin_lock_irqsave(&bl->read_lock, flags);
94 	buf = *bl->rp;
95 	if (bl->rp == bl->wp) {
96 		spin_unlock_irqrestore(&bl->read_lock, flags);
97 		return NULL;
98 	}
99 	if (++bl->rp >= bl->end) bl->rp = bl->bufs;
100 	spin_unlock_irqrestore(&bl->read_lock, flags);
101 
102 	return buf;
103 }
104 
drm_freelist_create(drm_freelist_t * bl,int count)105 int drm_freelist_create(drm_freelist_t *bl, int count)
106 {
107 	atomic_set(&bl->count, 0);
108 	bl->next      = NULL;
109 	init_waitqueue_head(&bl->waiting);
110 	bl->low_mark  = 0;
111 	bl->high_mark = 0;
112 	atomic_set(&bl->wfh,   0);
113 	bl->lock      = SPIN_LOCK_UNLOCKED;
114 	++bl->initialized;
115 	return 0;
116 }
117 
drm_freelist_destroy(drm_freelist_t * bl)118 int drm_freelist_destroy(drm_freelist_t *bl)
119 {
120 	atomic_set(&bl->count, 0);
121 	bl->next = NULL;
122 	return 0;
123 }
124 
drm_freelist_put(drm_device_t * dev,drm_freelist_t * bl,drm_buf_t * buf)125 int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
126 {
127 	drm_device_dma_t *dma  = dev->dma;
128 
129 	if (!dma) {
130 		DRM_ERROR("No DMA support\n");
131 		return 1;
132 	}
133 
134 	if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) {
135 		DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n",
136 			  buf->idx, buf->waiting, buf->pending, buf->list);
137 	}
138 	if (!bl) return 1;
139 #if DRM_DMA_HISTOGRAM
140 	buf->time_freed = get_cycles();
141 	drm_histogram_compute(dev, buf);
142 #endif
143 	buf->list	= DRM_LIST_FREE;
144 
145 	spin_lock(&bl->lock);
146 	buf->next	= bl->next;
147 	bl->next	= buf;
148 	spin_unlock(&bl->lock);
149 
150 	atomic_inc(&bl->count);
151 	if (atomic_read(&bl->count) > dma->buf_count) {
152 		DRM_ERROR("%d of %d buffers free after addition of %d\n",
153 			  atomic_read(&bl->count), dma->buf_count, buf->idx);
154 		return 1;
155 	}
156 				/* Check for high water mark */
157 	if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) {
158 		atomic_set(&bl->wfh, 0);
159 		wake_up_interruptible(&bl->waiting);
160 	}
161 	return 0;
162 }
163 
drm_freelist_try(drm_freelist_t * bl)164 static drm_buf_t *drm_freelist_try(drm_freelist_t *bl)
165 {
166 	drm_buf_t	  *buf;
167 
168 	if (!bl) return NULL;
169 
170 				/* Get buffer */
171 	spin_lock(&bl->lock);
172 	if (!bl->next) {
173 		spin_unlock(&bl->lock);
174 		return NULL;
175 	}
176 	buf	  = bl->next;
177 	bl->next  = bl->next->next;
178 	spin_unlock(&bl->lock);
179 
180 	atomic_dec(&bl->count);
181 	buf->next = NULL;
182 	buf->list = DRM_LIST_NONE;
183 	if (buf->waiting || buf->pending) {
184 		DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n",
185 			  buf->idx, buf->waiting, buf->pending, buf->list);
186 	}
187 
188 	return buf;
189 }
190 
drm_freelist_get(drm_freelist_t * bl,int block)191 drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block)
192 {
193 	drm_buf_t	  *buf	= NULL;
194 	DECLARE_WAITQUEUE(entry, current);
195 
196 	if (!bl || !bl->initialized) return NULL;
197 
198 				/* Check for low water mark */
199 	if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */
200 		atomic_set(&bl->wfh, 1);
201 	if (atomic_read(&bl->wfh)) {
202 		if (block) {
203 			add_wait_queue(&bl->waiting, &entry);
204 			for (;;) {
205 				current->state = TASK_INTERRUPTIBLE;
206 				if (!atomic_read(&bl->wfh)
207 				    && (buf = drm_freelist_try(bl))) break;
208 				schedule();
209 				if (signal_pending(current)) break;
210 			}
211 			current->state = TASK_RUNNING;
212 			remove_wait_queue(&bl->waiting, &entry);
213 		}
214 		return buf;
215 	}
216 
217 	return drm_freelist_try(bl);
218 }
219