All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] 9pfs: Twalk benchmark
  2021-05-27 17:13 [PATCH 0/3] 9pfs: Twalk optimization Christian Schoenebeck
@ 2021-05-27 17:03 ` Christian Schoenebeck
  2021-05-27 17:04 ` [PATCH 2/3] 9pfs: capture root stat Christian Schoenebeck
  2021-05-27 17:05 ` [PATCH 3/3] 9pfs: reduce latency of Twalk Christian Schoenebeck
  2 siblings, 0 replies; 7+ messages in thread
From: Christian Schoenebeck @ 2021-05-27 17:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: Greg Kurz

This patch is not intentended to be merged, it just acts as performance
A/B comparison benchmark for the subsequent patch.

Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
 tests/qtest/virtio-9p-test.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 41fed41de1..2cd9e427b4 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -20,6 +20,18 @@
 #include "libqos/virtio-9p.h"
 #include "libqos/qgraph.h"
 
+/*
+ * to benchmark the real time (not CPU time) that elapsed between start of
+ * a request and arrival of its response
+ */
+static double wall_time(void)
+{
+    struct timeval t;
+    struct timezone tz;
+    gettimeofday(&t, &tz);
+    return t.tv_sec + t.tv_usec * 0.000001;
+}
+
 #define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
 static QGuestAllocator *alloc;
 
@@ -646,12 +658,30 @@ static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc)
     }
 
     do_attach(v9p);
+    const double start = wall_time();
     req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0);
+    const double twalk = wall_time();
     v9fs_req_wait_for_reply(req, NULL);
+    const double waitforreply = wall_time();
     v9fs_rwalk(req, &nwqid, &wqid);
+    const double end = wall_time();
 
     g_assert_cmpint(nwqid, ==, P9_MAXWELEM);
 
+    printf("\nTime client spent on sending Twalk: %fs\n\n",
+           twalk - start);
+
+    printf("Time client spent for waiting for reply from server: %fs "
+           "[MOST IMPORTANT]\n", waitforreply - start);
+    printf("(This is the most important value, because it reflects the time\n"
+           "the 9p server required to process and return the result of the\n"
+           "Twalk request.)\n\n");
+
+    printf("Total client time: %fs\n", end - start);
+
+    //printf("Details of response message data: R_readddir nentries=%d "
+    //       "rbytes=%d\n", nentries, count);
+
     for (i = 0; i < P9_MAXWELEM; i++) {
         g_free(wnames[i]);
     }
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/3] 9pfs: capture root stat
  2021-05-27 17:13 [PATCH 0/3] 9pfs: Twalk optimization Christian Schoenebeck
  2021-05-27 17:03 ` [PATCH 1/3] 9pfs: Twalk benchmark Christian Schoenebeck
