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

4.10
Last change on this file since f33be44 was f33be44, checked in by Joel Sherrill <joel.sherrill@…>, on 06/24/10 at 19:58:00

2010-06-24 Bharath Suri <bharath.s.jois@…>

PR 1542/filesystem
PR 1585/filesystem

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