V4L/DVB (7854): cx18/ivtv: improve and fix out-of-memory handling
[safe/jmp/linux-2.6] / drivers / media / video / cx18 / cx18-queue.c
1 /*
2  *  cx18 buffer queues
3  *
4  *  Derived from ivtv-queue.c
5  *
6  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21  *  02111-1307  USA
22  */
23
24 #include "cx18-driver.h"
25 #include "cx18-streams.h"
26 #include "cx18-queue.h"
27 #include "cx18-scb.h"
28
29 int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
30                 const char __user *src, int copybytes)
31 {
32         if (s->buf_size - buf->bytesused < copybytes)
33                 copybytes = s->buf_size - buf->bytesused;
34         if (copy_from_user(buf->buf + buf->bytesused, src, copybytes))
35                 return -EFAULT;
36         buf->bytesused += copybytes;
37         return copybytes;
38 }
39
40 void cx18_buf_swap(struct cx18_buffer *buf)
41 {
42         int i;
43
44         for (i = 0; i < buf->bytesused; i += 4)
45                 swab32s((u32 *)(buf->buf + i));
46 }
47
48 void cx18_queue_init(struct cx18_queue *q)
49 {
50         INIT_LIST_HEAD(&q->list);
51         q->buffers = 0;
52         q->length = 0;
53         q->bytesused = 0;
54 }
55
56 void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
57                 struct cx18_queue *q)
58 {
59         unsigned long flags = 0;
60
61         /* clear the buffer if it is going to be enqueued to the free queue */
62         if (q == &s->q_free) {
63                 buf->bytesused = 0;
64                 buf->readpos = 0;
65                 buf->b_flags = 0;
66         }
67         spin_lock_irqsave(&s->qlock, flags);
68         list_add_tail(&buf->list, &q->list);
69         q->buffers++;
70         q->length += s->buf_size;
71         q->bytesused += buf->bytesused - buf->readpos;
72         spin_unlock_irqrestore(&s->qlock, flags);
73 }
74
75 struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
76 {
77         struct cx18_buffer *buf = NULL;
78         unsigned long flags = 0;
79
80         spin_lock_irqsave(&s->qlock, flags);
81         if (!list_empty(&q->list)) {
82                 buf = list_entry(q->list.next, struct cx18_buffer, list);
83                 list_del_init(q->list.next);
84                 q->buffers--;
85                 q->length -= s->buf_size;
86                 q->bytesused -= buf->bytesused - buf->readpos;
87         }
88         spin_unlock_irqrestore(&s->qlock, flags);
89         return buf;
90 }
91
92 struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
93         u32 bytesused)
94 {
95         struct cx18 *cx = s->cx;
96         struct list_head *p;
97
98         list_for_each(p, &s->q_free.list) {
99                 struct cx18_buffer *buf =
100                         list_entry(p, struct cx18_buffer, list);
101
102                 if (buf->id != id)
103                         continue;
104                 buf->bytesused = bytesused;
105                 /* the transport buffers are handled differently,
106                    so there is no need to move them to the full queue */
107                 if (s->type == CX18_ENC_STREAM_TYPE_TS)
108                         return buf;
109                 s->q_free.buffers--;
110                 s->q_free.length -= s->buf_size;
111                 s->q_full.buffers++;
112                 s->q_full.length += s->buf_size;
113                 s->q_full.bytesused += buf->bytesused;
114                 list_move_tail(&buf->list, &s->q_full.list);
115                 return buf;
116         }
117         CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
118         return NULL;
119 }
120
121 static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
122                 struct cx18_queue *to, int clear, int full)
123 {
124         struct cx18_buffer *buf =
125                 list_entry(from->list.next, struct cx18_buffer, list);
126
127         list_move_tail(from->list.next, &to->list);
128         from->buffers--;
129         from->length -= s->buf_size;
130         from->bytesused -= buf->bytesused - buf->readpos;
131         /* special handling for q_free */
132         if (clear)
133                 buf->bytesused = buf->readpos = buf->b_flags = 0;
134         else if (full) {
135                 /* special handling for stolen buffers, assume
136                    all bytes are used. */
137                 buf->bytesused = s->buf_size;
138                 buf->readpos = buf->b_flags = 0;
139         }
140         to->buffers++;
141         to->length += s->buf_size;
142         to->bytesused += buf->bytesused - buf->readpos;
143 }
144
145 /* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
146    If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
147    If 'steal' != NULL, then buffers may also taken from that queue if
148    needed.
149
150    The buffer is automatically cleared if it goes to the free queue. It is
151    also cleared if buffers need to be taken from the 'steal' queue and
152    the 'from' queue is the free queue.
153
154    When 'from' is q_free, then needed_bytes is compared to the total
155    available buffer length, otherwise needed_bytes is compared to the
156    bytesused value. For the 'steal' queue the total available buffer
157    length is always used.
158
159    -ENOMEM is returned if the buffers could not be obtained, 0 if all
160    buffers where obtained from the 'from' list and if non-zero then
161    the number of stolen buffers is returned. */
162 int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
163         struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes)
164 {
165         unsigned long flags;
166         int rc = 0;
167         int from_free = from == &s->q_free;
168         int to_free = to == &s->q_free;
169         int bytes_available;
170
171         spin_lock_irqsave(&s->qlock, flags);
172         if (needed_bytes == 0) {
173                 from_free = 1;
174                 needed_bytes = from->length;
175         }
176
177         bytes_available = from_free ? from->length : from->bytesused;
178         bytes_available += steal ? steal->length : 0;
179
180         if (bytes_available < needed_bytes) {
181                 spin_unlock_irqrestore(&s->qlock, flags);
182                 return -ENOMEM;
183         }
184         if (from_free) {
185                 u32 old_length = to->length;
186
187                 while (to->length - old_length < needed_bytes) {
188                         if (list_empty(&from->list))
189                                 from = steal;
190                         if (from == steal)
191                                 rc++;   /* keep track of 'stolen' buffers */
192                         cx18_queue_move_buf(s, from, to, 1, 0);
193                 }
194         } else {
195                 u32 old_bytesused = to->bytesused;
196
197                 while (to->bytesused - old_bytesused < needed_bytes) {
198                         if (list_empty(&from->list))
199                                 from = steal;
200                         if (from == steal)
201                                 rc++;   /* keep track of 'stolen' buffers */
202                         cx18_queue_move_buf(s, from, to, to_free, rc);
203                 }
204         }
205         spin_unlock_irqrestore(&s->qlock, flags);
206         return rc;
207 }
208
209 void cx18_flush_queues(struct cx18_stream *s)
210 {
211         cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
212         cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
213 }
214
215 int cx18_stream_alloc(struct cx18_stream *s)
216 {
217         struct cx18 *cx = s->cx;
218         int i;
219
220         if (s->buffers == 0)
221                 return 0;
222
223         CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
224                 s->name, s->buffers, s->buf_size,
225                 s->buffers * s->buf_size / 1024);
226
227         if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
228                                 (char *)cx->scb) > SCB_RESERVED_SIZE) {
229                 unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE -
230                                         ((char *)cx->scb->cpu_mdl));
231
232                 CX18_ERR("Too many buffers, cannot fit in SCB area\n");
233                 CX18_ERR("Max buffers = %zd\n",
234                         bufsz / sizeof(struct cx18_mdl));
235                 return -ENOMEM;
236         }
237
238         s->mdl_offset = cx->mdl_offset;
239
240         /* allocate stream buffers. Initially all buffers are in q_free. */
241         for (i = 0; i < s->buffers; i++) {
242                 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
243                                                 GFP_KERNEL|__GFP_NOWARN);
244
245                 if (buf == NULL)
246                         break;
247                 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
248                 if (buf->buf == NULL) {
249                         kfree(buf);
250                         break;
251                 }
252                 buf->id = cx->buffer_id++;
253                 INIT_LIST_HEAD(&buf->list);
254                 buf->dma_handle = pci_map_single(s->cx->dev,
255                                 buf->buf, s->buf_size, s->dma);
256                 cx18_buf_sync_for_cpu(s, buf);
257                 cx18_enqueue(s, buf, &s->q_free);
258         }
259         if (i == s->buffers) {
260                 cx->mdl_offset += s->buffers;
261                 return 0;
262         }
263         CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
264         cx18_stream_free(s);
265         return -ENOMEM;
266 }
267
268 void cx18_stream_free(struct cx18_stream *s)
269 {
270         struct cx18_buffer *buf;
271
272         /* move all buffers to q_free */
273         cx18_flush_queues(s);
274
275         /* empty q_free */
276         while ((buf = cx18_dequeue(s, &s->q_free))) {
277                 pci_unmap_single(s->cx->dev, buf->dma_handle,
278                                 s->buf_size, s->dma);
279                 kfree(buf->buf);
280                 kfree(buf);
281         }
282 }