9f5ccc
From e2af9793014ad67859aa73088765a52307cbe466 Mon Sep 17 00:00:00 2001
9f5ccc
From: Csaba Henk <csaba@redhat.com>
9f5ccc
Date: Tue, 7 Jan 2020 19:43:05 +0100
9f5ccc
Subject: [PATCH 346/346] fuse: degrade logging of write failure to fuse device
9f5ccc
9f5ccc
Problem:
9f5ccc
9f5ccc
FUSE uses failures of communicating with /dev/fuse with various
9f5ccc
errnos to indicate in-kernel conditions to userspace. Some of these
9f5ccc
shouldn't be handled as an application error. Also the standard
9f5ccc
POSIX errno description should not be shown as they are misleading
9f5ccc
in this context.
9f5ccc
9f5ccc
Solution:
9f5ccc
9f5ccc
When writing to the fuse device, the caller of the respective
9f5ccc
convenience routine can mask those errnos which don't qualify to
9f5ccc
be an error for the application in that context, so then those
9f5ccc
shall be reported at DEBUG level.
9f5ccc
9f5ccc
The possible non-standard errnos are reported with their
9f5ccc
POSIX name instead of their description to avoid confusion.
9f5ccc
(Eg. for ENOENT we don't log "no such file or directory",
9f5ccc
we log indeed literal "ENOENT".)
9f5ccc
9f5ccc
Upstream on https://review.gluster.org/23974
9f5ccc
> Change-Id: I510158843e4b1d482bdc496c2e97b1860dc1ba93
9f5ccc
> updates: bz#1193929
9f5ccc
> Signed-off-by: Csaba Henk <csaba@redhat.com>
9f5ccc
9f5ccc
BUG: 1763208
9f5ccc
Change-Id: Ib1676bb334ed153ce74ae1c0413fc0e58fb388c7
9f5ccc
Signed-off-by: Csaba Henk <csaba@redhat.com>
9f5ccc
Reviewed-on: https://code.engineering.redhat.com/gerrit/189056
9f5ccc
Tested-by: RHGS Build Bot <nigelb@redhat.com>
9f5ccc
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
9f5ccc
---
9f5ccc
 xlators/mount/fuse/src/fuse-bridge.c | 78 +++++++++++++++++++++++++++++++++---
9f5ccc
 xlators/mount/fuse/src/fuse-bridge.h |  9 ++++-
9f5ccc
 2 files changed, 80 insertions(+), 7 deletions(-)
