diff options
author | Andy Walls <awalls@radix.net> | 2008-09-03 16:11:54 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-09-03 17:37:14 -0400 |
commit | 6c9de52884aeafb05a935467c3a2c05a2d445ab7 (patch) | |
tree | 5afe1394ac754e97b3942cdbb74044d90a2c56d7 /drivers/media/video | |
parent | c6eb8eafdba4ad18b4520a0d28a38bc9e61883ea (diff) |
V4L/DVB (8769): cx18: Simplify queue flush logic to prevent oops in cx18_flush_queues()
cx18: Simplify queue flush logic to prevent oops in cx18_flush_queues().
If accounting of a queue is in error, logic borrowed from ivtv will cause
an oops when flushing the queues for a stream. This change greatly
simplifies the queue flush logic, and sets the queue back to sane
defaults on a flush.
Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video')
-rw-r--r-- | drivers/media/video/cx18/cx18-queue.c | 98 |
1 files changed, 15 insertions, 83 deletions
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index a31da49f9123..dbe792ac3001 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c | |||
@@ -110,99 +110,31 @@ struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, | |||
110 | return NULL; | 110 | return NULL; |
111 | } | 111 | } |
112 | 112 | ||
113 | static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from, | 113 | /* Move all buffers of a queue to q_free, while flushing the buffers */ |
114 | struct cx18_queue *to, int clear, int full) | 114 | static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) |
115 | { | ||
116 | struct cx18_buffer *buf = | ||
117 | list_entry(from->list.next, struct cx18_buffer, list); | ||
118 | |||
119 | list_move_tail(from->list.next, &to->list); | ||
120 | from->buffers--; | ||
121 | from->length -= s->buf_size; | ||
122 | from->bytesused -= buf->bytesused - buf->readpos; | ||
123 | /* special handling for q_free */ | ||
124 | if (clear) | ||
125 | buf->bytesused = buf->readpos = buf->b_flags = 0; | ||
126 | else if (full) { | ||
127 | /* special handling for stolen buffers, assume | ||
128 | all bytes are used. */ | ||
129 | buf->bytesused = s->buf_size; | ||
130 | buf->readpos = buf->b_flags = 0; | ||
131 | } | ||
132 | to->buffers++; | ||
133 | to->length += s->buf_size; | ||
134 | to->bytesused += buf->bytesused - buf->readpos; | ||
135 | } | ||
136 | |||
137 | /* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. | ||
138 | If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. | ||
139 | If 'steal' != NULL, then buffers may also taken from that queue if | ||
140 | needed. | ||
141 | |||
142 | The buffer is automatically cleared if it goes to the free queue. It is | ||
143 | also cleared if buffers need to be taken from the 'steal' queue and | ||
144 | the 'from' queue is the free queue. | ||
145 | |||
146 | When 'from' is q_free, then needed_bytes is compared to the total | ||
147 | available buffer length, otherwise needed_bytes is compared to the | ||
148 | bytesused value. For the 'steal' queue the total available buffer | ||
149 | length is always used. | ||
150 | |||
151 | -ENOMEM is returned if the buffers could not be obtained, 0 if all | ||
152 | buffers where obtained from the 'from' list and if non-zero then | ||
153 | the number of stolen buffers is returned. */ | ||
154 | static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from, | ||
155 | struct cx18_queue *steal, struct cx18_queue *to, | ||
156 | int needed_bytes) | ||
157 | { | 115 | { |
158 | unsigned long flags; | 116 | unsigned long flags; |
159 | int rc = 0; | 117 | struct cx18_buffer *buf; |
160 | int from_free = from == &s->q_free; | ||
161 | int to_free = to == &s->q_free; | ||
162 | int bytes_available; | ||
163 | |||
164 | spin_lock_irqsave(&s->qlock, flags); | ||
165 | if (needed_bytes == 0) { | ||
166 | from_free = 1; | ||
167 | needed_bytes = from->length; | ||
168 | } | ||
169 | |||
170 | bytes_available = from_free ? from->length : from->bytesused; | ||
171 | bytes_available += steal ? steal->length : 0; | ||
172 | 118 | ||
173 | if (bytes_available < needed_bytes) { | 119 | if (q == &s->q_free) |
174 | spin_unlock_irqrestore(&s->qlock, flags); | 120 | return; |
175 | return -ENOMEM; | ||
176 | } | ||
177 | if (from_free) { | ||
178 | u32 old_length = to->length; | ||
179 | 121 | ||
180 | while (to->length - old_length < needed_bytes) { | 122 | spin_lock_irqsave(&s->qlock, flags); |
181 | if (list_empty(&from->list)) | 123 | while (!list_empty(&q->list)) { |
182 | from = steal; | 124 | buf = list_entry(q->list.next, struct cx18_buffer, list); |
183 | if (from == steal) | 125 | list_move_tail(q->list.next, &s->q_free.list); |
184 | rc++; /* keep track of 'stolen' buffers */ | 126 | buf->bytesused = buf->readpos = buf->b_flags = 0; |
185 | cx18_queue_move_buf(s, from, to, 1, 0); | 127 | s->q_free.buffers++; |
186 | } | 128 | s->q_free.length += s->buf_size; |
187 | } else { | ||
188 | u32 old_bytesused = to->bytesused; | ||
189 | |||
190 | while (to->bytesused - old_bytesused < needed_bytes) { | ||
191 | if (list_empty(&from->list)) | ||
192 | from = steal; | ||
193 | if (from == steal) | ||
194 | rc++; /* keep track of 'stolen' buffers */ | ||
195 | cx18_queue_move_buf(s, from, to, to_free, rc); | ||
196 | } | ||
197 | } | 129 | } |
130 | cx18_queue_init(q); | ||
198 | spin_unlock_irqrestore(&s->qlock, flags); | 131 | spin_unlock_irqrestore(&s->qlock, flags); |
199 | return rc; | ||
200 | } | 132 | } |
201 | 133 | ||
202 | void cx18_flush_queues(struct cx18_stream *s) | 134 | void cx18_flush_queues(struct cx18_stream *s) |
203 | { | 135 | { |
204 | cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0); | 136 | cx18_queue_flush(s, &s->q_io); |
205 | cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0); | 137 | cx18_queue_flush(s, &s->q_full); |
206 | } | 138 | } |
207 | 139 | ||
208 | int cx18_stream_alloc(struct cx18_stream *s) | 140 | int cx18_stream_alloc(struct cx18_stream *s) |