qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Bandan Das <bsd@redhat.com>
To: qemu-devel@nongnu.org
Cc: Bandan Das <bsd@redhat.com>, kraxel@redhat.com
Subject: [Qemu-devel] [PATCH 2/3] usb-mtp: Add support for inotify based file monitoring
Date: Tue,  3 Nov 2015 19:00:24 -0500	[thread overview]
Message-ID: <1446595225-23608-3-git-send-email-bsd@redhat.com> (raw)
In-Reply-To: <1446595225-23608-1-git-send-email-bsd@redhat.com>

For now, we use inotify watches to track only a small number of
events, namely, add, delete and modify. Note that for delete, the kernel
already deactivates the watch for us and we just need to
take care of modifying our internal state.

Suggested-by: Gerd Hoffman <kraxel@redhat.com>
Signed-off-by: Bandan Das <bsd@redhat.com>
---
 hw/usb/dev-mtp.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 trace-events     |   3 +
 2 files changed, 251 insertions(+), 2 deletions(-)

diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 37dfa13..79d4ab0 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -15,9 +15,11 @@
 
 #include <sys/stat.h>
 #include <sys/statvfs.h>
+#include <sys/inotify.h>
 
 #include "qemu-common.h"
 #include "qemu/iov.h"
+#include "qemu/main-loop.h"
 #include "trace.h"
 #include "hw/usb.h"
 #include "hw/usb/desc.h"
@@ -77,6 +79,7 @@ typedef struct MTPState MTPState;
 typedef struct MTPControl MTPControl;
 typedef struct MTPData MTPData;
 typedef struct MTPObject MTPObject;
+typedef struct MTPMonEntry MTPMonEntry;
 
 enum {
     EP_DATA_IN = 1,
@@ -84,6 +87,19 @@ enum {
     EP_EVENT,
 };
 
+struct MTPMonEntry {
+    uint32_t event;
+    uint32_t handle;
+
+    QTAILQ_ENTRY(MTPMonEntry) next;
+};
+
+enum inotify_event_type {
+    CREATE = 1,
+    DELETE = 2,
+    MODIFY = 3,
+};
+
 struct MTPControl {
     uint16_t     code;
     uint32_t     trans;
@@ -108,6 +124,8 @@ struct MTPObject {
     char         *name;
     char         *path;
     struct stat  stat;
+    /* inotify watch cookie */
+    int          watchfd;
     MTPObject    *parent;
     uint32_t     nchildren;
     QLIST_HEAD(, MTPObject) children;
@@ -121,6 +139,8 @@ struct MTPState {
     char         *root;
     char         *desc;
     uint32_t     flags;
+    /* inotify descriptor */
+    int          inotifyfd;
 
     MTPData      *data_in;
     MTPData      *data_out;
@@ -129,6 +149,7 @@ struct MTPState {
     uint32_t     next_handle;
 
     QTAILQ_HEAD(, MTPObject) objects;
+    QTAILQ_HEAD(events, MTPMonEntry) events;
 };
 
 #define TYPE_USB_MTP "usb-mtp"
@@ -270,6 +291,40 @@ static const USBDesc desc = {
 };
 
 /* ----------------------------------------------------------------------- */
+static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
+                                             char *name, int len)
+{
+    MTPObject *iter;
+
+    QLIST_FOREACH(iter, &parent->children, list) {
+        if (strncmp(iter->name, name, len) == 0) {
+            return iter;
+        }
+    }
+
+    return NULL;
+}
+
+static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
+{
+    MTPObject *iter;
+
+    QTAILQ_FOREACH(iter, &s->objects, next) {
+        if (iter->watchfd == wd) {
+            return iter;
+        }
+    }
+
+    return NULL;
+}
+
+static int usb_mtp_add_watch(int inotifyfd, char *path)
+{
+    uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY |
+        IN_ISDIR;
+
+    return inotify_add_watch(inotifyfd, path, mask);
+}
 
 static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
                                        MTPObject *parent, char *name)
@@ -316,6 +371,46 @@ ignore:
     return NULL;
 }
 
+static void usb_mtp_inotify_cleanup(MTPState *s)
+{
+    MTPMonEntry *e;
+
+    if (!s->inotifyfd) {
+        return;
+    }
+
+    qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s);
+    close(s->inotifyfd);
+
+    QTAILQ_FOREACH(e, &s->events, next) {
+        QTAILQ_REMOVE(&s->events, e, next);
+        g_free(e);
+    }
+}
+
+static int usb_mtp_inotify_mon(MTPState *s, MTPObject *o)
+{
+    int watchfd;
+
+    if (!s->inotifyfd) {
+        return 0;
+    }
+    assert(o->format == FMT_ASSOCIATION);
+    if (o->watchfd > 0) {
+        /* already watching */
+        return 0;
+    }
+
+    watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
+    if (watchfd == -1) {
+        return 1;
+    }
+
+    o->watchfd = watchfd;
+    trace_usb_mtp_mon(s->dev.addr, o->path, watchfd);
+
+    return 0;
+}
 static void usb_mtp_object_free(MTPState *s, MTPObject *o)
 {
     MTPObject *iter;
@@ -370,6 +465,149 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
     return child;
 }
 
