The mkfs.f2fs checks 'zoned' and 'chunk_sectors' sysfs attributes of
zoned block devices to place F2FS areas aligned to block zone
boundaries. However, mkfs.f2fs code assumes the given device name has
corresponding sysfs path under /sys/block directory. This is not the
case when partition devices or symbolic links are specified as the
command line argument of mkfs.f2fs. In such a case, mkfs.f2fs fails to
read sysfs attributes for zoned block devices, handles the device as a
regular block device, and do not align F2FS areas to the block zone
boundaries. This results in F2FS write failures because of zone write
pointer miss-alignment in sequential write required zones.

Change mkfs.f2fs to handle sysfs path correctly. Read symbolic link
to get canonical sysfs path. If the device is a partition, cut the last
directory name in the sysfs path to refer the holder device attributes.

Also introduce read_file() and read_sys_attr() helper functions for
code simplicity.

Signed-off-by: Shin'ichiro Kawasaki <[email protected]>
---
 lib/libf2fs_zoned.c | 134 ++++++++++++++++++++++++++++++--------------
 1 file changed, 91 insertions(+), 43 deletions(-)

diff --git a/lib/libf2fs_zoned.c b/lib/libf2fs_zoned.c
index 6e32f32..e645004 100644
--- a/lib/libf2fs_zoned.c
+++ b/lib/libf2fs_zoned.c
@@ -8,6 +8,7 @@
  */
 #define _LARGEFILE64_SOURCE
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -15,6 +16,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <linux/limits.h>
 #ifndef ANDROID_WINDOWS_HOST
 #include <sys/ioctl.h>
 #endif
@@ -24,67 +27,112 @@
 
 #ifdef HAVE_LINUX_BLKZONED_H
 
+/*
+ * Read up to 255 characters from the first line of a file. Strip the trailing
+ * newline.
+ */
+static char *read_file(const char *path)
+{
+       char line[256], *p = line;
+       FILE *f;
+
+       f = fopen(path, "rb");
+       if (!f)
+               return NULL;
+       if (!fgets(line, sizeof(line), f))
+               line[0] = '\0';
+       strsep(&p, "\n");
+       fclose(f);
+
+       return strdup(line);
+}
+
+static char *read_sys_attr(const char *dev_path, const char *attr)
+{
+       struct stat statbuf;
+       char *sys_devno_path = NULL;
+       char sys_path[PATH_MAX];
+       ssize_t sz;
+       char *part_attr_path = NULL;
+       char *part_str = NULL;
+       char *delim = NULL;
+       char *attr_path = NULL;
+       char *attr_str = NULL;
+
+       if (stat(dev_path, &statbuf) < 0)
+               goto out;
+
+       if (asprintf(&sys_devno_path, "/sys/dev/block/%d:%d",
+                    major(statbuf.st_rdev), minor(statbuf.st_rdev)) < 0)
+               goto out;
+
+       sz = readlink(sys_devno_path, sys_path, sizeof(sys_path) - 1);
+       if (sz < 0)
+               goto out;
+       sys_path[sz] = '\0';
+
+       /*
+        * If the device is a partition device, cut the device name in the
+        * canonical sysfs path to obtain the sysfs path of the holder device.
+        *   e.g.:  /sys/devices/.../sda/sda1 -> /sys/devices/.../sda
+        */
+       if (asprintf(&part_attr_path, "/sys/dev/block/%s/partition",
+                    sys_path) < 0)
+               goto out;
+       part_str = read_file(part_attr_path);
+       if (part_str && *part_str == '1') {
+               delim = strrchr(sys_path, '/');
+               if (!delim)
+                       goto out;
+               *delim = '\0';
+       }
+
+       if (asprintf(&attr_path, "/sys/dev/block/%s/%s", sys_path, attr) < 0)
+               goto out;
+
+       attr_str = read_file(attr_path);
+out:
+       free(attr_path);
+       free(part_str);
+       free(part_attr_path);
+       free(sys_devno_path);
+       return attr_str;
+}
+
 void f2fs_get_zoned_model(int i)
 {
        struct device_info *dev = c.devices + i;
-       char str[128];
-       FILE *file;
-       int res;
-
-       /* Check that this is a zoned block device */
-       snprintf(str, sizeof(str),
-                "/sys/block/%s/queue/zoned",
-                basename(dev->path));
-       file = fopen(str, "r");
-       if (!file)
-               goto not_zoned;
-
-       memset(str, 0, sizeof(str));
-       res = fscanf(file, "%s", str);
-       fclose(file);
-
-       if (res != 1)
-               goto not_zoned;
-
-       if (strcmp(str, "host-aware") == 0) {
-               dev->zoned_model = F2FS_ZONED_HA;
+       char *model_str;
+
+       dev->zoned_model = F2FS_ZONED_NONE;
+
+       model_str = read_sys_attr(dev->path, "queue/zoned");
+       if (!model_str)
                return;
-       }
-       if (strcmp(str, "host-managed") == 0) {
+
+       if (strcmp(model_str, "host-aware") == 0)
+               dev->zoned_model = F2FS_ZONED_HA;
+       else if (strcmp(model_str, "host-managed") == 0)
                dev->zoned_model = F2FS_ZONED_HM;
-               return;
-       }
 
-not_zoned:
-       dev->zoned_model = F2FS_ZONED_NONE;
+       free(model_str);
 }
 
 int f2fs_get_zone_blocks(int i)
 {
        struct device_info *dev = c.devices + i;
        uint64_t sectors;
-       char str[128];
-       FILE *file;
-       int res;
+       char * cs_str;
 
        /* Get zone size */
        dev->zone_blocks = 0;
 
-       snprintf(str, sizeof(str),
-                "/sys/block/%s/queue/chunk_sectors",
-                basename(dev->path));
-       file = fopen(str, "r");
-       if (!file)
-               return -1;
-
-       memset(str, 0, sizeof(str));
-       res = fscanf(file, "%s", str);
-       fclose(file);
-
-       if (res != 1)
+       cs_str = read_sys_attr(dev->path, "queue/chunk_sectors");
+       if (!cs_str)
                return -1;
 
-       sectors = atol(str);
+       sectors = atol(cs_str);
+       free(cs_str);
        if (!sectors)
                return -1;
 
-- 
2.20.1



_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to