@ 2021-05-27 17:04 ` Christian Schoenebeck
  2021-06-04 13:45   ` Christian Schoenebeck
  2021-05-27 17:05 ` [PATCH 3/3] 9pfs: reduce latency of Twalk Christian Schoenebeck
  2 siblings, 1 reply; 7+ messages in thread
From: Christian Schoenebeck @ 2021-05-27 17:04 UTC (permalink / raw)
  To: qemu-devel; +Cc: Greg Kurz

We already capture the QID of the exported 9p root path, i.e. to
prevent client access outside the defined, exported filesystem's tree.
This is currently checked by comparing the root QID with another FID's
QID.

The problem with the latter is that resolving a QID of any given 9p path
can only be done on 9p server's main thread, that's because it might
mutate the server's state if inode remapping is enabled.

For that reason also capture the POSIX stat info of the root path for
being able to identify on any (e.g. worker) thread whether an
arbitrary given path is identical to the export root.

Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
 hw/9pfs/9p.c | 10 +++++++++-
 hw/9pfs/9p.h |  1 +
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 89aa07db78..825de1561d 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -1400,6 +1400,7 @@ static void coroutine_fn v9fs_attach(void *opaque)
     size_t offset = 7;
     V9fsQID qid;
     ssize_t err;
+    struct stat stbuf;
 
     v9fs_string_init(&uname);
     v9fs_string_init(&aname);
@@ -1422,7 +1423,13 @@ static void coroutine_fn v9fs_attach(void *opaque)
         clunk_fid(s, fid);
         goto out;
     }
-    err = fid_to_qid(pdu, fidp, &qid);
+    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
+    if (err < 0) {
+        err = -EINVAL;
+        clunk_fid(s, fid);
+        goto out;
+    }
+    err = stat_to_qid(pdu, &stbuf, &qid);
     if (err < 0) {
         err = -EINVAL;
         clunk_fid(s, fid);
@@ -1455,6 +1462,7 @@ static void coroutine_fn v9fs_attach(void *opaque)
     err += offset;
 
     memcpy(&s->root_qid, &qid, sizeof(qid));
+    memcpy(&s->root_st, &stbuf, sizeof(struct stat));
     trace_v9fs_attach_return(pdu->tag, pdu->id,
                              qid.type, qid.version, qid.path);
 out:
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 00381591ff..6f0b4c78c0 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -356,6 +356,7 @@ struct V9fsState {
     Error *migration_blocker;
     V9fsConf fsconf;
     V9fsQID root_qid;
+    struct stat root_st;
     dev_t dev_id;
     struct qht qpd_table;
     struct qht qpp_table;
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/3] 9pfs: reduce latency of Twalk
  2021-05-27 17:13 [PATCH 0/3] 9pfs: Twalk optimization Christian Schoenebeck
  2021-05-27 17:03 ` [PATCH 1/3] 9pfs: Twalk benchmark Christian Schoenebeck
  2021-05-27 17:04 ` [PATCH 2/3] 9pfs: capture root stat Christian Schoenebeck
@ 2021-05-27 17:05 ` Christian Schoenebeck
  2021-05-27 18:24   ` Christian Schoenebeck
  2 siblings, 1 reply; 7+ messages in thread
From: Christian Schoenebeck @ 2021-05-27 17:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: Greg Kurz

As on the previous performance optimization on Treaddir handling;
reduce the overall latency, i.e. overall time spent on processing
a Twalk request by reducing the amount of thread hops between the
9p server's main thread and fs worker thread(s).

In fact this patch even reduces the thread hops for Twalk handling
to its theoritical minimum of exactly 2 thread hops:

main thread -> fs worker thread -> main thread

This is achieved by doing all the required fs driver tasks altogether
in a single v9fs_co_run_in_worker({ ... }); code block.

This patches also changes the way how an arbitrary path is
identified to whether it equals the 9p export root. Previously
QIDs were compared for this, which forces to be done on main thread
for resolving individual path element QIDs. For that reason POSIX
stat device number and inode number pairs are compared instead now.
Accordingly, as 9p server's root_qid member variable is no longer
used, nor are functions fid_to_qid() and not_same_qid(), hence drop
them.

Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
 hw/9pfs/9p.c | 118 +++++++++++++++++++++++++++++++++------------------
 hw/9pfs/9p.h |   1 -
 2 files changed, 76 insertions(+), 43 deletions(-)

diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 825de1561d..cc1b176eb5 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -971,23 +971,6 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, V9fsQID *qidp)
     return 0;
 }
 
-static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
-                                   V9fsQID *qidp)
-{
-    struct stat stbuf;
-    int err;
-
-    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
-    if (err < 0) {
-        return err;
-    }
-    err = stat_to_qid(pdu, &stbuf, qidp);
-    if (err < 0) {
-        return err;
-    }
-    return 0;
-}
-
 V9fsPDU *pdu_alloc(V9fsState *s)
 {
     V9fsPDU *pdu = NULL;
@@ -1461,7 +1444,6 @@ static void coroutine_fn v9fs_attach(void *opaque)
     }
     err += offset;
 
-    memcpy(&s->root_qid, &qid, sizeof(qid));
     memcpy(&s->root_st, &stbuf, sizeof(struct stat));
     trace_v9fs_attach_return(pdu->tag, pdu->id,
                              qid.type, qid.version, qid.path);
@@ -1713,12 +1695,9 @@ static bool name_is_illegal(const char *name)
     return !*name || strchr(name, '/') != NULL;
 }
 
-static bool not_same_qid(const V9fsQID *qid1, const V9fsQID *qid2)
+static bool same_stat_id(const struct stat *a, const struct stat *b)
 {
-    return
-        qid1->type != qid2->type ||
-        qid1->version != qid2->version ||
-        qid1->path != qid2->path;
+    return a->st_dev == b->st_dev && a->st_ino == b->st_ino;
 }
 
 static void coroutine_fn v9fs_walk(void *opaque)
@@ -1726,9 +1705,9 @@ static void coroutine_fn v9fs_walk(void *opaque)
     int name_idx;
     V9fsQID *qids = NULL;
     int i, err = 0;
-    V9fsPath dpath, path;
+    V9fsPath dpath, path, *pathes = NULL;
     uint16_t nwnames;
-    struct stat stbuf;
+    struct stat stbuf, fidst, *stbufs = NULL;
     size_t offset = 7;
     int32_t fid, newfid;
     V9fsString *wnames = NULL;
@@ -1754,6 +1733,8 @@ static void coroutine_fn v9fs_walk(void *opaque)
     if (nwnames) {
         wnames = g_new0(V9fsString, nwnames);
         qids   = g_new0(V9fsQID, nwnames);
+        stbufs = g_new0(struct stat, nwnames);
+        pathes = g_new0(V9fsPath, nwnames);
         for (i = 0; i < nwnames; i++) {
             err = pdu_unmarshal(pdu, offset, "s", &wnames[i]);
             if (err < 0) {
@@ -1774,35 +1755,85 @@ static void coroutine_fn v9fs_walk(void *opaque)
 
     v9fs_path_init(&dpath);
     v9fs_path_init(&path);
+    /*
+     * Both dpath and path initially point to fidp.
+     * Needed to handle request with nwnames == 0
+     */
+    v9fs_path_copy(&dpath, &fidp->path);
+    v9fs_path_copy(&path, &fidp->path);
 
-    err = fid_to_qid(pdu, fidp, &qid);
+    /*
+     * To keep latency (i.e. overall execution time for processing this
+     * Twalk client request) as small as possible, run all the required fs
+     * driver code altogether inside the following block.
+     */
+    v9fs_co_run_in_worker({
+        if (v9fs_request_cancelled(pdu)) {
+            err = -EINTR;
+            break;
+        }
+        err = s->ops->lstat(&s->ctx, &dpath, &fidst);
+        if (err < 0) {
+            err = -errno;
+            break;
+        }
+        stbuf = fidst;
+        for (name_idx = 0; name_idx < nwnames; name_idx++) {
+            if (v9fs_request_cancelled(pdu)) {
+                err = -EINTR;
+                break;
+            }
+            if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
+                strcmp("..", wnames[name_idx].data))
+            {
+                err = s->ops->name_to_path(&s->ctx, &dpath,
+                                        wnames[name_idx].data, &path);
+                if (err < 0) {
+                    err = -errno;
+                    break;
+                }
+                if (v9fs_request_cancelled(pdu)) {
+                    err = -EINTR;
+                    break;
+                }
+                err = s->ops->lstat(&s->ctx, &path, &stbuf);
+                if (err < 0) {
+                    err = -errno;
+                    break;
+                }
+                stbufs[name_idx] = stbuf;
+                v9fs_path_copy(&dpath, &path);
+                v9fs_path_copy(&pathes[name_idx], &path);
+            }
+        }
+    });
+    /*
+     * Handle all the rest of this Twalk request on main thread ...
+     */
     if (err < 0) {
         goto out;
     }
 
-    /*
-     * Both dpath and path initially poin to fidp.
-     * Needed to handle request with nwnames == 0
-     */
+    err = stat_to_qid(pdu, &fidst, &qid);
+    if (err < 0) {
+        goto out;
+    }
+    stbuf = fidst;
+
+    /* reset dpath and path */
     v9fs_path_copy(&dpath, &fidp->path);
     v9fs_path_copy(&path, &fidp->path);
-    for (name_idx = 0; name_idx < nwnames; name_idx++) {
-        if (not_same_qid(&pdu->s->root_qid, &qid) ||
-            strcmp("..", wnames[name_idx].data)) {
-            err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data,
-                                       &path);
-            if (err < 0) {
-                goto out;
-            }
 
-            err = v9fs_co_lstat(pdu, &path, &stbuf);
-            if (err < 0) {
-                goto out;
-            }
+    for (name_idx = 0; name_idx < nwnames; name_idx++) {
+        if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
+            strcmp("..", wnames[name_idx].data))
+        {
+            stbuf = stbufs[name_idx];
             err = stat_to_qid(pdu, &stbuf, &qid);
             if (err < 0) {
                 goto out;
             }
+            v9fs_path_copy(&path, &pathes[name_idx]);
             v9fs_path_copy(&dpath, &path);
         }
         memcpy(&qids[name_idx], &qid, sizeof(qid));
@@ -1838,9 +1869,12 @@ out_nofid:
     if (nwnames && nwnames <= P9_MAXWELEM) {
         for (name_idx = 0; name_idx < nwnames; name_idx++) {
             v9fs_string_free(&wnames[name_idx]);
+            v9fs_path_free(&pathes[name_idx]);
         }
         g_free(wnames);
         g_free(qids);
+        g_free(stbufs);
+        g_free(pathes);
     }
 }
 
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 6f0b4c78c0..1567b67841 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -355,7 +355,6 @@ struct V9fsState {
     int32_t root_fid;
     Error *migration_blocker;
     V9fsConf fsconf;
-    V9fsQID root_qid;
     struct stat root_st;
     dev_t dev_id;
     struct qht qpd_table;
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 0/3] 9pfs: Twalk optimization
@ 2021-05-27 17:13 Christian Schoenebeck
  2021-05-27 17:03 ` [PATCH 1/3] 9pfs: Twalk benchmark Christian Schoenebeck
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Christian Schoenebeck @ 2021-05-27 17:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Greg Kurz

