diff options
Diffstat (limited to 'arch/mips/kernel/rtlx.c')
-rw-r--r-- | arch/mips/kernel/rtlx.c | 253 |
1 files changed, 114 insertions, 139 deletions
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index d92c48e0d7a6..bfc8ca168f83 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c | |||
@@ -53,7 +53,8 @@ static char module_name[] = "rtlx"; | |||
53 | static struct chan_waitqueues { | 53 | static struct chan_waitqueues { |
54 | wait_queue_head_t rt_queue; | 54 | wait_queue_head_t rt_queue; |
55 | wait_queue_head_t lx_queue; | 55 | wait_queue_head_t lx_queue; |
56 | int in_open; | 56 | atomic_t in_open; |
57 | struct mutex mutex; | ||
57 | } channel_wqs[RTLX_CHANNELS]; | 58 | } channel_wqs[RTLX_CHANNELS]; |
58 | 59 | ||
59 | static struct irqaction irq; | 60 | static struct irqaction irq; |
@@ -146,110 +147,91 @@ static void stopping(int vpe) | |||
146 | 147 | ||
147 | int rtlx_open(int index, int can_sleep) | 148 | int rtlx_open(int index, int can_sleep) |
148 | { | 149 | { |
149 | int ret; | 150 | struct rtlx_info **p; |
150 | struct rtlx_channel *chan; | 151 | struct rtlx_channel *chan; |
151 | volatile struct rtlx_info **p; | 152 | enum rtlx_state state; |
153 | int ret = 0; | ||
152 | 154 | ||
153 | if (index >= RTLX_CHANNELS) { | 155 | if (index >= RTLX_CHANNELS) { |
154 | printk(KERN_DEBUG "rtlx_open index out of range\n"); | 156 | printk(KERN_DEBUG "rtlx_open index out of range\n"); |
155 | return -ENOSYS; | 157 | return -ENOSYS; |
156 | } | 158 | } |
157 | 159 | ||
158 | if (channel_wqs[index].in_open) { | 160 | if (atomic_inc_return(&channel_wqs[index].in_open) > 1) { |
159 | printk(KERN_DEBUG "rtlx_open channel %d already opened\n", index); | 161 | printk(KERN_DEBUG "rtlx_open channel %d already opened\n", |
160 | return -EBUSY; | 162 | index); |
163 | ret = -EBUSY; | ||
164 | goto out_fail; | ||
161 | } | 165 | } |
162 | 166 | ||
163 | channel_wqs[index].in_open++; | ||
164 | |||
165 | if (rtlx == NULL) { | 167 | if (rtlx == NULL) { |
166 | if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { | 168 | if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { |
167 | if (can_sleep) { | 169 | if (can_sleep) { |
168 | DECLARE_WAITQUEUE(wait, current); | 170 | __wait_event_interruptible(channel_wqs[index].lx_queue, |
169 | 171 | (p = vpe_get_shared(RTLX_TARG_VPE)), | |
170 | /* go to sleep */ | 172 | ret); |
171 | add_wait_queue(&channel_wqs[index].lx_queue, &wait); | 173 | if (ret) |
172 | 174 | goto out_fail; | |
173 | set_current_state(TASK_INTERRUPTIBLE); | ||
174 | while ((p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { | ||
175 | schedule(); | ||
176 | set_current_state(TASK_INTERRUPTIBLE); | ||
177 | } | ||
178 | |||
179 | set_current_state(TASK_RUNNING); | ||
180 | remove_wait_queue(&channel_wqs[index].lx_queue, &wait); | ||
181 | |||
182 | /* back running */ | ||
183 | } else { | 175 | } else { |
184 | printk( KERN_DEBUG "No SP program loaded, and device " | 176 | printk(KERN_DEBUG "No SP program loaded, and device " |
185 | "opened with O_NONBLOCK\n"); | 177 | "opened with O_NONBLOCK\n"); |
186 | channel_wqs[index].in_open = 0; | 178 | ret = -ENOSYS; |
187 | return -ENOSYS; | 179 | goto out_fail; |
188 | } | 180 | } |
189 | } | 181 | } |
190 | 182 | ||
183 | smp_rmb(); | ||
191 | if (*p == NULL) { | 184 | if (*p == NULL) { |
192 | if (can_sleep) { | 185 | if (can_sleep) { |
193 | DECLARE_WAITQUEUE(wait, current); | 186 | DEFINE_WAIT(wait); |
194 | 187 | ||
195 | /* go to sleep */ | 188 | for (;;) { |
196 | add_wait_queue(&channel_wqs[index].lx_queue, &wait); | 189 | prepare_to_wait(&channel_wqs[index].lx_queue, &wait, TASK_INTERRUPTIBLE); |
197 | 190 | smp_rmb(); | |
198 | set_current_state(TASK_INTERRUPTIBLE); | 191 | if (*p != NULL) |
199 | while (*p == NULL) { | 192 | break; |
200 | schedule(); | 193 | if (!signal_pending(current)) { |
201 | 194 | schedule(); | |
202 | /* reset task state to interruptable otherwise | 195 | continue; |
203 | we'll whizz round here like a very fast loopy | 196 | } |
204 | thing. schedule() appears to return with state | 197 | ret = -ERESTARTSYS; |
205 | set to TASK_RUNNING. | 198 | goto out_fail; |
206 | |||
207 | If the loaded SP program, for whatever reason, | ||
208 | doesn't set up the shared structure *p will never | ||
209 | become true. So whoever connected to either /dev/rt? | ||
210 | or if it was kspd, will then take up rather a lot of | ||
211 | processor cycles. | ||
212 | */ | ||
213 | |||
214 | set_current_state(TASK_INTERRUPTIBLE); | ||
215 | } | 199 | } |
216 | 200 | finish_wait(&channel_wqs[index].lx_queue, &wait); | |
217 | set_current_state(TASK_RUNNING); | 201 | } else { |
218 | remove_wait_queue(&channel_wqs[index].lx_queue, &wait); | ||
219 | |||
220 | /* back running */ | ||
221 | } | ||
222 | else { | ||
223 | printk(" *vpe_get_shared is NULL. " | 202 | printk(" *vpe_get_shared is NULL. " |
224 | "Has an SP program been loaded?\n"); | 203 | "Has an SP program been loaded?\n"); |
225 | channel_wqs[index].in_open = 0; | 204 | ret = -ENOSYS; |
226 | return -ENOSYS; | 205 | goto out_fail; |
227 | } | 206 | } |
228 | } | 207 | } |
229 | 208 | ||
230 | if ((unsigned int)*p < KSEG0) { | 209 | if ((unsigned int)*p < KSEG0) { |
231 | printk(KERN_WARNING "vpe_get_shared returned an invalid pointer " | 210 | printk(KERN_WARNING "vpe_get_shared returned an invalid pointer " |
232 | "maybe an error code %d\n", (int)*p); | 211 | "maybe an error code %d\n", (int)*p); |
233 | channel_wqs[index].in_open = 0; | 212 | ret = -ENOSYS; |
234 | return -ENOSYS; | 213 | goto out_fail; |
235 | } | 214 | } |
236 | 215 | ||
237 | if ((ret = rtlx_init(*p)) < 0) { | 216 | if ((ret = rtlx_init(*p)) < 0) |
238 | channel_wqs[index].in_open = 0; | 217 | goto out_ret; |
239 | return ret; | ||
240 | } | ||
241 | } | 218 | } |
242 | 219 | ||
243 | chan = &rtlx->channel[index]; | 220 | chan = &rtlx->channel[index]; |
244 | 221 | ||
245 | if (chan->lx_state == RTLX_STATE_OPENED) { | 222 | state = xchg(&chan->lx_state, RTLX_STATE_OPENED); |
246 | channel_wqs[index].in_open = 0; | 223 | if (state == RTLX_STATE_OPENED) { |
247 | return -EBUSY; | 224 | ret = -EBUSY; |
248 | } | 225 | goto out_fail; |
226 | } | ||
249 | 227 | ||
250 | chan->lx_state = RTLX_STATE_OPENED; | 228 | out_fail: |
251 | channel_wqs[index].in_open = 0; | 229 | smp_mb(); |
252 | return 0; | 230 | atomic_dec(&channel_wqs[index].in_open); |
231 | smp_mb(); | ||
232 | |||
233 | out_ret: | ||
234 | return ret; | ||
253 | } | 235 | } |
254 | 236 | ||
255 | int rtlx_release(int index) | 237 | int rtlx_release(int index) |
@@ -270,30 +252,17 @@ unsigned int rtlx_read_poll(int index, int can_sleep) | |||
270 | /* data available to read? */ | 252 | /* data available to read? */ |
271 | if (chan->lx_read == chan->lx_write) { | 253 | if (chan->lx_read == chan->lx_write) { |
272 | if (can_sleep) { | 254 | if (can_sleep) { |
273 | DECLARE_WAITQUEUE(wait, current); | 255 | int ret = 0; |
274 | |||
275 | /* go to sleep */ | ||
276 | add_wait_queue(&channel_wqs[index].lx_queue, &wait); | ||
277 | |||
278 | set_current_state(TASK_INTERRUPTIBLE); | ||
279 | while (chan->lx_read == chan->lx_write) { | ||
280 | schedule(); | ||
281 | 256 | ||
282 | set_current_state(TASK_INTERRUPTIBLE); | 257 | __wait_event_interruptible(channel_wqs[index].lx_queue, |
258 | chan->lx_read != chan->lx_write || sp_stopping, | ||
259 | ret); | ||
260 | if (ret) | ||
261 | return ret; | ||
283 | 262 | ||
284 | if (sp_stopping) { | 263 | if (sp_stopping) |
285 | set_current_state(TASK_RUNNING); | 264 | return 0; |
286 | remove_wait_queue(&channel_wqs[index].lx_queue, &wait); | 265 | } else |
287 | return 0; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | set_current_state(TASK_RUNNING); | ||
292 | remove_wait_queue(&channel_wqs[index].lx_queue, &wait); | ||
293 | |||
294 | /* back running */ | ||
295 | } | ||
296 | else | ||
297 | return 0; | 266 | return 0; |
298 | } | 267 | } |
299 | 268 | ||
@@ -320,56 +289,53 @@ unsigned int rtlx_write_poll(int index) | |||
320 | return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size); | 289 | return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size); |
321 | } | 290 | } |
322 | 291 | ||
323 | static inline void copy_to(void *dst, void *src, size_t count, int user) | 292 | ssize_t rtlx_read(int index, void __user *buff, size_t count) |
324 | { | 293 | { |
325 | if (user) | 294 | size_t lx_write, fl = 0L; |
326 | copy_to_user(dst, src, count); | ||
327 | else | ||
328 | memcpy(dst, src, count); | ||
329 | } | ||
330 | |||
331 | static inline void copy_from(void *dst, void *src, size_t count, int user) | ||
332 | { | ||
333 | if (user) | ||
334 | copy_from_user(dst, src, count); | ||
335 | else | ||
336 | memcpy(dst, src, count); | ||
337 | } | ||
338 | |||
339 | ssize_t rtlx_read(int index, void *buff, size_t count, int user) | ||
340 | { | ||
341 | size_t fl = 0L; | ||
342 | struct rtlx_channel *lx; | 295 | struct rtlx_channel *lx; |
296 | unsigned long failed; | ||
343 | 297 | ||
344 | if (rtlx == NULL) | 298 | if (rtlx == NULL) |
345 | return -ENOSYS; | 299 | return -ENOSYS; |
346 | 300 | ||
347 | lx = &rtlx->channel[index]; | 301 | lx = &rtlx->channel[index]; |
348 | 302 | ||
303 | mutex_lock(&channel_wqs[index].mutex); | ||
304 | smp_rmb(); | ||
305 | lx_write = lx->lx_write; | ||
306 | |||
349 | /* find out how much in total */ | 307 | /* find out how much in total */ |
350 | count = min(count, | 308 | count = min(count, |
351 | (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) | 309 | (size_t)(lx_write + lx->buffer_size - lx->lx_read) |
352 | % lx->buffer_size); | 310 | % lx->buffer_size); |
353 | 311 | ||
354 | /* then how much from the read pointer onwards */ | 312 | /* then how much from the read pointer onwards */ |
355 | fl = min( count, (size_t)lx->buffer_size - lx->lx_read); | 313 | fl = min(count, (size_t)lx->buffer_size - lx->lx_read); |
356 | 314 | ||
357 | copy_to(buff, &lx->lx_buffer[lx->lx_read], fl, user); | 315 | failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl); |
316 | if (failed) | ||
317 | goto out; | ||
358 | 318 | ||
359 | /* and if there is anything left at the beginning of the buffer */ | 319 | /* and if there is anything left at the beginning of the buffer */ |
360 | if ( count - fl ) | 320 | if (count - fl) |
361 | copy_to (buff + fl, lx->lx_buffer, count - fl, user); | 321 | failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl); |
322 | |||
323 | out: | ||
324 | count -= failed; | ||
362 | 325 | ||
363 | /* update the index */ | 326 | smp_wmb(); |
364 | lx->lx_read += count; | 327 | lx->lx_read = (lx->lx_read + count) % lx->buffer_size; |
365 | lx->lx_read %= lx->buffer_size; | 328 | smp_wmb(); |
329 | mutex_unlock(&channel_wqs[index].mutex); | ||
366 | 330 | ||
367 | return count; | 331 | return count; |
368 | } | 332 | } |
369 | 333 | ||
370 | ssize_t rtlx_write(int index, void *buffer, size_t count, int user) | 334 | ssize_t rtlx_write(int index, const void __user *buffer, size_t count) |
371 | { | 335 | { |
372 | struct rtlx_channel *rt; | 336 | struct rtlx_channel *rt; |
337 | unsigned long failed; | ||
338 | size_t rt_read; | ||
373 | size_t fl; | 339 | size_t fl; |
374 | 340 | ||
375 | if (rtlx == NULL) | 341 | if (rtlx == NULL) |
@@ -377,24 +343,35 @@ ssize_t rtlx_write(int index, void *buffer, size_t count, int user) | |||
377 | 343 | ||
378 | rt = &rtlx->channel[index]; | 344 | rt = &rtlx->channel[index]; |
379 | 345 | ||
346 | mutex_lock(&channel_wqs[index].mutex); | ||
347 | smp_rmb(); | ||
348 | rt_read = rt->rt_read; | ||
349 | |||
380 | /* total number of bytes to copy */ | 350 | /* total number of bytes to copy */ |
381 | count = min(count, | 351 | count = min(count, |
382 | (size_t)write_spacefree(rt->rt_read, rt->rt_write, | 352 | (size_t)write_spacefree(rt_read, rt->rt_write, rt->buffer_size)); |
383 | rt->buffer_size)); | ||
384 | 353 | ||
385 | /* first bit from write pointer to the end of the buffer, or count */ | 354 | /* first bit from write pointer to the end of the buffer, or count */ |
386 | fl = min(count, (size_t) rt->buffer_size - rt->rt_write); | 355 | fl = min(count, (size_t) rt->buffer_size - rt->rt_write); |
387 | 356 | ||
388 | copy_from (&rt->rt_buffer[rt->rt_write], buffer, fl, user); | 357 | failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl); |
358 | if (failed) | ||
359 | goto out; | ||
389 | 360 | ||
390 | /* if there's any left copy to the beginning of the buffer */ | 361 | /* if there's any left copy to the beginning of the buffer */ |
391 | if( count - fl ) | 362 | if (count - fl) { |
392 | copy_from (rt->rt_buffer, buffer + fl, count - fl, user); | 363 | failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl); |
364 | } | ||
393 | 365 | ||
394 | rt->rt_write += count; | 366 | out: |
395 | rt->rt_write %= rt->buffer_size; | 367 | count -= failed; |
396 | 368 | ||
397 | return(count); | 369 | smp_wmb(); |
370 | rt->rt_write = (rt->rt_write + count) % rt->buffer_size; | ||
371 | smp_wmb(); | ||
372 | mutex_unlock(&channel_wqs[index].mutex); | ||
373 | |||
374 | return count; | ||
398 | } | 375 | } |
399 | 376 | ||
400 | 377 | ||
@@ -446,7 +423,7 @@ static ssize_t file_read(struct file *file, char __user * buffer, size_t count, | |||
446 | return 0; // -EAGAIN makes cat whinge | 423 | return 0; // -EAGAIN makes cat whinge |
447 | } | 424 | } |
448 | 425 | ||
449 | return rtlx_read(minor, buffer, count, 1); | 426 | return rtlx_read(minor, buffer, count); |
450 | } | 427 | } |
451 | 428 | ||
452 | static ssize_t file_write(struct file *file, const char __user * buffer, | 429 | static ssize_t file_write(struct file *file, const char __user * buffer, |
@@ -454,28 +431,25 @@ static ssize_t file_write(struct file *file, const char __user * buffer, | |||
454 | { | 431 | { |
455 | int minor; | 432 | int minor; |
456 | struct rtlx_channel *rt; | 433 | struct rtlx_channel *rt; |
457 | DECLARE_WAITQUEUE(wait, current); | ||
458 | 434 | ||
459 | minor = iminor(file->f_path.dentry->d_inode); | 435 | minor = iminor(file->f_path.dentry->d_inode); |
460 | rt = &rtlx->channel[minor]; | 436 | rt = &rtlx->channel[minor]; |
461 | 437 | ||
462 | /* any space left... */ | 438 | /* any space left... */ |
463 | if (!rtlx_write_poll(minor)) { | 439 | if (!rtlx_write_poll(minor)) { |
440 | int ret = 0; | ||
464 | 441 | ||
465 | if (file->f_flags & O_NONBLOCK) | 442 | if (file->f_flags & O_NONBLOCK) |
466 | return -EAGAIN; | 443 | return -EAGAIN; |
467 | 444 | ||
468 | add_wait_queue(&channel_wqs[minor].rt_queue, &wait); | 445 | __wait_event_interruptible(channel_wqs[minor].rt_queue, |
469 | set_current_state(TASK_INTERRUPTIBLE); | 446 | rtlx_write_poll(minor), |
470 | 447 | ret); | |
471 | while (!rtlx_write_poll(minor)) | 448 | if (ret) |
472 | schedule(); | 449 | return ret; |
473 | |||
474 | set_current_state(TASK_RUNNING); | ||
475 | remove_wait_queue(&channel_wqs[minor].rt_queue, &wait); | ||
476 | } | 450 | } |
477 | 451 | ||
478 | return rtlx_write(minor, (void *)buffer, count, 1); | 452 | return rtlx_write(minor, buffer, count); |
479 | } | 453 | } |
480 | 454 | ||
481 | static const struct file_operations rtlx_fops = { | 455 | static const struct file_operations rtlx_fops = { |
@@ -513,7 +487,8 @@ static int rtlx_module_init(void) | |||
513 | for (i = 0; i < RTLX_CHANNELS; i++) { | 487 | for (i = 0; i < RTLX_CHANNELS; i++) { |
514 | init_waitqueue_head(&channel_wqs[i].rt_queue); | 488 | init_waitqueue_head(&channel_wqs[i].rt_queue); |
515 | init_waitqueue_head(&channel_wqs[i].lx_queue); | 489 | init_waitqueue_head(&channel_wqs[i].lx_queue); |
516 | channel_wqs[i].in_open = 0; | 490 | atomic_set(&channel_wqs[i].in_open, 0); |
491 | mutex_init(&channel_wqs[i].mutex); | ||
517 | 492 | ||
518 | dev = device_create(mt_class, NULL, MKDEV(major, i), | 493 | dev = device_create(mt_class, NULL, MKDEV(major, i), |
519 | "%s%d", module_name, i); | 494 | "%s%d", module_name, i); |