aboutsummaryrefslogtreecommitdiff
path: root/kernel/fs/pipe.c
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/fs/pipe.c
Created student weenix repository
Diffstat (limited to 'kernel/fs/pipe.c')
-rw-r--r--kernel/fs/pipe.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/kernel/fs/pipe.c b/kernel/fs/pipe.c
new file mode 100644
index 0000000..b1d365f
--- /dev/null
+++ b/kernel/fs/pipe.c
@@ -0,0 +1,256 @@
+/*
+ * FILE: pipe.c
+ * AUTH: eric
+ * DESC: Implementation of pipe(2) system call.
+ * DATE: Thu Dec 26 17:08:34 2013
+ */
+
+#include "errno.h"
+#include "globals.h"
+
+#include "fs/file.h"
+#include "fs/pipe.h"
+#include "fs/stat.h"
+#include "fs/vfs.h"
+#include "fs/vfs_syscall.h"
+#include "fs/vnode.h"
+
+#include "mm/kmalloc.h"
+#include "mm/slab.h"
+
+#include "util/debug.h"
+#include "util/string.h"
+
+#define PIPE_BUF_SIZE 4096
+
+static void pipe_read_vnode(fs_t *fs, vnode_t *vnode);
+
+static void pipe_delete_vnode(fs_t *fs, vnode_t *vnode);
+
+static fs_ops_t pipe_fsops = {.read_vnode = pipe_read_vnode,
+ .delete_vnode = pipe_delete_vnode,
+ .umount = NULL};
+
+static fs_t pipe_fs = {.fs_dev = "pipe",
+ .fs_type = "pipe",
+ .fs_ops = &pipe_fsops,
+ .fs_root = NULL,
+ .fs_i = NULL};
+
+static long pipe_read(vnode_t *vnode, size_t pos, void *buf, size_t count);
+
+static long pipe_write(vnode_t *vnode, size_t pos, const void *buf,
+ size_t count);
+
+static long pipe_stat(vnode_t *vnode, stat_t *ss);
+
+static long pipe_acquire(vnode_t *vnode, file_t *file);
+
+static long pipe_release(vnode_t *vnode, file_t *file);
+
+static vnode_ops_t pipe_vops = {
+ .read = pipe_read,
+ .write = pipe_write,
+ .mmap = NULL,
+ .mknod = NULL,
+ .lookup = NULL,
+ .link = NULL,
+ .unlink = NULL,
+ .mkdir = NULL,
+ .rmdir = NULL,
+ .readdir = NULL,
+ .stat = pipe_stat,
+ .acquire = pipe_acquire,
+ .release = pipe_release,
+ .get_pframe = NULL,
+ .fill_pframe = NULL,
+ .flush_pframe = NULL,
+};
+
+/* struct pipe defines some data specific to pipes. One of these
+ should be present in the vn_i field of each pipe vnode. */
+typedef struct pipe
+{
+ /* Buffer for data in the pipe, which has been written but not yet read. */
+ char *pv_buf;
+ /*
+ * Position of the head and number of characters in the buffer. You can
+ * write in characters at position head so long as size does not grow beyond
+ * the pipe buffer size.
+ */
+ off_t pv_head;
+ size_t pv_size;
+ /* Number of file descriptors using this pipe for read and write. */
+ int pv_readers;
+ int pv_writers;
+ /*
+ * Mutexes for reading and writing. Without these, readers might get non-
+ * contiguous reads in a single call (for example, if they empty the buffer
+ * but still have more to read, then the writer continues writing, waking up
+ * a different thread first) and similarly for writers.
+ */
+ kmutex_t pv_rdlock;
+ kmutex_t pv_wrlock;
+ /*
+ * Waitqueues for threads attempting to read from an empty buffer, or
+ * write to a full buffer. When the pipe becomes non-empty (or non-full)
+ * then the corresponding waitq should be broadcasted on to make sure all
+ * of the threads get a chance to go.
+ */
+ ktqueue_t pv_read_waitq;
+ ktqueue_t pv_write_waitq;
+} pipe_t;
+
+#define VNODE_TO_PIPE(vn) ((pipe_t *)((vn)->vn_i))
+
+static slab_allocator_t *pipe_allocator = NULL;
+static int next_pno = 0;
+
+void pipe_init(void)
+{
+ pipe_allocator = slab_allocator_create("pipe", sizeof(pipe_t));
+ KASSERT(pipe_allocator);
+}
+
+/*
+ * Create a pipe struct here. You are going to need to allocate all
+ * of the necessary structs and buffers, and then initialize all of
+ * the necessary fields (head, size, readers, writers, and the locks
+ * and queues.)
+ */
+static pipe_t *pipe_create(void)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return NULL;
+}
+
+/*
+ * Free all necessary memory.
+ */
+static void pipe_destroy(pipe_t *pipe)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+}
+
+/* pipefs vnode operations */
+static void pipe_read_vnode(fs_t *fs, vnode_t *vnode)
+{
+ vnode->vn_ops = &pipe_vops;
+ vnode->vn_mode = S_IFIFO;
+ vnode->vn_len = 0;
+ vnode->vn_i = NULL;
+}
+
+static void pipe_delete_vnode(fs_t *fs, vnode_t *vnode)
+{
+ pipe_t *p = VNODE_TO_PIPE(vnode);
+ if (p)
+ {
+ pipe_destroy(p);
+ }
+}
+
+/*
+ * Gets a new vnode representing a pipe. The reason
+ * why we don't just do this setup in pipe_read_vnode
+ * is that the creation of the pipe data might fail, since
+ * there is memory allocation going on in there. Thus,
+ * we split it into two steps, the first of which relies on
+ * pipe_read_vnode to do some setup, and then the pipe_create
+ * call, at which point we can safely vput the allocated
+ * vnode if pipe_create fails.
+ */
+static vnode_t *pget(void)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return NULL;
+}
+
+/*
+ * An implementation of the pipe(2) system call. You really
+ * only have to worry about a few things:
+ * o Running out of memory when allocating the vnode, at which
+ * point you should fail with ENOMEM;
+ * o Running out of file descriptors, in which case you should
+ * fail with EMFILE.
+ * Once all of the structures are set up, just put the read-end
+ * file descriptor of the pipe into pipefd[0], and the write-end
+ * descriptor into pipefd[1].
+ */
+int do_pipe(int pipefd[2])
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return -ENOTSUP;
+}
+
+/*
+ * When reading from a pipe, you should make sure there are enough characters in
+ * the buffer to read. If there are, grab them and move up the tail by
+ * subtracting from size. offset is ignored. Also, remember to take the reader
+ * lock to prevent other threads from reading while you are waiting for more
+ * characters.
+ *
+ * This might block, e.g. if there are no or not enough characters to read.
+ * It might be the case that there are no more writers and we aren't done
+ * reading. However, in situations like this, there is no way to open the pipe
+ * for writing again so no more writers will ever put characters in the pipe.
+ * The reader should just take as much as it needs (or barring that, as much as
+ * it can get) and return with a partial buffer.
+ */
+static long pipe_read(vnode_t *vnode, size_t pos, void *buf, size_t count)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return -EINVAL;
+}
+
+/*
+ * Writing to a pipe is the dual of reading: if there is room, we can write our
+ * data and go, but if not, we have to wait until there is more room and alert
+ * any potential readers. Like above, you should take the writer lock to make
+ * sure your write is contiguous.
+ *
+ * If there are no more readers, we have a broken pipe, and should fail with
+ * the EPIPE error number.
+ */
+static long pipe_write(vnode_t *vnode, size_t pos, const void *buf,
+ size_t count)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return -EINVAL;
+}
+
+/*
+ * It's still possible to stat a pipe using the fstat call, which takes a file
+ * descriptor. Pipes don't have too much information, though. The only ones that
+ * matter here are st_mode and st_ino, though you want to zero out some of the
+ * others.
+ */
+static long pipe_stat(vnode_t *vnode, stat_t *ss)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return -EINVAL;
+}
+
+/*
+ * If someone is opening the read end of the pipe, we need to increment
+ * the reader count, and the same for the writer count if a file open
+ * for writing is acquiring this vnode. This count needs to be accurate
+ * for correct reading and writing behavior.
+ */
+static long pipe_acquire(vnode_t *vnode, file_t *file)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return 0;
+}
+
+/*
+ * Subtract from the reader or writer count as necessary here. If either
+ * count hits zero, you are going to need to wake up the other group of
+ * threads so they can either return with their partial read or notice
+ * the broken pipe.
+ */
+static long pipe_release(vnode_t *vnode, file_t *file)
+{
+ NOT_YET_IMPLEMENTED("PIPES: ***none***");
+ return 0;
+}