First draft for reducing latency of Twalk handling by reducing the amount
of thread hops, similar to previous Treaddir optimization. The performance
gain is not as spectacular as on Treaddir, but there is definitely a
measurable difference.

With the benchmark of patch 1, the runtime of the Twalk test was cut in half.
In real world tests I measured a performance gain (i.e. running an entire
guest OS, and hence mixed with all othe kinds of 9p requests) of about 2%,
again measured in a mix, not concentrated on Treaddir at all.

Independent of the actual performance optimization (patch 3), there are some
things about Twalk handling in general which I am yet unsure about. So I'll
add some of my thoughts as reply to patch 3, and depending on that I might
still cleanup / reduce some of the code.

Christian Schoenebeck (3):
  9pfs: Twalk benchmark
  9pfs: capture root stat
  9pfs: reduce latency of Twalk

 hw/9pfs/9p.c                 | 128 +++++++++++++++++++++++------------
 hw/9pfs/9p.h                 |   2 +-
 tests/qtest/virtio-9p-test.c |  30 ++++++++
 3 files changed, 116 insertions(+), 44 deletions(-)

-- 
2.20.1



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 3/3] 9pfs: reduce latency of Twalk
  2021-05-27 17:05 ` [PATCH 3/3] 9pfs: reduce latency of Twalk Christian Schoenebeck
@ 2021-05-27 18:24   ` Christian Schoenebeck
  2021-06-04 12:14     ` Christian Schoenebeck
  0 siblings, 1 reply; 7+ messages in thread
From: Christian Schoenebeck @ 2021-05-27 18:24 UTC (permalink / raw)
  To: qemu-devel; +Cc: Greg Kurz

On Donnerstag, 27. Mai 2021 19:05:43 CEST Christian Schoenebeck wrote:
> As on the previous performance optimization on Treaddir handling;
> reduce the overall latency, i.e. overall time spent on processing
> a Twalk request by reducing the amount of thread hops between the
> 9p server's main thread and fs worker thread(s).
> 
> In fact this patch even reduces the thread hops for Twalk handling
> to its theoritical minimum of exactly 2 thread hops:
> 
> main thread -> fs worker thread -> main thread
> 
> This is achieved by doing all the required fs driver tasks altogether
> in a single v9fs_co_run_in_worker({ ... }); code block.
> 
> This patches also changes the way how an arbitrary path is
> identified to whether it equals the 9p export root. Previously
> QIDs were compared for this, which forces to be done on main thread
> for resolving individual path element QIDs. For that reason POSIX
> stat device number and inode number pairs are compared instead now.
> Accordingly, as 9p server's root_qid member variable is no longer
> used, nor are functions fid_to_qid() and not_same_qid(), hence drop
> them.
> 
> Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
> ---
>  hw/9pfs/9p.c | 118 +++++++++++++++++++++++++++++++++------------------
>  hw/9pfs/9p.h |   1 -
>  2 files changed, 76 insertions(+), 43 deletions(-)
> 
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index 825de1561d..cc1b176eb5 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -971,23 +971,6 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat
> *stbuf, V9fsQID *qidp) return 0;
>  }
> 
> -static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
> -                                   V9fsQID *qidp)
> -{
> -    struct stat stbuf;
> -    int err;
> -
> -    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
> -    if (err < 0) {
> -        return err;
> -    }
> -    err = stat_to_qid(pdu, &stbuf, qidp);
> -    if (err < 0) {
> -        return err;
> -    }
> -    return 0;
> -}
> -
>  V9fsPDU *pdu_alloc(V9fsState *s)
>  {
>      V9fsPDU *pdu = NULL;
> @@ -1461,7 +1444,6 @@ static void coroutine_fn v9fs_attach(void *opaque)
>      }
>      err += offset;
> 
> -    memcpy(&s->root_qid, &qid, sizeof(qid));
>      memcpy(&s->root_st, &stbuf, sizeof(struct stat));
>      trace_v9fs_attach_return(pdu->tag, pdu->id,
>                               qid.type, qid.version, qid.path);
> @@ -1713,12 +1695,9 @@ static bool name_is_illegal(const char *name)
>      return !*name || strchr(name, '/') != NULL;
>  }
> 
> -static bool not_same_qid(const V9fsQID *qid1, const V9fsQID *qid2)
> +static bool same_stat_id(const struct stat *a, const struct stat *b)
>  {
> -    return
> -        qid1->type != qid2->type ||
> -        qid1->version != qid2->version ||
> -        qid1->path != qid2->path;
> +    return a->st_dev == b->st_dev && a->st_ino == b->st_ino;
>  }
> 
>  static void coroutine_fn v9fs_walk(void *opaque)
> @@ -1726,9 +1705,9 @@ static void coroutine_fn v9fs_walk(void *opaque)
>      int name_idx;
>      V9fsQID *qids = NULL;
>      int i, err = 0;
> -    V9fsPath dpath, path;
> +    V9fsPath dpath, path, *pathes = NULL;
>      uint16_t nwnames;
> -    struct stat stbuf;
> +    struct stat stbuf, fidst, *stbufs = NULL;
>      size_t offset = 7;
>      int32_t fid, newfid;
>      V9fsString *wnames = NULL;
> @@ -1754,6 +1733,8 @@ static void coroutine_fn v9fs_walk(void *opaque)
>      if (nwnames) {
>          wnames = g_new0(V9fsString, nwnames);
>          qids   = g_new0(V9fsQID, nwnames);
> +        stbufs = g_new0(struct stat, nwnames);
> +        pathes = g_new0(V9fsPath, nwnames);
>          for (i = 0; i < nwnames; i++) {
>              err = pdu_unmarshal(pdu, offset, "s", &wnames[i]);
>              if (err < 0) {
> @@ -1774,35 +1755,85 @@ static void coroutine_fn v9fs_walk(void *opaque)
> 
>      v9fs_path_init(&dpath);
>      v9fs_path_init(&path);
> +    /*
> +     * Both dpath and path initially point to fidp.
> +     * Needed to handle request with nwnames == 0
> +     */
> +    v9fs_path_copy(&dpath, &fidp->path);
> +    v9fs_path_copy(&path, &fidp->path);
> 
> -    err = fid_to_qid(pdu, fidp, &qid);
> +    /*
> +     * To keep latency (i.e. overall execution time for processing this
> +     * Twalk client request) as small as possible, run all the required fs
> +     * driver code altogether inside the following block.
> +     */
> +    v9fs_co_run_in_worker({
> +        if (v9fs_request_cancelled(pdu)) {
> +            err = -EINTR;
> +            break;
> +        }
> +        err = s->ops->lstat(&s->ctx, &dpath, &fidst);
> +        if (err < 0) {
> +            err = -errno;
> +            break;
> +        }
> +        stbuf = fidst;
> +        for (name_idx = 0; name_idx < nwnames; name_idx++) {
> +            if (v9fs_request_cancelled(pdu)) {
> +                err = -EINTR;
> +                break;
> +            }
> +            if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
> +                strcmp("..", wnames[name_idx].data))
> +            {
> +                err = s->ops->name_to_path(&s->ctx, &dpath,
> +                                        wnames[name_idx].data, &path);
> +                if (err < 0) {
> +                    err = -errno;
> +                    break;
> +                }
> +                if (v9fs_request_cancelled(pdu)) {
> +                    err = -EINTR;
> +                    break;
> +                }
> +                err = s->ops->lstat(&s->ctx, &path, &stbuf);
> +                if (err < 0) {
> +                    err = -errno;
> +                    break;
> +                }
> +                stbufs[name_idx] = stbuf;
> +                v9fs_path_copy(&dpath, &path);
> +                v9fs_path_copy(&pathes[name_idx], &path);
> +            }
> +        }
> +    });
> +    /*
> +     * Handle all the rest of this Twalk request on main thread ...
> +     */
>      if (err < 0) {
>          goto out;
>      }

Depending on my last question below, this check might be wrong, i.e. according 
to the specs this should only error out if the first element failed.

> 
> -    /*
> -     * Both dpath and path initially poin to fidp.
> -     * Needed to handle request with nwnames == 0
> -     */
> +    err = stat_to_qid(pdu, &fidst, &qid);
> +    if (err < 0) {
> +        goto out;
> +    }
> +    stbuf = fidst;
> +
> +    /* reset dpath and path */
>      v9fs_path_copy(&dpath, &fidp->path);
>      v9fs_path_copy(&path, &fidp->path);

I am not sure what the expectation of a potential mutation of fid is here. 
Right now this code (as the previous one) assumes that fid does not mutate 
during entire Twalk request handling. But the same issue actually applies to 
any 9p request handling, not just Twalk.

> -    for (name_idx = 0; name_idx < nwnames; name_idx++) {
> -        if (not_same_qid(&pdu->s->root_qid, &qid) ||
> -            strcmp("..", wnames[name_idx].data)) {
> -            err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data,
> -                                       &path);
> -            if (err < 0) {
> -                goto out;
> -            }
> 
> -            err = v9fs_co_lstat(pdu, &path, &stbuf);
> -            if (err < 0) {
> -                goto out;
> -            }
> +    for (name_idx = 0; name_idx < nwnames; name_idx++) {
> +        if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
> +            strcmp("..", wnames[name_idx].data))
> +        {
> +            stbuf = stbufs[name_idx];
>              err = stat_to_qid(pdu, &stbuf, &qid);
>              if (err < 0) {
>                  goto out;
>              }
> +            v9fs_path_copy(&path, &pathes[name_idx]);
>              v9fs_path_copy(&dpath, &path);
>          }
>          memcpy(&qids[name_idx], &qid, sizeof(qid));

So far, replicating just the previous Twalk behaviour here (yet). Right now 
the request returns Rerror if any of the transmitted path elements could not 
be walked. Looking at the specs though, this seems to be wrong:

"If the first element cannot be walked for any reason, Rerror is returned. 
Otherwise, the walk will return an Rwalk mes- sage containing nwqid qids 
corresponding, in order, to the files that are visited by the nwqid successful 
elementwise walks; nwqid is therefore either nwname or the index of the first 
elementwise walk that failed. The value of nwqid can- not be zero unless 
nwname is zero. Also, nwqid will always be less than or equal to nwname. Only 
if it is equal, how- ever, will newfid be affected, in which case newfid will 
represent the file reached by the final elementwise walk requested in the 
message."

http://ericvh.github.io/9p-rfc/rfc9p2000.html#anchor33

> @@ -1838,9 +1869,12 @@ out_nofid:
>      if (nwnames && nwnames <= P9_MAXWELEM) {
>          for (name_idx = 0; name_idx < nwnames; name_idx++) {
>              v9fs_string_free(&wnames[name_idx]);
> +            v9fs_path_free(&pathes[name_idx]);
>          }
>          g_free(wnames);
>          g_free(qids);
> +        g_free(stbufs);
> +        g_free(pathes);
>      }
>  }
> 
> diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
> index 6f0b4c78c0..1567b67841 100644
> --- a/hw/9pfs/9p.h
> +++ b/hw/9pfs/9p.h
> @@ -355,7 +355,6 @@ struct V9fsState {
>      int32_t root_fid;
>      Error *migration_blocker;
>      V9fsConf fsconf;
> -    V9fsQID root_qid;
>      struct stat root_st;
>      dev_t dev_id;
>      struct qht qpd_table;

Best regards,
Christian Schoenebeck




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 3/3] 9pfs: reduce latency of Twalk
  2021-05-27 18:24   ` Christian Schoenebeck
