source: rtems/cpukit/libfs/src/pipe/fifo.c @ 0298b5ef

4.10
Last change on this file since 0298b5ef was 0298b5ef, checked in by Chris Johns <chrisj@…>, on Jun 23, 2010 at 5:06:34 AM

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