/**************************************************************************** * drivers/pipes/pipe_common.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/wait.h" #include #ifdef CONFIG_DEBUG_FEATURES # include #endif #include #include #include "pipe_common.h" #include "los_printf.h" #include "user_copy.h" #ifdef LOSCFG_KERNEL_PIPE /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* CONFIG_DEV_PIPEDUMP will dump the contents of each transfer into and out * of the pipe. */ #ifdef CONFIG_DEV_PIPEDUMP # define pipe_dumpbuffer(m,a,n) lib_dumpbuffer(m,a,n) #else # define pipe_dumpbuffer(m,a,n) #endif /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void pipecommon_semtake(sem_t *sem); /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: pipecommon_semtake ****************************************************************************/ static void pipecommon_semtake(sem_t *sem) { int ret; do { /* Take the semaphore (perhaps waiting) */ ret = sem_wait(sem); } while (ret == -EINTR); } /**************************************************************************** * Name: pipecommon_pollnotify ****************************************************************************/ static void pipecommon_pollnotify(struct pipe_dev_s *dev, pollevent_t eventset) { if (eventset & POLLERR) { eventset &= ~(POLLOUT | POLLIN); } notify_poll_with_key(&dev->wq, eventset); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: pipecommon_allocdev ****************************************************************************/ struct pipe_dev_s *pipecommon_allocdev(size_t bufsize, const char *name) { struct pipe_dev_s *dev = NULL; int ret; if (bufsize > CONFIG_DEV_PIPE_MAXSIZE) { return NULL; } /* Allocate a private structure to manage the pipe */ dev = (struct pipe_dev_s *)malloc(sizeof(struct pipe_dev_s)); if (dev) { /* Initialize the private structure */ (void)memset_s(dev, sizeof(struct pipe_dev_s), 0, sizeof(struct pipe_dev_s)); ret = strcpy_s(dev->name, sizeof(dev->name), name); if (ret < 0) { free(dev); return NULL; } sem_init(&dev->d_bfsem, 0, 1); sem_init(&dev->d_rdsem, 0, 0); sem_init(&dev->d_wrsem, 0, 0); LOS_ListInit(&dev->wq.poll_queue); /* The read/write wait semaphores are used for signaling and, hence, * should not have priority inheritance enabled. */ dev->d_bufsize = bufsize; } return dev; } /**************************************************************************** * Name: pipecommon_freedev ****************************************************************************/ void pipecommon_freedev(struct pipe_dev_s *dev) { sem_destroy(&dev->d_bfsem); sem_destroy(&dev->d_rdsem); sem_destroy(&dev->d_wrsem); free(dev); } /**************************************************************************** * Name: pipecommon_open ****************************************************************************/ int pipecommon_open(struct file *filep) { struct Vnode *vnode = filep->f_vnode; struct pipe_dev_s *dev = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv; int sval; int ret; if (dev == NULL) { return -EINVAL; } /* Make sure that we have exclusive access to the device structure. The * sem_wait() call should fail only if we are awakened by a signal. */ ret = sem_wait(&dev->d_bfsem); if (ret < 0) { PRINT_ERR("ERROR: sem_wait failed: %d\n", ret); return ret; } /* If this the first reference on the device, then allocate the buffer. * In the case of policy 1, the buffer already be present when the pipe * is first opened. */ if (vnode->useCount == 1 && dev->d_buffer == NULL) { dev->d_buffer = (uint8_t *)malloc(dev->d_bufsize); if (!dev->d_buffer) { (void)sem_post(&dev->d_bfsem); return -ENOMEM; } } /* If opened for writing, increment the count of writers on the pipe instance */ if ((filep->f_oflags & O_WRONLY) != 0) { dev->d_nwriters++; /* If this this is the first writer, then the read semaphore indicates the * number of readers waiting for the first writer. Wake them all up. */ if (dev->d_nwriters == 1) { while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0) { sem_post(&dev->d_rdsem); } } } /* If opened for reading, increment the count of reader on on the pipe instance */ if ((filep->f_oflags & O_WRONLY) == 0) { dev->d_nreaders++; } /* If opened for read-only, then wait for either (1) at least one writer * on the pipe (policy == 0), or (2) until there is buffered data to be * read (policy == 1). */ (void)sem_post(&dev->d_bfsem); if ((filep->f_oflags & O_WRONLY) == 0 && /* Read-only */ dev->d_nwriters < 1 && /* No writers on the pipe */ dev->d_wrndx == dev->d_rdndx) /* Buffer is empty */ { /* NOTE: d_rdsem is normally used when the read logic waits for more * data to be written. But until the first writer has opened the * pipe, the meaning is different: it is used prevent O_RDONLY open * calls from returning until there is at least one writer on the pipe. * This is required both by spec and also because it prevents * subsequent read() calls from returning end-of-file because there is * no writer on the pipe. */ ret = sem_wait(&dev->d_rdsem); if (ret < 0) { /* The sem_wait() call should fail only if we are awakened by * a signal. */ PRINT_ERR("ERROR: sem_wait failed: %d\n", ret); /* Immediately close the pipe that we just opened */ (void)pipecommon_close(filep); } } VnodeHold(); vnode->useCount++; VnodeDrop(); return ret; } /**************************************************************************** * Name: pipecommon_close ****************************************************************************/ int pipecommon_close(struct file *filep) { struct Vnode *vnode = filep->f_vnode; struct pipe_dev_s *dev = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv; int sval; if (dev == NULL || filep->f_vnode->useCount <= 1) { return -EINVAL; } /* Make sure that we have exclusive access to the device structure. * NOTE: close() is supposed to return EINTR if interrupted, however * I've never seen anyone check that. */ pipecommon_semtake(&dev->d_bfsem); /* Decrement the number of references on the pipe. Check if there are * still outstanding references to the pipe. */ /* Check if the decremented vnode reference count would go to zero */ if (vnode->useCount > 1) { /* More references.. If opened for writing, decrement the count of * writers on the pipe instance. */ if ((filep->f_oflags & O_WRONLY) != 0) { /* If there are no longer any writers on the pipe, then notify all of the * waiting readers that they must return end-of-file. */ if (--dev->d_nwriters <= 0) { while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0) { sem_post(&dev->d_rdsem); } /* Inform poll readers that other end closed. */ pipecommon_pollnotify(dev, POLLHUP); } } /* If opened for reading, decrement the count of readers on the pipe * instance. */ if ((filep->f_oflags & O_WRONLY) == 0) { if (--dev->d_nreaders <= 0) { if (PIPE_IS_POLICY_0(dev->d_flags)) { /* Inform poll writers that other end closed. */ pipecommon_pollnotify(dev, POLLERR); } } } } /* What is the buffer management policy? Do we free the buffer when the * last client closes the pipe policy 0, or when the buffer becomes empty. * In the latter case, the buffer data will remain valid and can be * obtained when the pipe is re-opened. */ else if (PIPE_IS_POLICY_0(dev->d_flags) || dev->d_wrndx == dev->d_rdndx) { /* Policy 0 or the buffer is empty ... deallocate the buffer now. */ free(dev->d_buffer); dev->d_buffer = NULL; /* And reset all counts and indices */ dev->d_wrndx = 0; dev->d_rdndx = 0; dev->d_nwriters = 0; dev->d_nreaders = 0; } VnodeHold(); vnode->useCount--; VnodeDrop(); sem_post(&dev->d_bfsem); return OK; } /**************************************************************************** * Name: pipecommon_read ****************************************************************************/ ssize_t pipecommon_read(struct file *filep, char *buffer, size_t len) { struct Vnode *vnode = filep->f_vnode; struct pipe_dev_s *dev = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv; ssize_t nread; int sval; int ret; volatile int num; if (dev == NULL) { return -EINVAL; } if (len == 0) { return 0; } if (len > MAX_READ_WRITE_LEN) { return -EINVAL; } /* Make sure that we have exclusive access to the device structure */ ret = sem_wait(&dev->d_bfsem); if (ret < 0) { return ret; } /* If the pipe is empty, then wait for something to be written to it */ while (dev->d_wrndx == dev->d_rdndx) { /* If O_NONBLOCK was set, then return EGAIN */ if (filep->f_oflags & O_NONBLOCK) { sem_post(&dev->d_bfsem); return -EAGAIN; } /* If there are no writers on the pipe, then return end of file */ if (dev->d_nwriters <= 0) { sem_post(&dev->d_bfsem); return 0; } /* Otherwise, wait for something to be written to the pipe */ sem_post(&dev->d_bfsem); ret = sem_wait(&dev->d_rdsem); if (ret < 0 || (ret = sem_wait(&dev->d_bfsem)) < 0) { return ret; } } /* Then return whatever is available in the pipe (which is at least one byte) */ nread = 0; for (; ; ) { while ((size_t)nread < len && dev->d_wrndx != dev->d_rdndx) { ret = LOS_ArchCopyToUser(buffer, dev->d_buffer + dev->d_rdndx, sizeof(char)); if (ret != 0) { sem_post(&dev->d_bfsem); return -EFAULT; } buffer++; if (++dev->d_rdndx >= dev->d_bufsize) { dev->d_rdndx = 0; } nread++; } /* Is the read complete? */ if ((size_t)nread >= len) { break; } /* Notify all waiting writers that bytes have been read from the buffer */ num = 0; while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval == 0) { sem_post(&dev->d_wrsem); num++; } /* If there are no writers be awakened, then return */ if (num == 0 || num == 1) { pipecommon_pollnotify(dev, POLLOUT); sem_post(&dev->d_bfsem); return nread; } /* If there are no writers on the pipe, then return the number of read bytes */ if (dev->d_nwriters <= 0) { sem_post(&dev->d_wrsem); pipecommon_pollnotify(dev, POLLOUT); sem_post(&dev->d_bfsem); return nread; } /* wait for something to be written to the pipe */ pipecommon_pollnotify(dev, POLLOUT); while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval != 0) { sem_wait(&dev->d_rdsem); } sem_post(&dev->d_bfsem); ret = sem_wait(&dev->d_rdsem); if (ret < 0) { return ret; } ret = sem_wait(&dev->d_bfsem); if (ret < 0) { return ret; } } /* Notify all waiting writers that bytes have been removed from the buffer */ while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval == 0) { sem_post(&dev->d_wrsem); } /* Notify all poll/select waiters that they can write to the FIFO */ pipecommon_pollnotify(dev, POLLOUT); sem_post(&dev->d_bfsem); return nread; } /**************************************************************************** * Name: pipecommon_write ****************************************************************************/ ssize_t pipecommon_write(struct file *filep, const char *buffer, size_t len) { struct Vnode *vnode = filep->f_vnode; struct pipe_dev_s *dev = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv; ssize_t nwritten = 0; ssize_t last; int nxtwrndx; int sval; int ret; if (dev == NULL) { return -EINVAL; } /* Handle zero-length writes */ if (len == 0) { return 0; } if (len > MAX_READ_WRITE_LEN) { return -EINVAL; } /* REVISIT: "If all file descriptors referring to the read end of a pipe * have been closed, then a write will cause a SIGPIPE signal to be * generated for the calling process. If the calling process is ignoring * this signal, then write(2) fails with the error EPIPE." */ if (dev->d_nreaders <= 0) { return -EPIPE; } /* At present, this method cannot be called from interrupt handlers. That * is because it calls sem_wait (via pipecommon_semtake below) and * sem_wait cannot be called from interrupt level. This actually * happens fairly commonly IF [a-z]err() is called from interrupt handlers * and stdout is being redirected via a pipe. In that case, the debug * output will try to go out the pipe (interrupt handlers should use the * _err() APIs). * * On the other hand, it would be very valuable to be able to feed the pipe * from an interrupt handler! TODO: Consider disabling interrupts instead * of taking semaphores so that pipes can be written from interrupt handlers */ /* Make sure that we have exclusive access to the device structure */ ret = sem_wait(&dev->d_bfsem); if (ret < 0) { return ret; } /* Loop until all of the bytes have been written */ last = 0; for (; ; ) { /* Calculate the write index AFTER the next byte is written */ nxtwrndx = dev->d_wrndx + 1; if (nxtwrndx >= dev->d_bufsize) { nxtwrndx = 0; } /* Would the next write overflow the circular buffer? */ if (nxtwrndx != dev->d_rdndx) { /* No... copy the byte */ ret = LOS_ArchCopyFromUser(dev->d_buffer + dev->d_wrndx, buffer, sizeof(char)); if (ret != 0) { sem_post(&dev->d_bfsem); return -EFAULT; } buffer++; dev->d_wrndx = nxtwrndx; /* Is the write complete? */ nwritten++; if ((size_t)nwritten >= len) { /* Yes.. Notify all of the waiting readers that more data is available */ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0) { sem_post(&dev->d_rdsem); } /* Notify all poll/select waiters that they can read from the FIFO */ pipecommon_pollnotify(dev, POLLIN); /* Return the number of bytes written */ sem_post(&dev->d_bfsem); return len; } } else { /* There is not enough room for the next byte. Was anything written in this pass? */ if (last < nwritten) { /* Yes.. Notify all of the waiting readers that more data is available */ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0) { sem_post(&dev->d_rdsem); } /* Notify all poll/select waiters that they can read from the FIFO */ pipecommon_pollnotify(dev, POLLIN); } last = nwritten; /* If O_NONBLOCK was set, then return partial bytes written or EGAIN */ if (filep->f_oflags & O_NONBLOCK) { if (nwritten == 0) { nwritten = -EAGAIN; } sem_post(&dev->d_bfsem); return nwritten; } /* There is more to be written.. wait for data to be removed from the pipe */ while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval != 0) { pipecommon_semtake(&dev->d_wrsem); } sem_post(&dev->d_bfsem); pipecommon_semtake(&dev->d_wrsem); pipecommon_semtake(&dev->d_bfsem); } } } /**************************************************************************** * Name: pipecommon_poll ****************************************************************************/ int pipecommon_poll(struct file *filep, poll_table *table) { struct Vnode *vnode = filep->f_vnode; struct pipe_dev_s *dev = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv; pollevent_t eventset; pipe_ndx_t nbytes; int ret; if (dev == NULL || table == NULL) { return -EINVAL; } /* Are we setting up the poll? Or tearing it down? */ pipecommon_semtake(&dev->d_bfsem); /* Should immediately notify on any of the requested events? * First, determine how many bytes are in the buffer */ if (dev->d_wrndx >= dev->d_rdndx) { nbytes = dev->d_wrndx - dev->d_rdndx; } else { nbytes = dev->d_bufsize + dev->d_wrndx - dev->d_rdndx; } /* Notify the POLLOUT event if the pipe is not full, but only if * there is readers. */ eventset = 0; if (((filep->f_oflags & O_WRONLY) != 0) && (nbytes < (dev->d_bufsize - 1))) { eventset |= POLLOUT; } /* Notify the POLLIN event if the pipe is not empty */ if (((filep->f_oflags & O_WRONLY) == 0) && (nbytes > 0)) { eventset |= POLLIN; } /* Notify the POLLHUP event if the pipe is empty and no writers */ if (nbytes == 0 && dev->d_nwriters <= 0) { eventset |= POLLHUP; } /* Change POLLOUT to POLLERR, if no readers and policy 0. */ if ((eventset | POLLOUT) && PIPE_IS_POLICY_0(dev->d_flags) && dev->d_nreaders <= 0) { eventset |= POLLERR; } ret = eventset & table->key; if (ret == 0) { poll_wait(NULL, &dev->wq, table); } sem_post(&dev->d_bfsem); return ret; } /**************************************************************************** * Name: pipecommon_ioctl ****************************************************************************/ int pipecommon_ioctl(struct file *filep, int cmd, unsigned long arg) { return -ENOSYS; } /**************************************************************************** * Name: pipecommon_unlink ****************************************************************************/ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS int pipecommon_unlink(struct Vnode *vnode) { struct pipe_dev_s *dev = NULL; if (vnode == NULL || vnode->data == NULL) { return -EINVAL; } dev = ((struct drv_data *)vnode->data)->priv; if (dev == NULL) { return -EINVAL; } if (dev->d_buffer) { free(dev->d_buffer); } /* And free the device structure. */ unregister_driver(dev->name); pipecommon_freedev(dev); return OK; } #endif #endif /* LOSCFG_KERNEL_PIPE */