@ 2021-06-04 12:14     ` Christian Schoenebeck
  0 siblings, 0 replies; 7+ messages in thread
From: Christian Schoenebeck @ 2021-06-04 12:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: Greg Kurz

On Donnerstag, 27. Mai 2021 20:24:23 CEST Christian Schoenebeck wrote:
> On Donnerstag, 27. Mai 2021 19:05:43 CEST Christian Schoenebeck wrote:
> > As on the previous performance optimization on Treaddir handling;
> > reduce the overall latency, i.e. overall time spent on processing
> > a Twalk request by reducing the amount of thread hops between the
> > 9p server's main thread and fs worker thread(s).
> > 
> > In fact this patch even reduces the thread hops for Twalk handling
> > to its theoritical minimum of exactly 2 thread hops:
> > 
> > main thread -> fs worker thread -> main thread
> > 
> > This is achieved by doing all the required fs driver tasks altogether
> > in a single v9fs_co_run_in_worker({ ... }); code block.
> > 
> > This patches also changes the way how an arbitrary path is
> > identified to whether it equals the 9p export root. Previously
> > QIDs were compared for this, which forces to be done on main thread
> > for resolving individual path element QIDs. For that reason POSIX
> > stat device number and inode number pairs are compared instead now.
> > Accordingly, as 9p server's root_qid member variable is no longer
> > used, nor are functions fid_to_qid() and not_same_qid(), hence drop
> > them.
> > 
> > Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > ---
> > 
> >  hw/9pfs/9p.c | 118 +++++++++++++++++++++++++++++++++------------------
> >  hw/9pfs/9p.h |   1 -
> >  2 files changed, 76 insertions(+), 43 deletions(-)
> > 
> > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> > index 825de1561d..cc1b176eb5 100644
> > --- a/hw/9pfs/9p.c
> > +++ b/hw/9pfs/9p.c
> > @@ -971,23 +971,6 @@ static int stat_to_qid(V9fsPDU *pdu, const struct
> > stat
> > *stbuf, V9fsQID *qidp) return 0;
> > 
> >  }
> > 
> > -static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
> > -                                   V9fsQID *qidp)
> > -{
> > -    struct stat stbuf;
> > -    int err;
> > -
> > -    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
> > -    if (err < 0) {
> > -        return err;
> > -    }
> > -    err = stat_to_qid(pdu, &stbuf, qidp);
> > -    if (err < 0) {
> > -        return err;
> > -    }
> > -    return 0;
> > -}
> > -
> > 
> >  V9fsPDU *pdu_alloc(V9fsState *s)
> >  {
> >  
> >      V9fsPDU *pdu = NULL;
> > 
> > @@ -1461,7 +1444,6 @@ static void coroutine_fn v9fs_attach(void *opaque)
> > 
> >      }
> >      err += offset;
> > 
> > -    memcpy(&s->root_qid, &qid, sizeof(qid));
> > 
> >      memcpy(&s->root_st, &stbuf, sizeof(struct stat));
> >      trace_v9fs_attach_return(pdu->tag, pdu->id,
> >      
> >                               qid.type, qid.version, qid.path);
> > 
> > @@ -1713,12 +1695,9 @@ static bool name_is_illegal(const char *name)
> > 
> >      return !*name || strchr(name, '/') != NULL;
> >  
> >  }
> > 
> > -static bool not_same_qid(const V9fsQID *qid1, const V9fsQID *qid2)
> > +static bool same_stat_id(const struct stat *a, const struct stat *b)
> > 
> >  {
> > 
> > -    return
> > -        qid1->type != qid2->type ||
> > -        qid1->version != qid2->version ||
> > -        qid1->path != qid2->path;
> > +    return a->st_dev == b->st_dev && a->st_ino == b->st_ino;
> > 
> >  }

I forgot: this ^ can be split out to a separate, preparatory patch.

> >  
> >  static void coroutine_fn v9fs_walk(void *opaque)
> > 
> > @@ -1726,9 +1705,9 @@ static void coroutine_fn v9fs_walk(void *opaque)
> > 
> >      int name_idx;
> >      V9fsQID *qids = NULL;
> >      int i, err = 0;
> > 
> > -    V9fsPath dpath, path;
> > +    V9fsPath dpath, path, *pathes = NULL;
> > 
> >      uint16_t nwnames;
> > 
> > -    struct stat stbuf;
> > +    struct stat stbuf, fidst, *stbufs = NULL;
> > 
> >      size_t offset = 7;
> >      int32_t fid, newfid;
> >      V9fsString *wnames = NULL;
> > 
> > @@ -1754,6 +1733,8 @@ static void coroutine_fn v9fs_walk(void *opaque)
> > 
> >      if (nwnames) {
> >      
> >          wnames = g_new0(V9fsString, nwnames);
> >          qids   = g_new0(V9fsQID, nwnames);
> > 
> > +        stbufs = g_new0(struct stat, nwnames);
> > +        pathes = g_new0(V9fsPath, nwnames);
> > 
> >          for (i = 0; i < nwnames; i++) {
> >          
> >              err = pdu_unmarshal(pdu, offset, "s", &wnames[i]);
> >              if (err < 0) {
> > 
> > @@ -1774,35 +1755,85 @@ static void coroutine_fn v9fs_walk(void *opaque)
> > 
> >      v9fs_path_init(&dpath);
> >      v9fs_path_init(&path);
> > 
> > +    /*
> > +     * Both dpath and path initially point to fidp.
> > +     * Needed to handle request with nwnames == 0
> > +     */
> > +    v9fs_path_copy(&dpath, &fidp->path);
> > +    v9fs_path_copy(&path, &fidp->path);
> > 
> > -    err = fid_to_qid(pdu, fidp, &qid);
> > +    /*
> > +     * To keep latency (i.e. overall execution time for processing this
> > +     * Twalk client request) as small as possible, run all the required
> > fs
> > +     * driver code altogether inside the following block.
> > +     */
> > +    v9fs_co_run_in_worker({
> > +        if (v9fs_request_cancelled(pdu)) {
> > +            err = -EINTR;
> > +            break;
> > +        }
> > +        err = s->ops->lstat(&s->ctx, &dpath, &fidst);
> > +        if (err < 0) {
> > +            err = -errno;
> > +            break;
> > +        }
> > +        stbuf = fidst;
> > +        for (name_idx = 0; name_idx < nwnames; name_idx++) {
> > +            if (v9fs_request_cancelled(pdu)) {
> > +                err = -EINTR;
> > +                break;
> > +            }
> > +            if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
> > +                strcmp("..", wnames[name_idx].data))
> > +            {
> > +                err = s->ops->name_to_path(&s->ctx, &dpath,
> > +                                        wnames[name_idx].data, &path);
> > +                if (err < 0) {
> > +                    err = -errno;
> > +                    break;
> > +                }
> > +                if (v9fs_request_cancelled(pdu)) {
> > +                    err = -EINTR;
> > +                    break;
> > +                }
> > +                err = s->ops->lstat(&s->ctx, &path, &stbuf);
> > +                if (err < 0) {
> > +                    err = -errno;
> > +                    break;
> > +                }
> > +                stbufs[name_idx] = stbuf;
> > +                v9fs_path_copy(&dpath, &path);
> > +                v9fs_path_copy(&pathes[name_idx], &path);
> > +            }
> > +        }
> > +    });
> > +    /*
> > +     * Handle all the rest of this Twalk request on main thread ...
> > +     */
> > 
> >      if (err < 0) {
> >      
> >          goto out;
> >      
> >      }
> 
> Depending on my last question below, this check might be wrong, i.e.
> according to the specs this should only error out if the first element
> failed.
> > -    /*
> > -     * Both dpath and path initially poin to fidp.
> > -     * Needed to handle request with nwnames == 0
> > -     */
> > +    err = stat_to_qid(pdu, &fidst, &qid);
> > +    if (err < 0) {
> > +        goto out;
> > +    }
> > +    stbuf = fidst;
> > +
> > +    /* reset dpath and path */
> > 
> >      v9fs_path_copy(&dpath, &fidp->path);
> >      v9fs_path_copy(&path, &fidp->path);
> 
> I am not sure what the expectation of a potential mutation of fid is here.
> Right now this code (as the previous one) assumes that fid does not mutate
> during entire Twalk request handling. But the same issue actually applies to
> any 9p request handling, not just Twalk.
> 
> > -    for (name_idx = 0; name_idx < nwnames; name_idx++) {
> > -        if (not_same_qid(&pdu->s->root_qid, &qid) ||
> > -            strcmp("..", wnames[name_idx].data)) {
> > -            err = v9fs_co_name_to_path(pdu, &dpath,
> > wnames[name_idx].data,
> > -                                       &path);
> > -            if (err < 0) {
> > -                goto out;
> > -            }
> > 
> > -            err = v9fs_co_lstat(pdu, &path, &stbuf);
> > -            if (err < 0) {
> > -                goto out;
> > -            }
> > +    for (name_idx = 0; name_idx < nwnames; name_idx++) {
> > +        if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
> > +            strcmp("..", wnames[name_idx].data))
> > +        {
> > +            stbuf = stbufs[name_idx];
> > 
> >              err = stat_to_qid(pdu, &stbuf, &qid);
> >              if (err < 0) {
> >              
> >                  goto out;
> >              
> >              }
> > 
> > +            v9fs_path_copy(&path, &pathes[name_idx]);
> > 
> >              v9fs_path_copy(&dpath, &path);
> >          
> >          }
> >          memcpy(&qids[name_idx], &qid, sizeof(qid));
> 
> So far, replicating just the previous Twalk behaviour here (yet). Right now
> the request returns Rerror if any of the transmitted path elements could not
> be walked. Looking at the specs though, this seems to be wrong:
> 
> "If the first element cannot be walked for any reason, Rerror is returned.
> Otherwise, the walk will return an Rwalk mes- sage containing nwqid qids
> corresponding, in order, to the files that are visited by the nwqid
> successful elementwise walks; nwqid is therefore either nwname or the index
> of the first elementwise walk that failed. The value of nwqid can- not be
> zero unless nwname is zero. Also, nwqid will always be less than or equal
> to nwname. Only if it is equal, how- ever, will newfid be affected, in
> which case newfid will represent the file reached by the final elementwise
> walk requested in the message."
> 
> http://ericvh.github.io/9p-rfc/rfc9p2000.html#anchor33

I think it makes sense not mixing this purely internal performance 
optimization here with publicly visible protocol behaviour changes within the 
same patch. So I would retain the protocol behaviour of this patch for now and 
postpone the protocol fix to its own patch series a bit later, especially as 
the latter would also want that protocol behaviour change to be covered by a 
synth test case appropriately.

> 
> > @@ -1838,9 +1869,12 @@ out_nofid:
> >      if (nwnames && nwnames <= P9_MAXWELEM) {
> >      
> >          for (name_idx = 0; name_idx < nwnames; name_idx++) {
> >          
> >              v9fs_string_free(&wnames[name_idx]);
> > 
> > +            v9fs_path_free(&pathes[name_idx]);
> > 
> >          }
> >          g_free(wnames);
> >          g_free(qids);
> > 
> > +        g_free(stbufs);
> > +        g_free(pathes);
> > 
> >      }
> >  
> >  }
> > 
> > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
> > index 6f0b4c78c0..1567b67841 100644
> > --- a/hw/9pfs/9p.h
> > +++ b/hw/9pfs/9p.h
> > @@ -355,7 +355,6 @@ struct V9fsState {
> > 
> >      int32_t root_fid;
> >      Error *migration_blocker;
> >      V9fsConf fsconf;
> > 
> > -    V9fsQID root_qid;
> > 
> >      struct stat root_st;
> >      dev_t dev_id;
> >      struct qht qpd_table;
> 
> Best regards,
> Christian Schoenebeck




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/3] 9pfs: capture root stat
  2021-05-27 17:04 ` [PATCH 2/3] 9pfs: capture root stat Christian Schoenebeck