9f5ccc
9f5ccc
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c
9f5ccc
index ebe5c28..6e99053 100644
9f5ccc
--- a/xlators/mount/fuse/src/fuse-bridge.c
9f5ccc
+++ b/xlators/mount/fuse/src/fuse-bridge.c
9f5ccc
@@ -198,7 +198,7 @@ fusedump_setup_meta(struct iovec *iovs, char *dir,
9f5ccc
 
9f5ccc
 static int
9f5ccc
 check_and_dump_fuse_W(fuse_private_t *priv, struct iovec *iov_out, int count,
9f5ccc
-                      ssize_t res)
9f5ccc
+                      ssize_t res, errnomask_t errnomask)
9f5ccc
 {
9f5ccc
     char w = 'W';
9f5ccc
     struct iovec diov[4] = {
9f5ccc
@@ -216,8 +216,59 @@ check_and_dump_fuse_W(fuse_private_t *priv, struct iovec *iov_out, int count,
9f5ccc
     struct fuse_out_header *fouh = NULL;
9f5ccc
 
9f5ccc
     if (res == -1) {
9f5ccc
-        gf_log_callingfn("glusterfs-fuse", GF_LOG_ERROR,
9f5ccc
-                         "writing to fuse device failed: %s", strerror(errno));
9f5ccc
+        const char *errdesc = NULL;
9f5ccc
+        gf_loglevel_t loglevel = GF_LOG_ERROR;
9f5ccc
+
9f5ccc
+        /* If caller masked the errno, then it
9f5ccc
+         * does not indicate an error at the application
9f5ccc
+         * level, so we degrade the log severity to DEBUG.
9f5ccc
+         */
9f5ccc
+        if (errnomask && errno < ERRNOMASK_MAX &&
9f5ccc
+            GET_ERRNO_MASK(errnomask, errno))
9f5ccc
+            loglevel = GF_LOG_DEBUG;
9f5ccc
+
9f5ccc
+        switch (errno) {
9f5ccc
+            /* The listed errnos are FUSE status indicators,
9f5ccc
+             * not legit values according to POSIX (see write(3p)),
9f5ccc
+             * so resolving them according to the standard
9f5ccc
+             * POSIX interpretation would be misleading.
9f5ccc
+             */
9f5ccc
+            case ENOENT:
9f5ccc
+                errdesc = "ENOENT";
9f5ccc
+                break;
9f5ccc
+            case ENOTDIR:
9f5ccc
+                errdesc = "ENOTDIR";
9f5ccc
+                break;
9f5ccc
+            case ENODEV:
9f5ccc
+                errdesc = "ENODEV";
9f5ccc
+                break;
9f5ccc
+            case EPERM:
9f5ccc
+                errdesc = "EPERM";
9f5ccc
+                break;
9f5ccc
+            case ENOMEM:
9f5ccc
+                errdesc = "ENOMEM";
9f5ccc
+                break;
9f5ccc
+            case ENOTCONN:
9f5ccc
+                errdesc = "ENOTCONN";
9f5ccc
+                break;
9f5ccc
+            case ECONNREFUSED:
9f5ccc
+                errdesc = "ECONNREFUSED";
9f5ccc
+                break;
9f5ccc
+            case EOVERFLOW:
9f5ccc
+                errdesc = "EOVERFLOW";
9f5ccc
+                break;
9f5ccc
+            case EBUSY:
9f5ccc
+                errdesc = "EBUSY";
9f5ccc
+                break;
9f5ccc
+            case ENOTEMPTY:
9f5ccc
+                errdesc = "ENOTEMPTY";
9f5ccc
+                break;
9f5ccc
+            default:
9f5ccc
+                errdesc = strerror(errno);
9f5ccc
+        }
9f5ccc
+
9f5ccc
+        gf_log_callingfn("glusterfs-fuse", loglevel,
9f5ccc
+                         "writing to fuse device failed: %s", errdesc);
9f5ccc
         return errno;
9f5ccc
     }
9f5ccc
 
9f5ccc
@@ -282,7 +333,7 @@ send_fuse_iov(xlator_t *this, fuse_in_header_t *finh, struct iovec *iov_out,
9f5ccc
     gf_log("glusterfs-fuse", GF_LOG_TRACE, "writev() result %d/%d %s", res,
9f5ccc
            fouh->len, res == -1 ? strerror(errno) : "");
9f5ccc
 
9f5ccc
-    return check_and_dump_fuse_W(priv, iov_out, count, res);
9f5ccc
+    return check_and_dump_fuse_W(priv, iov_out, count, res, NULL);
9f5ccc
 }
9f5ccc
 
9f5ccc
 static int
9f5ccc
@@ -353,6 +404,15 @@ fuse_invalidate_entry(xlator_t *this, uint64_t fuse_ino)
9f5ccc
         fouh->unique = 0;
9f5ccc
         fouh->error = FUSE_NOTIFY_INVAL_ENTRY;
9f5ccc
 
9f5ccc
+        if (ENOENT < ERRNOMASK_MAX)
9f5ccc
+            MASK_ERRNO(node->errnomask, ENOENT);
9f5ccc
+        if (ENOTDIR < ERRNOMASK_MAX)
9f5ccc
+            MASK_ERRNO(node->errnomask, ENOTDIR);
9f5ccc
+        if (EBUSY < ERRNOMASK_MAX)
9f5ccc
+            MASK_ERRNO(node->errnomask, EBUSY);
9f5ccc
+        if (ENOTEMPTY < ERRNOMASK_MAX)
9f5ccc
+            MASK_ERRNO(node->errnomask, ENOTEMPTY);
9f5ccc
+
9f5ccc
         if (dentry->name) {
9f5ccc
             nlen = strlen(dentry->name);
9f5ccc
             fouh->len = sizeof(*fouh) + sizeof(*fnieo) + nlen + 1;
9f5ccc
@@ -437,6 +497,9 @@ fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino)
9f5ccc
     fniio->off = 0;
9f5ccc
     fniio->len = -1;
9f5ccc
 
9f5ccc
+    if (ENOENT < ERRNOMASK_MAX)
9f5ccc
+        MASK_ERRNO(node->errnomask, ENOENT);
9f5ccc
+
9f5ccc
     fuse_log_eh(this, "Invalidated inode %" PRIu64 " (gfid: %s)", fuse_ino,
9f5ccc
                 uuid_utoa(inode->gfid));
9f5ccc
     gf_log("glusterfs-fuse", GF_LOG_TRACE,
9f5ccc
@@ -482,6 +545,7 @@ fuse_timed_message_new(void)
9f5ccc
     /* should be NULL if not set */
9f5ccc
     dmsg->fuse_message_body = NULL;
9f5ccc
     INIT_LIST_HEAD(&dmsg->next);
9f5ccc
+    memset(dmsg->errnomask, 0, sizeof(dmsg->errnomask));
9f5ccc
 
9f5ccc
     return dmsg;
9f5ccc
 }
9f5ccc
@@ -680,6 +744,8 @@ fuse_interrupt(xlator_t *this, fuse_in_header_t *finh, void *msg,
9f5ccc
         dmsg->fuse_out_header.unique = finh->unique;
9f5ccc
         dmsg->fuse_out_header.len = sizeof(dmsg->fuse_out_header);
9f5ccc
         dmsg->fuse_out_header.error = -EAGAIN;
9f5ccc
+        if (ENOENT < ERRNOMASK_MAX)
9f5ccc
+            MASK_ERRNO(dmsg->errnomask, ENOENT);
9f5ccc
         timespec_now(&dmsg->scheduled_ts);
9f5ccc
         timespec_adjust_delta(&dmsg->scheduled_ts,
9f5ccc
                               (struct timespec){0, 10000000});
9f5ccc
@@ -4848,7 +4914,7 @@ notify_kernel_loop(void *data)
9f5ccc
         iov_out.iov_base = node->inval_buf;
9f5ccc
         iov_out.iov_len = len;
9f5ccc
         rv = sys_writev(priv->fd, &iov_out, 1);
9f5ccc
-        check_and_dump_fuse_W(priv, &iov_out, 1, rv);
9f5ccc
+        check_and_dump_fuse_W(priv, &iov_out, 1, rv, node->errnomask);
9f5ccc
 
9f5ccc
         GF_FREE(node);
9f5ccc
 
9f5ccc
@@ -4940,7 +5006,7 @@ timed_response_loop(void *data)
9f5ccc
         iovs[1] = (struct iovec){dmsg->fuse_message_body,
9f5ccc
                                  len - sizeof(struct fuse_out_header)};
9f5ccc
         rv = sys_writev(priv->fd, iovs, 2);
9f5ccc
-        check_and_dump_fuse_W(priv, iovs, 2, rv);
9f5ccc
+        check_and_dump_fuse_W(priv, iovs, 2, rv, dmsg->errnomask);
9f5ccc
 
9f5ccc
         fuse_timed_message_free(dmsg);
9f5ccc
 
9f5ccc
diff --git a/xlators/mount/fuse/src/fuse-bridge.h b/xlators/mount/fuse/src/fuse-bridge.h
9f5ccc
index cf4479c..d2d462c 100644
9f5ccc
--- a/xlators/mount/fuse/src/fuse-bridge.h
9f5ccc
+++ b/xlators/mount/fuse/src/fuse-bridge.h
9f5ccc
@@ -195,14 +195,20 @@ struct fuse_private {
9f5ccc
 };
9f5ccc
 typedef struct fuse_private fuse_private_t;
9f5ccc
 
9f5ccc
+typedef uint64_t errnomask_t[2];
9f5ccc
+#define MASK_ERRNO(mask, n) ((mask)[(n) >> 6] |= ((uint64_t)1 << ((n)&63)))
9f5ccc
+#define GET_ERRNO_MASK(mask, n) ((mask)[(n) >> 6] & ((uint64_t)1 << ((n)&63)))
9f5ccc
+#define ERRNOMASK_MAX (64 * (sizeof(errnomask_t) / sizeof(uint64_t)))
9f5ccc
+
9f5ccc
 #define INVAL_BUF_SIZE                                                         \
9f5ccc
     (sizeof(struct fuse_out_header) +                                          \
9f5ccc
      max(sizeof(struct fuse_notify_inval_inode_out),                           \
9f5ccc
          sizeof(struct fuse_notify_inval_entry_out) + NAME_MAX + 1))
9f5ccc
 
9f5ccc
 struct fuse_invalidate_node {
9f5ccc
-    char inval_buf[INVAL_BUF_SIZE];
9f5ccc
+    errnomask_t errnomask;
9f5ccc
     struct list_head next;
9f5ccc
+    char inval_buf[INVAL_BUF_SIZE];
9f5ccc
 };
9f5ccc
 typedef struct fuse_invalidate_node fuse_invalidate_node_t;
9f5ccc
 
9f5ccc
@@ -210,6 +216,7 @@ struct fuse_timed_message {
9f5ccc
     struct fuse_out_header fuse_out_header;
9f5ccc
     void *fuse_message_body;
9f5ccc
     struct timespec scheduled_ts;
9f5ccc
+    errnomask_t errnomask;
9f5ccc
     struct list_head next;
9f5ccc
 };
9f5ccc
 typedef struct fuse_timed_message fuse_timed_message_t;
9f5ccc
-- 
9f5ccc
1.8.3.1
9f5ccc