Add FD tap infrastructure (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 30 Jul 2015 20:18:01 +0000 (16:18 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 28 Sep 2015 19:14:00 +0000 (15:14 -0400)
FD taps allow the user to receive events when certain things happen to
an FD's underlying disk/device file.  Think epoll/kqueue.  The specific
filters and commands are subject to change.

This commit adds a syscall and the device-independent infrastructure to
pass the command to the device and to deal with all of the issues
related to registration, removal, and other concurrent operations.

Unlike epoll, the FD tap is tracked on the FD and within the device (if
the device so chooses), and this leads to a certain amount of
complexity.  Check out the documentation for details.

Reinstall your kernel headers (make xcc-install-headers).

12 files changed:
Documentation/fd_taps.txt [new file with mode: 0644]
kern/include/fdtap.h [new file with mode: 0644]
kern/include/ns.h
kern/include/ros/bits/syscall.h
kern/include/ros/fdtap.h [new file with mode: 0644]
kern/include/vfs.h
kern/src/Kbuild
kern/src/fdtap.c [new file with mode: 0644]
kern/src/syscall.c
kern/src/vfs.c
user/parlib/include/parlib.h
user/parlib/syscall.c

diff --git a/Documentation/fd_taps.txt b/Documentation/fd_taps.txt
new file mode 100644 (file)
index 0000000..dc335a4
--- /dev/null
@@ -0,0 +1,172 @@
+FD Taps
+===========================
+2015-07-27 Barret Rhoden (brho)
+
+Contents
+---------------------------
+What are FD Taps?
+Where are the FD Taps?
+
+
+What are FD Taps?
+---------------------------
+
+Where are the FD Taps?
+---------------------------
+### Basics ###
+In Linux, the epoll blob is attached to the File (I think, this is the struct
+eventpoll).  Linux can get from a sock -> socket -> file -> eventpoll.  From the
+lower levels of the networking stack, you can get all the way to the accounting
+info for epoll.
+
+In Akaros, and in Plan 9, the analogous object to the file is the chan.
+However, in the networking stack, the conversation (like a struct sock) does not
+keep a pointer to it's chan.  Further, there is not a 1:1 correspondence between
+convs and chans: there could be several chans using the same conv, similar to
+using several OS files for the same underlying disk file (inode).  Although that
+might be a bad idea for network connections, it'd be nice to not have FD Taps
+assume anything about the underlying device.  So for Akaros, we want to have the
+tap somewhere within the device.  For #I, that probably means hanging off the
+conversation.  For #M (devmnt), it would be some other struct, where the tap is
+translated into a 9p message.
+
+Another aspect of this issue is that these are "FD" taps, not "file/chan" taps.
+If you read through the Q&A for epoll's man page, there are a bunch of weird
+conditions that result from having the tap on the file.  This is due to having
+multiple FDs point to the same file.
+
+The approach I took in Akaros was to have the tap in both the FD and within the
+device (the conversation).  If we're declaring interest in an FD, the FD is a
+reasonable place to track that interest.  We also need to track the tap within
+the device, as mentioned above.  Now we need to sort out the registration of
+taps and avoid any concurrency issues.
+
+### Code Issues ###
+We need to worry about a few things.  Overall, we want to register a tap on an
+FD (struct file_desc), and that registration needs to go through the device.
+Perhaps the device doesn't support taps, or it doesn't support the event filters
+we requested.  So we need to handle registration failure.  We also need to
+handle concurrent deregistrations, re-registrations, opens, and and closes.
+
+A basic approach would be to lock the FD table, make sure there's only one tap,
+register the new one with the device, insert into the table, and unlock.  The
+lock protects adding the tap (can only have one, racing on the FD's tap
+pointer), concurrent tap removals, enforces the FD points to a file, and
+protects against FD closes.
+
+But the problem is the FD table lock is a spinlock, and we don't want it to be
+more than that.  Device registration could be a blocking call.  So we need to
+come up with something else.  Part of the problem involves syncing with two
+places: the FD and the conv.
+
+At this point I thought about putting the tap in the device, and not the FD at
+all.  Deregistration becomes tricky.  We want to destroy the tap when the FD
+closes, or at least turn it off.  Say we do something like "after closing,
+deregister the tap".  We could come up with enough info to the device to make it
+work - we'd probably want to pass in the FD (integer), proc*, and probably the
+chan.  However, once we closed, the FD is now free, and we could have something
+like:
+       Trying to close:                                                User opens and taps a conv:
+       close(5) (FD 5 was 1/data with a tap)
+                                                                                       open(/net/tcp/1/data) (get 5 back)
+                                                                                       register_fd_tap(5) (two taps on 5, might fail!)
+       deregister_fd_tap(5)
+       cclose (needed to keep the chan alive)
+At the end, we might have no taps on 5.  Or if we opened 2/data instead of
+1/data, the deregister_fd_tap call will accidentally deregister from the new FD
+5 instead of the old one, and the old one will still be active!
+
+Maybe we deregister first, then close, to avoid FD reuse problems.  Remember
+that the only locking goes on in close.  Now consider:
+       Trying to close:                                                User tries to add (another) tap:
+       deregister_fd_tap(5)
+                                                                                       register_fd_tap(5)
+       close(5) (was 1/data with a tap)
+Now we just closed with a tap still registered.  Eventually, that FD tap might
+fire.  Spurious events are okay, but we could run into issues.  Say the evq in
+the original tap is no longer valid.  It was buggy for the user to perform this
+operation, but there are probably other issues.  And we didn't even get in to
+how registration works (register before putting it in the FD table?  After?
+What about concurrent ops?)
+
+We could flag the FD as 'untappable'.  But it seems that we're going to need to
+sync with the FD table regardless of where the tap exists.  We might as well go
+back to the original plan of having the tap hang off the FD in some manner.  It
+makes the most sense, aesthetically, since the FD tap is an attribute of the FD.
+
+One trick that would help with FD reuse is to have the device op for
+register/deregister take the fd_tap pointer.  Not only can we squeeze more info
+in the tap without mucking with the function signature, but the main benefit is
+that so long as the FD tap is allocated, it is unique.  FD = 5 can be reused.
+FD_tap = 0xffff800012345678 is unique.
+
+However, simply adding the tap pointer to register() isn't enough.  Say we did
+the basic "lock the FD table, (basic checks), attach the pointer, unlock, call
+device register, then free it if register fails", and a dereg locks the table,
+yanks it out, then call device dereg, then frees.  We still have some issues:
+
+- What if a deregister occurs while we are still trying to register and failed?
+  Who actually frees the FD tap?  We can't completely free it while the other op
+  is in progress.  That sounds like a job for a kref on the FD tap.
+
+- What if we added the tap, then go to register, then it fails, then we have a
+  concurrent close try to deregister it.  Now we have concurrent deregisters.
+  We can deal with this by having the device op accept spurious deregisters, but
+  that's ugly (and unnecessary, see below).
+
+- What if a legit deregister occurs while we are registering and eventually will
+  succeed?  Say:
+                                                                                       sys_register_fd_tap(0xf00)
+                                                                                       adds to fdset, unlocks
+       close(5)
+       yanks 0xf00 from the fd
+       deregister tap 0xf00 (fails, spurious)
+                                                                                       register tap(chan, 0xf00)
+       free 0xf00?
+The deregister fails, since it was never there (remember we said it could have
+spurious deregister calls).  Then register happens.  But the FD is closed!  And
+then who is freeing the tap?  Hopefully we don't free it while the device still
+has a pointer...
+
+The issue here is the assumption that the tap would have been registered.  Since
+we unlock the FD table, we can violate those assumptions.  We want to guarantee
+the order of register/deregister operations, such that register happens before
+deregister.
+
+It turns out that the kref can do this too!  The trick is to use the release
+operation to do the deregistration.  That ensures that so long as a reference is
+held, we won't call deregister *and* that deregister will happen exactly once.
+close() simply becomes "lock the FDT, remove the tap, unlock, decref": extremely
+simple.  Note that decref could trigger the release method which could then
+sleep (since it calls into a device), so we decref outside the lock.  register()
+ups the refcnt by two, one for itself to keep the tap alive (and preventing a
+concurrent dereg) and one for the pointer in the FD table.
+
+Note that as soon as we unlock, our tap could be decref'd and a completely new
+tap could be added and registered for that FD.  That means the following can
+happen:
+       lock FDT
+       add tap 0xf00 to FD 5
+       unlock FDT
+                                                                                       lock FDT
+                                                                                       remove tap from FD 5
+                                                                                       unlock FDT
+                                                                                       decref 0xf00
+                                                                                       (new syscall)
+                                                                                       lock FDT
+                                                                                       add tap 0xbar to FD 5
+                                                                                       unlock FDT
+                                                                                       register tap 0xbar for FD 5
+       register tap 0xf00 for FD 5
+       decref and trigger a deregister of f00
+
+In this case the device could see two separate taps (0xf00 and 0xbar) for the
+same FD (5).  It just so happens that one of them will deregister soon.  It is
+also possible for an event to fire between the left column's register and
+decref, at which point two events would be created (possibly with the same evq
+and event id).
+
+The final case to consider is when registration fails.  To keep things simple
+for the device, we can make sure that we only deregister a tap if our register
+succeeded.  To do this nicely with krefs, we can simply change the release
+method, based on whether or not registration succeeds.
diff --git a/kern/include/fdtap.h b/kern/include/fdtap.h
new file mode 100644 (file)
index 0000000..d320824
--- /dev/null
@@ -0,0 +1,38 @@
+/* Copyright (c) 2015 Google Inc
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * FD taps.  Allows the user to receive events when certain things happen to an
+ * FD's underlying disk/device file. */
+
+#ifndef ROS_KERN_FDTAP_H
+#define ROS_KERN_FDTAP_H
+
+#include <ros/fdtap.h>
+#include <sys/queue.h>
+#include <kref.h>
+
+struct proc;
+struct event_queue;
+struct chan;
+
+struct fd_tap;
+SLIST_HEAD(fdtap_slist, fd_tap);
+
+struct fd_tap {
+       SLIST_ENTRY(fd_tap)                     link;   /* for device use */
+       struct kref                                     kref;
+       struct chan                                     *chan;
+       int                                                     fd;
+       int                                                     filter;
+       struct proc                                     *proc;
+       struct event_queue                      *ev_q;
+       int                                                     ev_id;
+       void                                            *data;
+};
+
+int add_fd_tap(struct proc *p, struct fd_tap_req *tap_req);
+int remove_fd_tap(struct proc *p, int fd);
+int fire_tap(struct fd_tap *tap, int filter);
+
+#endif /* ROS_KERN_FDTAP_H */
index c19eb60..533d2b0 100644 (file)
@@ -7,6 +7,7 @@
 #include <rendez.h>
 #include <rwlock.h>
 #include <linker_func.h>
+#include <fdtap.h>
 
 /*
  * functions (possibly) linked in, complete, from libc.
@@ -433,6 +434,7 @@ struct dev {
        void (*power) (int);            /* power mgt: power(1) → on, power (0) → off */
 //  int (*config)( int unused_int, char *unused_char_p_t, DevConf*);
        char *(*chaninfo) (struct chan *, char *, size_t);
+       int (*tapfd) (struct chan *, struct fd_tap *, int);
        /* we need to be aligned, we think to 64 bytes, for the linker tables. */
 } __attribute__ ((aligned(64)));
 
index b6a04b0..0a5c243 100644 (file)
@@ -74,6 +74,7 @@
 #define SYS_rename                             123
 #define SYS_fchdir                             124
 #define SYS_dup_fds_to                 125
+#define SYS_tap_fds                            126
 
 /* Misc syscalls */
 #define SYS_gettimeofday               140
diff --git a/kern/include/ros/fdtap.h b/kern/include/ros/fdtap.h
new file mode 100644 (file)
index 0000000..c844c8c
--- /dev/null
@@ -0,0 +1,47 @@
+/* Copyright (c) 2015 Google Inc
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details. */
+
+#ifndef ROS_INC_FDTAP_H
+#define ROS_INC_FDTAP_H
+
+#include <ros/event.h>
+
+/* FD Tap commands.  The commands get passed to the device, but intermediate
+ * code will process them to some extent. */
+#define FDTAP_CMD_ADD                  1
+#define FDTAP_CMD_REM                  2
+#define FDTAP_CMD_MOD                  3
+
+/* FD Tap Event/Filter types.  These are somewhat a mix of kqueue and epoll
+ * filters and are in flux.  For instance, we don't support things like
+ * ONESHOT/DISPATCH yet.
+ *
+ * When using these, you're communicating directly with the device, so really
+ * anything goes, but we'll try to standardize on a few flags. */
+#define FDTAP_FILT_READABLE            0x00000001
+#define FDTAP_FILT_WRITABLE            0x00000002
+#define FDTAP_FILT_WRITTEN             0x00000004
+#define FDTAP_FILT_DELETED             0x00000008
+#define FDTAP_FILT_ERROR               0x00000010      /* may overwrite *data */
+#define FDTAP_FILT_RENAME              0x00000020
+#define FDTAP_FILT_TRUNCATE            0x00000040
+#define FDTAP_FILT_ATTRIB              0x00000080
+#define FDTAP_FILT_PRIORITY            0x00000100
+#define FDTAP_FILT_HANGUP              0x00000200
+#define FDTAP_FILT_RDHUP               0x00000400
+
+/* When an event on FD matches filter, that event will be sent to ev_q with
+ * ev_id, with an optional data blob passed back.  The specifics will depend on
+ * the type of ev_q used.  For a CEQ, the event will coalesce, and the data will
+ * be a 'last write wins'. */
+struct fd_tap_req {
+       int                                                     fd;
+       int                                                     cmd;
+       int                                                     filter;
+       int                                                     ev_id;
+       struct event_queue                      *ev_q;
+       void                                            *data;
+};
+
+#endif /* ROS_INC_FDTAP_H */
index 99f53cd..b4d95d4 100644 (file)
@@ -23,6 +23,7 @@
 #include <hashtable.h>
 #include <pagemap.h>
 #include <blockdev.h>
+#include <fdtap.h>
 
 /* ghetto preprocessor hacks (since proc includes vfs) */
 struct page;
@@ -377,6 +378,7 @@ struct file_desc {
        struct file                                     *fd_file;
        struct chan                                     *fd_chan;
        unsigned int                            fd_flags;
+       struct fd_tap                           *fd_tap;
 };
 
 /* All open files for a process */
index 2302938..51b93eb 100644 (file)
@@ -15,6 +15,7 @@ obj-y                                         += err.o
 obj-$(CONFIG_ETH_AUDIO)                += eth_audio.o
 obj-y                                          += event.o
 obj-y                                          += ext2fs.o
+obj-y                                          += fdtap.o
 obj-y                                          += find_next_bit.o
 obj-y                                          += find_last_bit.o
 obj-y                                          += frontend.o
diff --git a/kern/src/fdtap.c b/kern/src/fdtap.c
new file mode 100644 (file)
index 0000000..26a1397
--- /dev/null
@@ -0,0 +1,169 @@
+/* Copyright (c) 2015 Google Inc
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * FD taps.  Allows the user to receive events when certain things happen to an
+ * FD's underlying device file/qid. */
+
+#include <fdtap.h>
+#include <vfs.h>
+#include <event.h>
+#include <kmalloc.h>
+#include <error.h>
+
+
+static void tap_min_release(struct kref *kref)
+{
+       struct fd_tap *tap = container_of(kref, struct fd_tap, kref);
+       cclose(tap->chan);
+       kfree(tap);
+}
+
+static void tap_full_release(struct kref *kref)
+{
+       struct fd_tap *tap = container_of(kref, struct fd_tap, kref);
+       devtab[tap->chan->type].tapfd(tap->chan, tap, FDTAP_CMD_REM);
+       tap_min_release(kref);
+}
+
+/* Adds a tap with the file/qid of the underlying device for the requested FD.
+ * The FD must be a chan, and the device must support the filter requested.
+ *
+ * Returns -1 or some other device-specific non-zero number on failure, 0 on
+ * success. */
+int add_fd_tap(struct proc *p, struct fd_tap_req *tap_req)
+{
+       struct fd_table *fdt = &p->open_files;
+       struct fd_tap *tap;
+       int ret = 0;
+       struct chan *chan;
+       int fd = tap_req->fd;
+
+       if (fd < 0) {
+               set_errno(EBADF);
+               return -1;
+       }
+       tap = kzmalloc(sizeof(struct fd_tap), KMALLOC_WAIT);
+       tap->proc = p;
+       tap->fd = fd;
+       tap->filter = tap_req->filter;
+       tap->ev_q = tap_req->ev_q;
+       tap->ev_id = tap_req->ev_id;
+       tap->data = tap_req->data;
+
+       spin_lock(&fdt->lock);
+       if (fd >= fdt->max_fdset) {
+               set_errno(ENFILE);
+               goto out_with_lock;
+       }
+       if (!GET_BITMASK_BIT(fdt->open_fds->fds_bits, fd)) {
+               set_errno(EBADF);
+               goto out_with_lock;
+       }
+       if (!fdt->fd[fd].fd_chan) {
+               set_errno(EINVAL);
+               set_errstr("Can't tap a VFS file");
+               goto out_with_lock;
+       }
+       chan = fdt->fd[fd].fd_chan;
+       if (fdt->fd[fd].fd_tap) {
+               set_errno(EBUSY);
+               set_errstr("FD %d already has a tap", fd);
+               goto out_with_lock;
+       }
+       if (!devtab[chan->type].tapfd) {
+               set_errno(ENOSYS);
+               set_errstr("Device %c does not handle taps", devtab[chan->type].dc);
+               goto out_with_lock;
+       }
+       /* need to keep chan alive for our call to the device.  someone else
+        * could come in and close the FD and the chan, once we unlock */
+       chan_incref(chan);
+       tap->chan = chan;
+       /* One for the FD table, one for us to keep the removal of *this* tap from
+        * happening until we've attempted to register with the device. */
+       kref_init(&tap->kref, tap_full_release, 2);
+       fdt->fd[fd].fd_tap = tap;
+       /* As soon as we unlock, another thread can come in and remove our old tap
+        * from the table and decref it.  Our ref keeps us from removing it yet,
+        * as well as keeps the memory safe.  However, a new tap can be installed
+        * and registered with the device before we even attempt to register.  The
+        * devices should be able to handle multiple, distinct taps, even if they
+        * happen to have the same {proc, fd} tuple. */
+       spin_unlock(&fdt->lock);
+       /* For refcnting fans, the tap ref is weak/uncounted.  We'll protect the
+        * memory and call the device when tap is being released. */
+       ret = devtab[chan->type].tapfd(chan, tap, FDTAP_CMD_ADD);
+       if (ret) {
+               /* we failed, so we need to make sure *our* tap is removed.  We haven't
+                * decreffed, so we know our tap pointer is unique. */
+               spin_lock(&fdt->lock);
+               if (fdt->fd[fd].fd_tap == tap) {
+                       fdt->fd[fd].fd_tap = 0;
+                       /* normally we can't decref a tap while holding a lock, but we
+                        * know we have another reference so this won't trigger a release */
+                       kref_put(&tap->kref);
+               }
+               spin_unlock(&fdt->lock);
+               /* Regardless of whether someone else removed it or not, *we* are the
+                * only ones that know that registration failed and that we shouldn't
+                * remove it.  Since we still hold a ref, we can change the release
+                * method to skip the device dereg. */
+               tap->kref.release = tap_min_release;
+       }
+       kref_put(&tap->kref);
+       return ret;
+out_with_lock:
+       spin_unlock(&fdt->lock);
+       kfree(tap);
+       return -1;
+}
+
+/* Removes the FD tap associated with FD.  Returns 0 on success, -1 with
+ * errno/errstr on failure. */
+int remove_fd_tap(struct proc *p, int fd)
+{
+       struct fd_table *fdt = &p->open_files;
+       struct fd_tap *tap;
+
+       spin_lock(&fdt->lock);
+       tap = fdt->fd[fd].fd_tap;
+       fdt->fd[fd].fd_tap = 0;
+       spin_unlock(&fdt->lock);
+       if (tap) {
+               kref_put(&tap->kref);
+               return 0;
+       } else {
+               set_errno(EBADF);
+               set_errstr("FD %d was not tapped", fd);
+               return -1;
+       }
+}
+
+/* Fires off tap, with the events of filter having occurred.  Returns -1 on
+ * error, though this need a little more thought.
+ *
+ * Some callers may require this to not block. */
+int fire_tap(struct fd_tap *tap, int filter)
+{
+       ERRSTACK(1);
+       struct event_msg ev_msg = {0};
+       int fire_filt = tap->filter & filter;
+
+       if (!fire_filt)
+               return 0;
+       if (waserror()) {
+               /* The process owning the tap could trigger a kernel PF, as with any
+                * send_event() call.  Eventually we'll catch that with waserror. */
+               warn("Tap for proc %d, fd %d, threw %s", tap->proc->pid, tap->fd,
+                    current_errstr());
+               poperror();
+               return -1;
+       }
+       ev_msg.ev_type = tap->ev_id;    /* e.g. CEQ idx */
+       ev_msg.ev_arg2 = fire_filt;             /* e.g. CEQ coalesce */
+       ev_msg.ev_arg3 = tap->data;             /* e.g. CEQ data */
+       send_event(tap->proc, tap->ev_q, &ev_msg, 0);
+       poperror();
+       return 0;
+}
index 9927357..0da01a2 100644 (file)
@@ -2331,6 +2331,40 @@ static intreg_t sys_dup_fds_to(struct proc *p, unsigned int pid,
        return ret;
 }
 
+/* 0 on success, anything else is an error, with errno/errstr set */
+static int handle_tap_req(struct proc *p, struct fd_tap_req *req)
+{
+       switch (req->cmd) {
+               case (FDTAP_CMD_ADD):
+                       return add_fd_tap(p, req);
+               case (FDTAP_CMD_REM):
+                       return remove_fd_tap(p, req->fd);
+               default:
+                       set_errno(ENOSYS);
+                       set_errstr("FD Tap Command %d not supported", req->cmd);
+                       return -1;
+       }
+}
+
+/* Processes up to nr_reqs tap requests.  If a request errors out, we stop
+ * immediately.  Returns the number processed.  If done != nr_reqs, check errno
+ * and errstr for the last failure, which is for tap_reqs[done]. */
+static intreg_t sys_tap_fds(struct proc *p, struct fd_tap_req *tap_reqs,
+                            size_t nr_reqs)
+{
+       struct fd_tap_req *req_i = tap_reqs;
+       int done;
+       if (!is_user_rwaddr(tap_reqs, sizeof(struct fd_tap_req) * nr_reqs)) {
+               set_errno(EINVAL);
+               return 0;
+       }
+       for (done = 0; done < nr_reqs; done++, req_i++) {
+               if (handle_tap_req(p, req_i))
+                       break;
+       }
+       return done;
+}
+
 /************** Syscall Invokation **************/
 
 const struct sys_table_entry syscall_table[] = {
@@ -2407,6 +2441,7 @@ const struct sys_table_entry syscall_table[] = {
        [SYS_fwstat] ={(syscall_t)sys_fwstat, "fwstat"},
        [SYS_rename] ={(syscall_t)sys_rename, "rename"},
        [SYS_dup_fds_to] = {(syscall_t)sys_dup_fds_to, "dup_fds_to"},
+       [SYS_tap_fds] = {(syscall_t)sys_tap_fds, "tap_fds"},
 };
 const int max_syscall = sizeof(syscall_table)/sizeof(syscall_table[0]);
 /* Executes the given syscall.
index 8be496c..649fc97 100644 (file)
@@ -17,6 +17,7 @@
 #include <umem.h>
 #include <smp.h>
 #include <ns.h>
+#include <fdtap.h>
 
 struct sb_tailq super_blocks = TAILQ_HEAD_INITIALIZER(super_blocks);
 spinlock_t super_blocks_lock = SPINLOCK_INITIALIZER;
@@ -2441,6 +2442,7 @@ bool close_fd(struct fd_table *fdt, int fd)
 {
        struct file *file = 0;
        struct chan *chan = 0;
+       struct fd_tap *tap = 0;
        bool ret = FALSE;
        if (fd < 0)
                return FALSE;
@@ -2452,8 +2454,10 @@ bool close_fd(struct fd_table *fdt, int fd)
                        assert(fd < fdt->max_files);
                        file = fdt->fd[fd].fd_file;
                        chan = fdt->fd[fd].fd_chan;
+                       tap = fdt->fd[fd].fd_tap;
                        fdt->fd[fd].fd_file = 0;
                        fdt->fd[fd].fd_chan = 0;
+                       fdt->fd[fd].fd_tap = 0;
                        CLR_BITMASK_BIT(fdt->open_fds->fds_bits, fd);
                        if (fd < fdt->hint_min_fd)
                                fdt->hint_min_fd = fd;
@@ -2466,6 +2470,8 @@ bool close_fd(struct fd_table *fdt, int fd)
                kref_put(&file->f_kref);
        else
                cclose(chan);
+       if (tap)
+               kref_put(&tap->kref);
        return ret;
 }
 
@@ -2589,6 +2595,8 @@ void close_fdt(struct fd_table *fdt, bool cloexec)
                                continue;
                        file = fdt->fd[i].fd_file;
                        chan = fdt->fd[i].fd_chan;
+                       to_close[idx].fd_tap = fdt->fd[i].fd_tap;
+                       fdt->fd[i].fd_tap = 0;
                        if (file) {
                                fdt->fd[i].fd_file = 0;
                                to_close[idx++].fd_file = file;
@@ -2614,6 +2622,8 @@ void close_fdt(struct fd_table *fdt, bool cloexec)
                        kref_put(&to_close[i].fd_file->f_kref);
                else
                        cclose(to_close[i].fd_chan);
+               if (to_close[i].fd_tap)
+                       kref_put(&to_close[i].fd_tap->kref);
        }
        kfree(to_close);
 }
index dee78e6..cbb53d4 100644 (file)
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <errno.h>
 #include <parlib/ros_debug.h>
+#include <ros/fdtap.h>
 
 __BEGIN_DECLS
 
@@ -55,6 +56,7 @@ int         sys_change_to_m(void);
 int         sys_poke_ksched(int pid, unsigned int res_type);
 int         sys_abort_sysc(struct syscall *sysc);
 int         sys_abort_sysc_fd(int fd);
+int         sys_tap_fds(struct fd_tap_req *tap_reqs, size_t nr_reqs);
 
 long           syscall_async(struct syscall *sysc, unsigned long num, ...);
 
index c645d9c..66ef567 100644 (file)
@@ -177,6 +177,11 @@ int sys_abort_sysc_fd(int fd)
        return ros_syscall(SYS_abort_sysc_fd, fd, 0, 0, 0, 0, 0);
 }
 
+int sys_tap_fds(struct fd_tap_req *tap_reqs, size_t nr_reqs)
+{
+       return ros_syscall(SYS_tap_fds, tap_reqs, nr_reqs, 0, 0, 0, 0);
+}
+
 long syscall_async(struct syscall *sysc, unsigned long num, ...)
 {
        va_list args;