@ 2021-06-04 13:45   ` Christian Schoenebeck
  0 siblings, 0 replies; 7+ messages in thread
From: Christian Schoenebeck @ 2021-06-04 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Greg Kurz

On Donnerstag, 27. Mai 2021 19:04:11 CEST Christian Schoenebeck wrote:
> We already capture the QID of the exported 9p root path, i.e. to
> prevent client access outside the defined, exported filesystem's tree.
> This is currently checked by comparing the root QID with another FID's
> QID.
> 
> The problem with the latter is that resolving a QID of any given 9p path
> can only be done on 9p server's main thread, that's because it might
> mutate the server's state if inode remapping is enabled.
> 
> For that reason also capture the POSIX stat info of the root path for
> being able to identify on any (e.g. worker) thread whether an
> arbitrary given path is identical to the export root.
> 
> Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
> ---
>  hw/9pfs/9p.c | 10 +++++++++-
>  hw/9pfs/9p.h |  1 +
>  2 files changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index 89aa07db78..825de1561d 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -1400,6 +1400,7 @@ static void coroutine_fn v9fs_attach(void *opaque)
>      size_t offset = 7;
>      V9fsQID qid;
>      ssize_t err;
> +    struct stat stbuf;
> 
>      v9fs_string_init(&uname);
>      v9fs_string_init(&aname);
> @@ -1422,7 +1423,13 @@ static void coroutine_fn v9fs_attach(void *opaque)
>          clunk_fid(s, fid);
>          goto out;
>      }
> -    err = fid_to_qid(pdu, fidp, &qid);
> +    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
> +    if (err < 0) {
> +        err = -EINVAL;
> +        clunk_fid(s, fid);
> +        goto out;
> +    }
> +    err = stat_to_qid(pdu, &stbuf, &qid);
>      if (err < 0) {
>          err = -EINVAL;
>          clunk_fid(s, fid);
> @@ -1455,6 +1462,7 @@ static void coroutine_fn v9fs_attach(void *opaque)
>      err += offset;
> 
>      memcpy(&s->root_qid, &qid, sizeof(qid));
> +    memcpy(&s->root_st, &stbuf, sizeof(struct stat));

I'll make that sizeof(stbuf) instead to match with common code style here.

>      trace_v9fs_attach_return(pdu->tag, pdu->id,
>                               qid.type, qid.version, qid.path);
>  out:
> diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
> index 00381591ff..6f0b4c78c0 100644
> --- a/hw/9pfs/9p.h
> +++ b/hw/9pfs/9p.h
> @@ -356,6 +356,7 @@ struct V9fsState {
>      Error *migration_blocker;
>      V9fsConf fsconf;
>      V9fsQID root_qid;
> +    struct stat root_st;
>      dev_t dev_id;
>      struct qht qpd_table;
>      struct qht qpp_table;




^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-06-04 13:46 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-27 17:13 [PATCH 0/3] 9pfs: Twalk optimization Christian Schoenebeck
2021-05-27 17:03 ` [PATCH 1/3] 9pfs: Twalk benchmark Christian Schoenebeck
2021-05-27 17:04 ` [PATCH 2/3] 9pfs: capture root stat Christian Schoenebeck
2021-06-04 13:45   ` Christian Schoenebeck
2021-05-27 17:05 ` [PATCH 3/3] 9pfs: reduce latency of Twalk Christian Schoenebeck
2021-05-27 18:24   ` Christian Schoenebeck
2021-06-04 12:14     ` Christian Schoenebeck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.