source: rtems/cpukit/libfs/src/pipe/fifo.c @ 68b49f1

4.115
Last change on this file since 68b49f1 was 68b49f1, checked in by Chris Johns <chrisj@…>, on 06/23/10 at 05:01:46

2010-06-23 Chris Johns <chrisj@…>

PR 1577/filesystem

  • libfs/src/pipe/fifo.c: Fixed the error codes returned on open.
  • Property mode set to 100644
File size: 12.5 KB
Line 
1/*
2 * fifo.c: POSIX FIFO/pipe for RTEMS
3 *
4 * Author: Wei Shen <cquark@gmail.com>
5 *
6 * The license and distribution terms for this file may be
7 * found in the file LICENSE in this distribution or at
8 * http://www.rtems.com/license/LICENSE.
9 *
10 * $Id$
11 */
12
13
14#if HAVE_CONFIG_H
15#include "config.h"
16#endif
17
18#ifdef RTEMS_POSIX_API
19#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
20#endif
21
22#include <errno.h>
23#include <stdlib.h>
24
25#include <rtems.h>
26#include <rtems/libio_.h>
27
28#include "pipe.h"
29
30
31#define MIN(a, b) ((a) < (b)? (a): (b))
32
33#define LIBIO_ACCMODE(_iop) ((_iop)->flags & LIBIO_FLAGS_READ_WRITE)
34#define LIBIO_NODELAY(_iop) ((_iop)->flags & LIBIO_FLAGS_NO_DELAY)
35
36static rtems_id pipe_semaphore = RTEMS_ID_NONE;
37
38
39#define PIPE_EMPTY(_pipe) (_pipe->Length == 0)
40#define PIPE_FULL(_pipe)  (_pipe->Length == _pipe->Size)
41#define PIPE_SPACE(_pipe) (_pipe->Size - _pipe->Length)
42#define PIPE_WSTART(_pipe) ((_pipe->Start + _pipe->Length) % _pipe->Size)
43
44#define PIPE_LOCK(_pipe)  \
45  ( rtems_semaphore_obtain(_pipe->Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT)  \
46   == RTEMS_SUCCESSFUL )
47
48#define PIPE_UNLOCK(_pipe)  rtems_semaphore_release(_pipe->Semaphore)
49
50#define PIPE_READWAIT(_pipe)  \
51  ( rtems_barrier_wait(_pipe->readBarrier, RTEMS_NO_TIMEOUT)  \
52   == RTEMS_SUCCESSFUL)
53
54#define PIPE_WRITEWAIT(_pipe)  \
55  ( rtems_barrier_wait(_pipe->writeBarrier, RTEMS_NO_TIMEOUT)  \
56   == RTEMS_SUCCESSFUL)
57
58#define PIPE_WAKEUPREADERS(_pipe) \
59  do {uint32_t n; rtems_barrier_release(_pipe->readBarrier, &n); } while(0)
60
61#define PIPE_WAKEUPWRITERS(_pipe) \
62  do {uint32_t n; rtems_barrier_release(_pipe->writeBarrier, &n); } while(0)
63
64
65#ifdef RTEMS_POSIX_API
66#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
67
68#include <rtems/rtems/barrier.h>
69#include <rtems/score/thread.h>
70
71/* Set barriers to be interruptible by signals. */
72static void pipe_interruptible(pipe_control_t *pipe)
73{
74  Objects_Locations location;
75
76  _Barrier_Get(pipe->readBarrier, &location)->Barrier.Wait_queue.state
77    |= STATES_INTERRUPTIBLE_BY_SIGNAL;
78  _Thread_Enable_dispatch();
79  _Barrier_Get(pipe->writeBarrier, &location)->Barrier.Wait_queue.state
80    |= STATES_INTERRUPTIBLE_BY_SIGNAL;
81  _Thread_Enable_dispatch();
82}
83#endif
84
85/*
86 * Alloc pipe control structure, buffer, and resources.
87 * Called with pipe_semaphore held.
88 */
89static int pipe_alloc(
90  pipe_control_t **pipep
91)
92{
93  static char c = 'a';
94  pipe_control_t *pipe;
95  int err = -ENOMEM;
96
97  pipe = malloc(sizeof(pipe_control_t));
98  if (pipe == NULL)
99    return err;
100  memset(pipe, 0, sizeof(pipe_control_t));
101
102  pipe->Size = PIPE_BUF;
103  pipe->Buffer = malloc(pipe->Size);
104  if (! pipe->Buffer)
105    goto err_buf;
106
107  if (rtems_barrier_create(
108        rtems_build_name ('P', 'I', 'r', c),
109        RTEMS_BARRIER_MANUAL_RELEASE, 0,
110        &pipe->readBarrier) != RTEMS_SUCCESSFUL)
111    goto err_rbar;
112  if (rtems_barrier_create(
113        rtems_build_name ('P', 'I', 'w', c),
114        RTEMS_BARRIER_MANUAL_RELEASE, 0,
115        &pipe->writeBarrier) != RTEMS_SUCCESSFUL)
116    goto err_wbar;
117  if (rtems_semaphore_create(
118        rtems_build_name ('P', 'I', 's', c), 1,
119        RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO,
120        RTEMS_NO_PRIORITY, &pipe->Semaphore) != RTEMS_SUCCESSFUL)
121    goto err_sem;
122
123#ifdef RTEMS_POSIX_API
124  pipe_interruptible(pipe);
125#endif
126
127  *pipep = pipe;
128  if (c ++ == 'z')
129    c = 'a';
130  return 0;
131
132err_sem:
133  rtems_barrier_delete(pipe->writeBarrier);
134err_wbar:
135  rtems_barrier_delete(pipe->readBarrier);
136err_rbar:
137  free(pipe->Buffer);
138err_buf:
139  free(pipe);
140  return err;
141}
142
143/* Called with pipe_semaphore held. */
144static inline void pipe_free(
145  pipe_control_t *pipe
146)
147{
148  rtems_barrier_delete(pipe->readBarrier);
149  rtems_barrier_delete(pipe->writeBarrier);
150  rtems_semaphore_delete(pipe->Semaphore);
151  free(pipe->Buffer);
152  free(pipe);
153}
154
155static rtems_status_code pipe_lock(void)
156{
157  rtems_status_code sc = RTEMS_SUCCESSFUL;
158
159  if (pipe_semaphore == RTEMS_ID_NONE) {
160    rtems_libio_lock();
161
162    if (pipe_semaphore == RTEMS_ID_NONE) {
163      sc = rtems_semaphore_create(
164        rtems_build_name('P', 'I', 'P', 'E'),
165        1,
166        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
167        RTEMS_NO_PRIORITY,
168        &pipe_semaphore
169      );
170    }
171
172    rtems_libio_unlock();
173  }
174
175  if (sc == RTEMS_SUCCESSFUL) {
176    sc = rtems_semaphore_obtain(pipe_semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
177  }
178
179  if (sc == RTEMS_SUCCESSFUL) {
180    return 0;
181  } else {
182    return -ENOMEM;
183  }
184}
185
186static void pipe_unlock(void)
187{
188  rtems_status_code sc = RTEMS_SUCCESSFUL;
189
190  sc = rtems_semaphore_release(pipe_semaphore);
191  if (sc != RTEMS_SUCCESSFUL) {
192    /* FIXME */
193    rtems_fatal_error_occurred(0xdeadbeef);
194  }
195}
196
197/*
198 * If called with *pipep = NULL, pipe_new will call pipe_alloc to allocate a
199 * pipe control structure and set *pipep to its address.
200 * pipe is locked, when pipe_new returns with no error.
201 */
202static int pipe_new(
203  pipe_control_t **pipep
204)
205{
206  pipe_control_t *pipe;
207  int err = 0;
208
209  err = pipe_lock();
210  if (err)
211    return err;
212
213  pipe = *pipep;
214  if (pipe == NULL) {
215    err = pipe_alloc(&pipe);
216    if (err)
217      goto out;
218  }
219
220  if (! PIPE_LOCK(pipe))
221    err = -EINTR;
222
223  if (*pipep == NULL) {
224    if (err)
225      pipe_free(pipe);
226    else
227      *pipep = pipe;
228  }
229
230out:
231  pipe_unlock();
232  return err;
233}
234
235/*
236 * Interface to file system close.
237 *
238 * *pipep points to pipe control structure. When the last user releases pipe,
239 * it will be set to NULL.
240 */
241int pipe_release(
242  pipe_control_t **pipep,
243  rtems_libio_t *iop
244)
245{
246  pipe_control_t *pipe = *pipep;
247  uint32_t mode;
248
249  if (pipe_lock())
250    /* WARN pipe not freed and pipep not set to NULL! */
251    /* FIXME */
252    rtems_fatal_error_occurred(0xdeadbeef);
253
254  if (!PIPE_LOCK(pipe))
255    /* WARN pipe not released! */
256    /* FIXME */
257    rtems_fatal_error_occurred(0xdeadbeef);
258
259  mode = LIBIO_ACCMODE(iop);
260  if (mode & LIBIO_FLAGS_READ)
261     pipe->Readers --;
262  if (mode & LIBIO_FLAGS_WRITE)
263     pipe->Writers --;
264
265  PIPE_UNLOCK(pipe);
266
267  if (pipe->Readers == 0 && pipe->Writers == 0) {
268#if 0
269    /* To delete an anonymous pipe file when all users closed it */
270    if (pipe->Anonymous)
271      delfile = TRUE;
272#endif
273    pipe_free(pipe);
274    *pipep = NULL;
275  }
276  else if (pipe->Readers == 0 && mode != LIBIO_FLAGS_WRITE)
277    /* Notify waiting Writers that all their partners left */
278    PIPE_WAKEUPWRITERS(pipe);
279  else if (pipe->Writers == 0 && mode != LIBIO_FLAGS_READ)
280    PIPE_WAKEUPREADERS(pipe);
281
282  pipe_unlock();
283
284#if 0
285  if (! delfile)
286    return 0;
287  if (iop->pathinfo.ops->unlink_h == NULL)
288    return 0;
289
290  /* This is safe for IMFS, but how about other FSes? */
291  iop->flags &= ~LIBIO_FLAGS_OPEN;
292  if(iop->pathinfo.ops->unlink_h(&iop->pathinfo))
293    return -errno;
294#endif
295
296  return 0;
297}
298
299/*
300 * Interface to file system open.
301 *
302 * *pipep points to pipe control structure. If called with *pipep = NULL,
303 * fifo_open will try allocating and initializing a control structure. If the
304 * call succeeds, *pipep will be set to address of new control structure.
305 */
306int fifo_open(
307  pipe_control_t **pipep,
308  rtems_libio_t *iop
309)
310{
311  pipe_control_t *pipe;
312  unsigned int prevCounter;
313  int err;
314
315  err = pipe_new(pipep);
316  if (err)
317    return err;
318  pipe = *pipep;
319
320  switch (LIBIO_ACCMODE(iop)) {
321    case LIBIO_FLAGS_READ:
322      pipe->readerCounter ++;
323      if (pipe->Readers ++ == 0)
324        PIPE_WAKEUPWRITERS(pipe);
325
326      if (pipe->Writers == 0) {
327        /* Not an error */
328        if (LIBIO_NODELAY(iop))
329          break;
330
331        prevCounter = pipe->writerCounter;
332        err = -EINTR;
333        /* Wait until a writer opens the pipe */
334        do {
335          PIPE_UNLOCK(pipe);
336          if (! PIPE_READWAIT(pipe))
337            goto out_error;
338          if (! PIPE_LOCK(pipe))
339            goto out_error;
340        } while (prevCounter == pipe->writerCounter);
341      }
342      break;
343
344    case LIBIO_FLAGS_WRITE:
345      if (pipe->Readers == 0 && LIBIO_NODELAY(iop)) {
346        err = -ENXIO;
347        goto out_error;
348      }
349
350      pipe->writerCounter ++;
351      if (pipe->Writers ++ == 0)
352        PIPE_WAKEUPREADERS(pipe);
353
354      if (pipe->Readers == 0) {
355        prevCounter = pipe->readerCounter;
356        err = -EINTR;
357        do {
358          PIPE_UNLOCK(pipe);
359          if (! PIPE_WRITEWAIT(pipe))
360            goto out_error;
361          if (! PIPE_LOCK(pipe))
362            goto out_error;
363        } while (prevCounter == pipe->readerCounter);
364      }
365      break;
366
367    case LIBIO_FLAGS_READ_WRITE:
368      pipe->readerCounter ++;
369      if (pipe->Readers ++ == 0)
370        PIPE_WAKEUPWRITERS(pipe);
371      pipe->writerCounter ++;
372      if (pipe->Writers ++ == 0)
373        PIPE_WAKEUPREADERS(pipe);
374      break;
375  }
376
377  PIPE_UNLOCK(pipe);
378  return 0;
379
380out_error:
381  pipe_release(pipep, iop);
382  return err;
383}
384
385/*
386 * Interface to file system read.
387 */
388ssize_t pipe_read(
389  pipe_control_t *pipe,
390  void           *buffer,
391  size_t          count,
392  rtems_libio_t  *iop
393)
394{
395  int chunk, chunk1, read = 0, ret = 0;
396
397  if (! PIPE_LOCK(pipe))
398    return -EINTR;
399
400  while (read < count) {
401    while (PIPE_EMPTY(pipe)) {
402      /* Not an error */
403      if (pipe->Writers == 0)
404        goto out_locked;
405
406      if (LIBIO_NODELAY(iop)) {
407        ret = -EAGAIN;
408        goto out_locked;
409      }
410
411      /* Wait until pipe is no more empty or no writer exists */
412      pipe->waitingReaders ++;
413      PIPE_UNLOCK(pipe);
414      if (! PIPE_READWAIT(pipe))
415        ret = -EINTR;
416      if (! PIPE_LOCK(pipe)) {
417        /* WARN waitingReaders not restored! */
418        ret = -EINTR;
419        goto out_nolock;
420      }
421      pipe->waitingReaders --;
422      if (ret != 0)
423        goto out_locked;
424    }
425
426    /* Read chunk bytes */
427    chunk = MIN(count - read,  pipe->Length);
428    chunk1 = pipe->Size - pipe->Start;
429    if (chunk > chunk1) {
430      memcpy(buffer + read, pipe->Buffer + pipe->Start, chunk1);
431      memcpy(buffer + read + chunk1, pipe->Buffer, chunk - chunk1);
432    }
433    else
434      memcpy(buffer + read, pipe->Buffer + pipe->Start, chunk);
435
436    pipe->Start += chunk;
437    pipe->Start %= pipe->Size;
438    pipe->Length -= chunk;
439    /* For buffering optimization */
440    if (PIPE_EMPTY(pipe))
441      pipe->Start = 0;
442
443    if (pipe->waitingWriters > 0)
444      PIPE_WAKEUPWRITERS(pipe);
445    read += chunk;
446  }
447
448out_locked:
449  PIPE_UNLOCK(pipe);
450
451out_nolock:
452  if (read > 0)
453    return read;
454  return ret;
455}
456
457/*
458 * Interface to file system write.
459 */
460ssize_t pipe_write(
461  pipe_control_t *pipe,
462  const void     *buffer,
463  size_t          count,
464  rtems_libio_t  *iop
465)
466{
467  int chunk, chunk1, written = 0, ret = 0;
468
469  /* Write nothing */
470  if (count == 0)
471    return 0;
472
473  if (! PIPE_LOCK(pipe))
474    return -EINTR;
475
476  if (pipe->Readers == 0) {
477    ret = -EPIPE;
478    goto out_locked;
479  }
480
481  /* Write of PIPE_BUF bytes or less shall not be interleaved */
482  chunk = count <= pipe->Size ? count : 1;
483
484  while (written < count) {
485    while (PIPE_SPACE(pipe) < chunk) {
486      if (LIBIO_NODELAY(iop)) {
487        ret = -EAGAIN;
488        goto out_locked;
489      }
490
491      /* Wait until there is chunk bytes space or no reader exists */
492      pipe->waitingWriters ++;
493      PIPE_UNLOCK(pipe);
494      if (! PIPE_WRITEWAIT(pipe))
495        ret = -EINTR;
496      if (! PIPE_LOCK(pipe)) {
497        /* WARN waitingWriters not restored! */
498        ret = -EINTR;
499        goto out_nolock;
500      }
501      pipe->waitingWriters --;
502      if (ret != 0)
503        goto out_locked;
504
505      if (pipe->Readers == 0) {
506        ret = -EPIPE;
507        goto out_locked;
508      }
509    }
510
511    chunk = MIN(count - written, PIPE_SPACE(pipe));
512    chunk1 = pipe->Size - PIPE_WSTART(pipe);
513    if (chunk > chunk1) {
514      memcpy(pipe->Buffer + PIPE_WSTART(pipe), buffer + written, chunk1);
515      memcpy(pipe->Buffer, buffer + written + chunk1, chunk - chunk1);
516    }
517    else
518      memcpy(pipe->Buffer + PIPE_WSTART(pipe), buffer + written, chunk);
519
520    pipe->Length += chunk;
521    if (pipe->waitingReaders > 0)
522      PIPE_WAKEUPREADERS(pipe);
523    written += chunk;
524    /* Write of more than PIPE_BUF bytes can be interleaved */
525    chunk = 1;
526  }
527
528out_locked:
529  PIPE_UNLOCK(pipe);
530
531out_nolock:
532#ifdef RTEMS_POSIX_API
533  /* Signal SIGPIPE */
534  if (ret == -EPIPE)
535    kill(getpid(), SIGPIPE);
536#endif
537
538  if (written > 0)
539    return written;
540  return ret;
541}
542
543/*
544 * Interface to file system ioctl.
545 */
546int pipe_ioctl(
547  pipe_control_t *pipe,
548  uint32_t        cmd,
549  void           *buffer,
550  rtems_libio_t  *iop
551)
552{
553  if (cmd == FIONREAD) {
554    if (buffer == NULL)
555      return -EFAULT;
556
557    if (! PIPE_LOCK(pipe))
558      return -EINTR;
559
560    /* Return length of pipe */
561    *(unsigned int *)buffer = pipe->Length;
562    PIPE_UNLOCK(pipe);
563    return 0;
564  }
565
566  return -EINVAL;
567}
568
569/*
570 * Interface to file system lseek.
571 */
572int pipe_lseek(
573  pipe_control_t *pipe,
574  off_t           offset,
575  int             whence,
576  rtems_libio_t  *iop
577)
578{
579  /* Seek on pipe is not supported */
580  return -ESPIPE;
581}
Note: See TracBrowser for help on using the repository browser.