diff options
author | Andy Walls <awalls@radix.net> | 2009-04-13 22:08:00 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-06-16 17:20:44 -0400 |
commit | 40c5520f55924ba87090d0d93222baad74202559 (patch) | |
tree | 731576eb5bb3c37df60c8cc78adbfc4d18bc0dd4 | |
parent | 5f0a3cfcfd315d87de8f80af49b114daf7137823 (diff) |
V4L/DVB (11618): cx18: Convert per stream mutex locks to per queue spin locks
To avoid sleeps in providing buffers to user space and in handling incoming
buffers from the capture unit, converted the per stream mutex for locking
queues to 3 spin locks. There is now a spin lock per queue
to increase concurrency when moving buffers around.
Also simplified queue manipulations and buffer handling of incoming buffers
of data from the capture unit.
Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.h | 2 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-mailbox.c | 25 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-queue.c | 83 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.c | 8 |
4 files changed, 69 insertions, 49 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 62dca432fdbb..35a6758a7aac 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h | |||
@@ -286,6 +286,7 @@ struct cx18_queue { | |||
286 | struct list_head list; | 286 | struct list_head list; |
287 | atomic_t buffers; | 287 | atomic_t buffers; |
288 | u32 bytesused; | 288 | u32 bytesused; |
289 | spinlock_t lock; | ||
289 | }; | 290 | }; |
290 | 291 | ||
291 | struct cx18_dvb { | 292 | struct cx18_dvb { |
@@ -365,7 +366,6 @@ struct cx18_stream { | |||
365 | unsigned mdl_offset; | 366 | unsigned mdl_offset; |
366 | 367 | ||
367 | u32 id; | 368 | u32 id; |
368 | struct mutex qlock; /* locks access to the queues */ | ||
369 | unsigned long s_flags; /* status flags, see above */ | 369 | unsigned long s_flags; /* status flags, see above */ |
370 | int dma; /* can be PCI_DMA_TODEVICE, | 370 | int dma; /* can be PCI_DMA_TODEVICE, |
371 | PCI_DMA_FROMDEVICE or | 371 | PCI_DMA_FROMDEVICE or |
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index df7d61d0a13e..afe46c3d4057 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c | |||
@@ -191,23 +191,24 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) | |||
191 | if (buf == NULL) { | 191 | if (buf == NULL) { |
192 | CX18_WARN("Could not find buf %d for stream %s\n", | 192 | CX18_WARN("Could not find buf %d for stream %s\n", |
193 | id, s->name); | 193 | id, s->name); |
194 | /* Put as many buffers as possible back into fw use */ | ||
195 | cx18_stream_load_fw_queue(s); | ||
196 | continue; | 194 | continue; |
197 | } | 195 | } |
198 | 196 | ||
199 | if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { | 197 | CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", |
200 | CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", | 198 | s->name, buf->bytesused); |
201 | buf->bytesused); | 199 | |
202 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | 200 | if (s->type != CX18_ENC_STREAM_TYPE_TS) |
203 | buf->bytesused); | 201 | cx18_enqueue(s, buf, &s->q_full); |
202 | else { | ||
203 | if (s->dvb.enabled) | ||
204 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | ||
205 | buf->bytesused); | ||
206 | cx18_enqueue(s, buf, &s->q_free); | ||
204 | } | 207 | } |
205 | /* Put as many buffers as possible back into fw use */ | ||
206 | cx18_stream_load_fw_queue(s); | ||
207 | /* Put back TS buffer, since it was removed from all queues */ | ||
208 | if (s->type == CX18_ENC_STREAM_TYPE_TS) | ||
209 | cx18_stream_put_buf_fw(s, buf); | ||
210 | } | 208 | } |
209 | /* Put as many buffers as possible back into fw use */ | ||
210 | cx18_stream_load_fw_queue(s); | ||
211 | |||
211 | wake_up(&cx->dma_waitq); | 212 | wake_up(&cx->dma_waitq); |
212 | if (s->id != -1) | 213 | if (s->id != -1) |
213 | wake_up(&s->waitq); | 214 | wake_up(&s->waitq); |
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 3046b8e74345..693a745b0858 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c | |||
@@ -53,13 +53,13 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, | |||
53 | buf->skipped = 0; | 53 | buf->skipped = 0; |
54 | } | 54 | } |
55 | 55 | ||
56 | mutex_lock(&s->qlock); | ||
57 | |||
58 | /* q_busy is restricted to a max buffer count imposed by firmware */ | 56 | /* q_busy is restricted to a max buffer count imposed by firmware */ |
59 | if (q == &s->q_busy && | 57 | if (q == &s->q_busy && |
60 | atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) | 58 | atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) |
61 | q = &s->q_free; | 59 | q = &s->q_free; |
62 | 60 | ||
61 | spin_lock(&q->lock); | ||
62 | |||
63 | if (to_front) | 63 | if (to_front) |
64 | list_add(&buf->list, &q->list); /* LIFO */ | 64 | list_add(&buf->list, &q->list); /* LIFO */ |
65 | else | 65 | else |
@@ -67,7 +67,7 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, | |||
67 | q->bytesused += buf->bytesused - buf->readpos; | 67 | q->bytesused += buf->bytesused - buf->readpos; |
68 | atomic_inc(&q->buffers); | 68 | atomic_inc(&q->buffers); |
69 | 69 | ||
70 | mutex_unlock(&s->qlock); | 70 | spin_unlock(&q->lock); |
71 | return q; | 71 | return q; |
72 | } | 72 | } |
73 | 73 | ||
@@ -75,7 +75,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) | |||
75 | { | 75 | { |
76 | struct cx18_buffer *buf = NULL; | 76 | struct cx18_buffer *buf = NULL; |
77 | 77 | ||
78 | mutex_lock(&s->qlock); | 78 | spin_lock(&q->lock); |
79 | if (!list_empty(&q->list)) { | 79 | if (!list_empty(&q->list)) { |
80 | buf = list_first_entry(&q->list, struct cx18_buffer, list); | 80 | buf = list_first_entry(&q->list, struct cx18_buffer, list); |
81 | list_del_init(&buf->list); | 81 | list_del_init(&buf->list); |
@@ -83,7 +83,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) | |||
83 | buf->skipped = 0; | 83 | buf->skipped = 0; |
84 | atomic_dec(&q->buffers); | 84 | atomic_dec(&q->buffers); |
85 | } | 85 | } |
86 | mutex_unlock(&s->qlock); | 86 | spin_unlock(&q->lock); |
87 | return buf; | 87 | return buf; |
88 | } | 88 | } |
89 | 89 | ||
@@ -94,9 +94,23 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, | |||
94 | struct cx18_buffer *buf; | 94 | struct cx18_buffer *buf; |
95 | struct cx18_buffer *tmp; | 95 | struct cx18_buffer *tmp; |
96 | struct cx18_buffer *ret = NULL; | 96 | struct cx18_buffer *ret = NULL; |
97 | 97 | LIST_HEAD(sweep_up); | |
98 | mutex_lock(&s->qlock); | 98 | |
99 | /* | ||
100 | * We don't have to acquire multiple q locks here, because we are | ||
101 | * serialized by the single threaded work handler. | ||
102 | * Buffers from the firmware will thus remain in order as | ||
103 | * they are moved from q_busy to q_full or to the dvb ring buffer. | ||
104 | */ | ||
105 | spin_lock(&s->q_busy.lock); | ||
99 | list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { | 106 | list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { |
107 | /* | ||
108 | * We should find what the firmware told us is done, | ||
109 | * right at the front of the queue. If we don't, we likely have | ||
110 | * missed a buffer done message from the firmware. | ||
111 | * Once we skip a buffer repeatedly, relative to the size of | ||
112 | * q_busy, we have high confidence we've missed it. | ||
113 | */ | ||
100 | if (buf->id != id) { | 114 | if (buf->id != id) { |
101 | buf->skipped++; | 115 | buf->skipped++; |
102 | if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { | 116 | if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { |
@@ -105,38 +119,41 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, | |||
105 | "times - it must have dropped out of " | 119 | "times - it must have dropped out of " |
106 | "rotation\n", s->name, buf->id, | 120 | "rotation\n", s->name, buf->id, |
107 | buf->skipped); | 121 | buf->skipped); |
108 | /* move it to q_free */ | 122 | /* Sweep it up to put it back into rotation */ |
109 | list_move_tail(&buf->list, &s->q_free.list); | 123 | list_move_tail(&buf->list, &sweep_up); |
110 | buf->bytesused = buf->readpos = buf->b_flags = | ||
111 | buf->skipped = 0; | ||
112 | atomic_dec(&s->q_busy.buffers); | 124 | atomic_dec(&s->q_busy.buffers); |
113 | atomic_inc(&s->q_free.buffers); | ||
114 | } | 125 | } |
115 | continue; | 126 | continue; |
116 | } | 127 | } |
117 | 128 | /* | |
118 | buf->bytesused = bytesused; | 129 | * We pull the desired buffer off of the queue here. Something |
119 | /* Sync the buffer before we release the qlock */ | 130 | * will have to put it back on a queue later. |
120 | cx18_buf_sync_for_cpu(s, buf); | 131 | */ |
121 | if (s->type == CX18_ENC_STREAM_TYPE_TS) { | 132 | list_del_init(&buf->list); |
122 | /* | ||
123 | * TS doesn't use q_full. As we pull the buffer off of | ||
124 | * the queue here, the caller will have to put it back. | ||
125 | */ | ||
126 | list_del_init(&buf->list); | ||
127 | } else { | ||
128 | /* Move buffer from q_busy to q_full */ | ||
129 | list_move_tail(&buf->list, &s->q_full.list); | ||
130 | set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); | ||
131 | s->q_full.bytesused += buf->bytesused; | ||
132 | atomic_inc(&s->q_full.buffers); | ||
133 | } | ||
134 | atomic_dec(&s->q_busy.buffers); | 133 | atomic_dec(&s->q_busy.buffers); |
135 | |||
136 | ret = buf; | 134 | ret = buf; |
137 | break; | 135 | break; |
138 | } | 136 | } |
139 | mutex_unlock(&s->qlock); | 137 | spin_unlock(&s->q_busy.lock); |
138 | |||
139 | /* | ||
140 | * We found the buffer for which we were looking. Get it ready for | ||
141 | * the caller to put on q_full or in the dvb ring buffer. | ||
142 | */ | ||
143 | if (ret != NULL) { | ||
144 | ret->bytesused = bytesused; | ||
145 | ret->skipped = 0; | ||
146 | /* readpos and b_flags were 0'ed when the buf went on q_busy */ | ||
147 | cx18_buf_sync_for_cpu(s, ret); | ||
148 | if (s->type != CX18_ENC_STREAM_TYPE_TS) | ||
149 | set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags); | ||
150 | } | ||
151 | |||
152 | /* Put any buffers the firmware is ignoring back into normal rotation */ | ||
153 | list_for_each_entry_safe(buf, tmp, &sweep_up, list) { | ||
154 | list_del_init(&buf->list); | ||
155 | cx18_enqueue(s, buf, &s->q_free); | ||
156 | } | ||
140 | return ret; | 157 | return ret; |
141 | } | 158 | } |
142 | 159 | ||
@@ -148,7 +165,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) | |||
148 | if (q == &s->q_free) | 165 | if (q == &s->q_free) |
149 | return; | 166 | return; |
150 | 167 | ||
151 | mutex_lock(&s->qlock); | 168 | spin_lock(&q->lock); |
152 | while (!list_empty(&q->list)) { | 169 | while (!list_empty(&q->list)) { |
153 | buf = list_first_entry(&q->list, struct cx18_buffer, list); | 170 | buf = list_first_entry(&q->list, struct cx18_buffer, list); |
154 | list_move_tail(&buf->list, &s->q_free.list); | 171 | list_move_tail(&buf->list, &s->q_free.list); |
@@ -156,7 +173,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) | |||
156 | atomic_inc(&s->q_free.buffers); | 173 | atomic_inc(&s->q_free.buffers); |
157 | } | 174 | } |
158 | cx18_queue_init(q); | 175 | cx18_queue_init(q); |
159 | mutex_unlock(&s->qlock); | 176 | spin_unlock(&q->lock); |
160 | } | 177 | } |
161 | 178 | ||
162 | void cx18_flush_queues(struct cx18_stream *s) | 179 | void cx18_flush_queues(struct cx18_stream *s) |
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index bbeb01c5cf32..e1934e9cfdc8 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c | |||
@@ -116,11 +116,13 @@ static void cx18_stream_init(struct cx18 *cx, int type) | |||
116 | s->buffers = cx->stream_buffers[type]; | 116 | s->buffers = cx->stream_buffers[type]; |
117 | s->buf_size = cx->stream_buf_size[type]; | 117 | s->buf_size = cx->stream_buf_size[type]; |
118 | 118 | ||
119 | mutex_init(&s->qlock); | ||
120 | init_waitqueue_head(&s->waitq); | 119 | init_waitqueue_head(&s->waitq); |
121 | s->id = -1; | 120 | s->id = -1; |
121 | spin_lock_init(&s->q_free.lock); | ||
122 | cx18_queue_init(&s->q_free); | 122 | cx18_queue_init(&s->q_free); |
123 | spin_lock_init(&s->q_busy.lock); | ||
123 | cx18_queue_init(&s->q_busy); | 124 | cx18_queue_init(&s->q_busy); |
125 | spin_lock_init(&s->q_full.lock); | ||
124 | cx18_queue_init(&s->q_full); | 126 | cx18_queue_init(&s->q_full); |
125 | } | 127 | } |
126 | 128 | ||
@@ -685,13 +687,13 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | |||
685 | 687 | ||
686 | /* Init all the cpu_mdls for this stream */ | 688 | /* Init all the cpu_mdls for this stream */ |
687 | cx18_flush_queues(s); | 689 | cx18_flush_queues(s); |
688 | mutex_lock(&s->qlock); | 690 | spin_lock(&s->q_free.lock); |
689 | list_for_each_entry(buf, &s->q_free.list, list) { | 691 | list_for_each_entry(buf, &s->q_free.list, list) { |
690 | cx18_writel(cx, buf->dma_handle, | 692 | cx18_writel(cx, buf->dma_handle, |
691 | &cx->scb->cpu_mdl[buf->id].paddr); | 693 | &cx->scb->cpu_mdl[buf->id].paddr); |
692 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); | 694 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); |
693 | } | 695 | } |
694 | mutex_unlock(&s->qlock); | 696 | spin_unlock(&s->q_free.lock); |
695 | _cx18_stream_load_fw_queue(s); | 697 | _cx18_stream_load_fw_queue(s); |
696 | 698 | ||
697 | /* begin_capture */ | 699 | /* begin_capture */ |