+static void inotify_watchfn(void *arg)
+{
+    MTPState *s = arg;
+    ssize_t bytes;
+    int error = 0;
+    /* From the man page: atleast one event can be read */
+    int len = sizeof(struct inotify_event) + NAME_MAX + 1;
+    char buf[len];
+
+    for (;;) {
+        char *p;
+        bytes = read(s->inotifyfd, buf, len);
+
+        if (bytes <= 0) {
+            /* Better luck next time */
+            goto done;
+        }
+
+        /*
+         * TODO: Ignore initiator initiated events.
+         * For now we are good because the store is RO
+         */
+        for (p = buf; p < buf + bytes;) {
+            struct inotify_event *event = (struct inotify_event *)p;
+            int watchfd = 0;
+            uint32_t mask = event->mask & (IN_CREATE | IN_DELETE |
+                                           IN_MODIFY | IN_IGNORED);
+            MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd);
+            MTPMonEntry *entry = NULL;
+            MTPObject *o;
+            char *name, *path;
+
+            /*
+             * TODO: Complain even on the slightest hint that
+             * something has gone wrong. Eventually, it makes
+             * sense to process remaining events, if any.
+             */
+            if (!parent) {
+                error = 1;
+                goto done;
+            }
+
+            switch (mask) {
+            case IN_CREATE:
+                if (event->mask & IN_ISDIR) {
+                    /* Add a new watch asap so as to not lose events */
+                    name = g_strndup(event->name, event->len);
+                    path = g_strdup_printf("%s/%s", parent->path, name);
+
+                    watchfd = usb_mtp_add_watch(s->inotifyfd, path);
+                    g_free(path);
+                    g_free(name);
+
+                    if (watchfd == -1) {
+                        error = 1;
+                        goto done;
+                    }
+                }
+
+                entry = g_new0(MTPMonEntry, 1);
+                entry->handle = s->next_handle;
+                entry->event = CREATE;
+                o = usb_mtp_add_child(s, parent, event->name);
+                if (!o) {
+                    error = 1;
+                    g_free(entry);
+                    goto done;
+                }
+                o->watchfd = watchfd;
+                trace_usb_mtp_inotify_event(s->dev.addr, path,
+                                      event->mask, "Obj Added");
+                break;
+
+            case IN_DELETE:
+                /*
+                 * The kernel issues a IN_IGNORED event
+                 * when a dir containing a watchpoint is
+                 * deleted
+                 */
+                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
+                if (!o) {
+                    error = 1;
+                    goto done;
+                }
+                entry = g_new0(MTPMonEntry, 1);
+                entry->handle = o->handle;
+                entry->event = DELETE;
+                usb_mtp_object_free(s, o);
+                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
+                                      event->mask, "Obj Deleted");
+                break;
+
+            case IN_MODIFY:
+                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
+                if (!o) {
+                    error = 1;
+                    goto done;
+                }
+                entry = g_new0(MTPMonEntry, 1);
+                entry->handle = o->handle;
+                entry->event = MODIFY;
+                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
+                                      event->mask, "Obj Modified");
+                break;
+
+            case IN_IGNORED:
+                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
+                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
+                                      event->mask, "Obj ignored");
+                break;
+
+            default:
+                error = 1;
+                goto done;
+            }
+
+            if (entry) {
+                QTAILQ_INSERT_HEAD(&s->events, entry, next);
+            }
+            p += sizeof(struct inotify_event) + event->len;
+        }
+    }
+done:
+    if (error) {
+        fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
+    }
+}
+
+static int usb_mtp_inotify_init(MTPState *s)
+{
+    int fd = inotify_init1(IN_NONBLOCK);
+    if (fd == -1) {
+        return 1;
+    }
+
+    QTAILQ_INIT(&s->events);
+    s->inotifyfd = fd;
+
+    qemu_set_fd_handler(fd, inotify_watchfn, NULL, s);
+
+    return 0;
+}
+
 static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
 {
     struct dirent *entry;
@@ -639,11 +877,11 @@ static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c,
 {
     MTPData *d = usb_mtp_data_alloc(c);
     uint32_t i = 0, handles[o->nchildren];
-    MTPObject *iter;
+    MTPObject *iter, *next;
 
     trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path);
 
-    QLIST_FOREACH(iter, &o->children, list) {
+    QLIST_FOREACH_SAFE(iter, &o->children, list, next) {
         handles[i++] = iter->handle;
     }
     assert(i == o->nchildren);
@@ -777,11 +1015,15 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
         trace_usb_mtp_op_open_session(s->dev.addr);
         s->session = c->argv[0];
         usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
+        if (usb_mtp_inotify_init(s)) {
+            fprintf(stderr, "usb-mtp: file monitoring init failed\n");
+        }
         break;
     case CMD_CLOSE_SESSION:
         trace_usb_mtp_op_close_session(s->dev.addr);
         s->session = 0;
         s->next_handle = 0;
+        usb_mtp_inotify_cleanup(s);
         usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
         assert(QTAILQ_EMPTY(&s->objects));
         break;
@@ -827,6 +1069,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
             return;
         }
         usb_mtp_object_readdir(s, o);
+        if (usb_mtp_inotify_mon(s, o)) {
+            fprintf(stderr, "usb-mtp: adding watch for %s failed\n", o->path);
+        }
         if (c->code == CMD_GET_NUM_OBJECTS) {
             trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path);
             nres = 1;
@@ -907,6 +1152,7 @@ static void usb_mtp_handle_reset(USBDevice *dev)
 
     trace_usb_mtp_reset(s->dev.addr);
 
+    usb_mtp_inotify_cleanup(s);
     usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
     s->session = 0;
     usb_mtp_data_free(s->data_in);
diff --git a/trace-events b/trace-events
index ba4473d..84c80fa 100644
--- a/trace-events
+++ b/trace-events
@@ -553,6 +553,9 @@ usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x"
 usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
 usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
 usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_inotify_mon(int dev, const char *path, int fd) "dev %d, path %s watchfd %d"
+usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s"
+usb_mtp_mon(int dev, const char *path, int watchfd) "dev %d path %s watchfd %d"
 
 # hw/usb/host-libusb.c
 usb_host_open_started(int bus, int addr) "dev %d:%d"
-- 
2.4.3

  parent reply	other threads:[~2015-11-04  0:01 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-04  0:00 [Qemu-devel] [PATCH 0/3] usb-mtp events support Bandan Das
2015-11-04  0:00 ` [Qemu-devel] [PATCH 1/3] usb-mtp: use a list for keeping track of children Bandan Das
2015-11-05  8:24   ` Gerd Hoffmann
2015-11-05 21:23     ` Bandan Das
2015-11-04  0:00 ` Bandan Das [this message]
2015-11-05  8:37   ` [Qemu-devel] [PATCH 2/3] usb-mtp: Add support for inotify based file monitoring Gerd Hoffmann
2015-11-05 21:28     ` Bandan Das
2015-11-05  8:49   ` Gerd Hoffmann
2015-11-09 23:12     ` Bandan Das
2015-11-09 23:28       ` Bandan Das
2015-11-12  8:16       ` Gerd Hoffmann
2015-11-12 22:40         ` Bandan Das
2015-11-13  8:08           ` Gerd Hoffmann
2015-11-05  8:49   ` Gerd Hoffmann
2015-11-09 23:13     ` Bandan Das
2015-11-04  0:00 ` [Qemu-devel] [PATCH 3/3] usb-mtp: add support for basic mtp events Bandan Das
2015-11-05  8:41   ` Gerd Hoffmann
2015-11-05 21:30     ` Bandan Das

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1446595225-23608-3-git-send-email-bsd@redhat.com \
    --to=bsd@redhat.com \
    --cc=kraxel@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).