casaroli commented on code in PR #3665: URL: https://github.com/apache/nuttx-apps/pull/3665#discussion_r3653798394
########## system/xipfs/xipfs_main.c: ########## @@ -0,0 +1,579 @@ +/**************************************************************************** + * apps/system/xipfs/xipfs_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/ioctl.h> +#include <sys/statfs.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <nuttx/fs/ioctl.h> +#include <nuttx/fs/xipfs.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAP_COLS 50 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* One row of the file table, gathered before anything is printed so that a + * failure part way through does not leave half a report behind. + */ + +struct xipfs_entry_s +{ + /* A path relative to the mountpoint, not one component, so that a file in + * a subdirectory is reported the way the volume names it. + */ + + char name[XIPFS_PATH_MAX + 1]; + uint32_t start; /* Relative to the data region */ + uint32_t nblocks; + uint32_t size; + uint32_t pincount; +}; + +struct xipfs_survey_s +{ + FAR char *map; /* One char per erase block */ + FAR struct xipfs_entry_s *files; + int nfiles; + uint32_t nblocks; /* Blocks in the data region */ + uint32_t blocksize; + uint32_t used; + uint32_t freeblocks; + uint32_t largestrun; + uint32_t nruns; /* Distinct free runs */ + uint32_t pinned; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void show_usage(FAR const char *progname, int exitcode) +{ + fprintf(stderr, "USAGE: %s [-n] [-t <ms>] [<mountpoint>]\n", progname); + fprintf(stderr, "\nCompact a xipfs volume, or report what one looks " + "like.\n"); + fprintf(stderr, "\nWhere:\n"); + fprintf(stderr, "\t-n: Dry run. Report block usage and fragmentation " + "and exit\n"); + fprintf(stderr, "\t without moving anything.\n"); + fprintf(stderr, "\t-t <ms>: Time budget for the compaction pass. " + "0, the default,\n"); + fprintf(stderr, "\t means run to completion.\n"); + fprintf(stderr, "\t<mountpoint>: Default: %s\n", + CONFIG_SYSTEM_XIPFS_MOUNTPOINT); + exit(exitcode); +} + +/**************************************************************************** + * Name: survey_free + ****************************************************************************/ + +static void survey_free(FAR struct xipfs_survey_s *s) +{ + free(s->map); + free(s->files); + s->map = NULL; + s->files = NULL; +} + +/**************************************************************************** + * Name: survey_walk + * + * Description: + * Record where every file below 'dir' physically sits, descending into + * the directories xipfs synthesises from the names. 'rel' is the path of + * 'dir' relative to the mount, which is what a file is recorded under so + * that the table names it the way the volume does. + * + ****************************************************************************/ + +static int survey_walk(FAR const char *dir, FAR const char *rel, + FAR struct xipfs_survey_s *s, FAR int *capacity) +{ + struct xipfs_extent_info_s info; + FAR struct dirent *de; + FAR DIR *dirp; + uint32_t i; + int ret; + + dirp = opendir(dir); + if (dirp == NULL) + { + fprintf(stderr, "ERROR: opendir %s failed: %d\n", dir, errno); + return -errno; + } + + while ((de = readdir(dirp)) != NULL) + { + FAR struct xipfs_entry_s *e; + char path[PATH_MAX]; + char sub[XIPFS_PATH_MAX + 1]; + int fd; + + snprintf(path, sizeof(path), "%s/%s", dir, de->d_name); + + if (rel[0] == '\0') + { + strlcpy(sub, de->d_name, sizeof(sub)); + } + else + { + snprintf(sub, sizeof(sub), "%s/%s", rel, de->d_name); + } + + if (de->d_type == DTYPE_DIRECTORY) + { + ret = survey_walk(path, sub, s, capacity); + if (ret < 0) + { + closedir(dirp); + return ret; + } + + continue; + } + + fd = open(path, O_RDONLY); + if (fd < 0) + { + continue; + } + + ret = ioctl(fd, XIPFSIOC_EXTENTINFO, (unsigned long)(uintptr_t)&info); + close(fd); + + if (ret < 0) + { + continue; + } + + if (s->nfiles == *capacity) + { + FAR void *tmp; + + *capacity *= 2; + tmp = realloc(s->files, *capacity * sizeof(struct xipfs_entry_s)); + if (tmp == NULL) + { + closedir(dirp); + return -ENOMEM; + } + + s->files = tmp; + } + + e = &s->files[s->nfiles++]; + strlcpy(e->name, sub, sizeof(e->name)); + e->start = info.start_block - info.data_start; + e->nblocks = info.nblocks; + e->size = info.size; + e->pincount = info.pincount; + + if (e->pincount > 0) + { + s->pinned += e->nblocks; + } + + for (i = 0; i < e->nblocks; i++) + { + if (e->start + i < s->nblocks) + { + s->map[e->start + i] = e->pincount > 0 ? 'P' : '#'; + } + } + } + + closedir(dirp); + return OK; +} + +/**************************************************************************** + * Name: survey_collect + * + * Description: + * Walk the mount and record where every file physically sits. The block + * counts come from statfs so that an empty volume still reports its + * geometry -- XIPFSIOC_EXTENTINFO can only be issued against a regular + * file, so with no files there is nothing to ask. + * + ****************************************************************************/ + +static int survey_collect(FAR const char *mount, + FAR struct xipfs_survey_s *s) +{ + struct statfs sbuf; + uint32_t run = 0; + uint32_t i; + int capacity = 8; + int ret; + + memset(s, 0, sizeof(*s)); + + if (statfs(mount, &sbuf) < 0) + { + fprintf(stderr, "ERROR: statfs %s failed: %d\n", mount, errno); + return -errno; + } + + s->nblocks = sbuf.f_blocks; + s->blocksize = sbuf.f_bsize; + + if (s->nblocks == 0) + { + fprintf(stderr, "ERROR: %s reports no blocks; not a xipfs mount?\n", + mount); + return -EINVAL; + } + + s->map = malloc(s->nblocks); + if (s->map == NULL) + { + return -ENOMEM; + } + + memset(s->map, '.', s->nblocks); + + s->files = malloc(capacity * sizeof(struct xipfs_entry_s)); + if (s->files == NULL) + { + survey_free(s); + return -ENOMEM; + } + + ret = survey_walk(mount, "", s, &capacity); + if (ret < 0) + { + survey_free(s); + return ret; + } + + /* Reduce the map to the numbers a caller actually decides on: how much is + * free, and how much of that is reachable by a single allocation. + */ + + for (i = 0; i < s->nblocks; i++) + { + if (s->map[i] == '.') + { + s->freeblocks++; + if (run == 0) + { + s->nruns++; + } + + if (++run > s->largestrun) + { + s->largestrun = run; + } + } + else + { + s->used++; + run = 0; + } + } + + return OK; +} + +/**************************************************************************** + * Name: survey_report + ****************************************************************************/ + +static void survey_report(FAR const char *mount, + FAR struct xipfs_survey_s *s) +{ + uint32_t frag; + uint32_t i; + int width; + int f; + + printf("%s: %lu blocks of %lu bytes (%lu KB)\n", + mount, (unsigned long)s->nblocks, (unsigned long)s->blocksize, + (unsigned long)(s->nblocks * (uint64_t)s->blocksize / 1024)); + + if (s->nfiles > 0) + { + /* A name here is a path relative to the mountpoint, so it can be + * longer than one component. Widen the column to the longest one + * rather than let it push the rest of the row out of alignment. + */ + + width = XIPFS_NAME_MAX; + for (f = 0; f < s->nfiles; f++) + { + int len = (int)strlen(s->files[f].name); + + if (len > width) + { + width = len; + } + } + + printf("\n %-*s %6s %7s %8s %4s\n", width, "file", + "start", "blocks", "bytes", "pin"); + + for (f = 0; f < s->nfiles; f++) + { + FAR struct xipfs_entry_s *e = &s->files[f]; + + printf(" %-*s %6lu %7lu %8lu %4lu\n", width, e->name, + (unsigned long)e->start, (unsigned long)e->nblocks, + (unsigned long)e->size, (unsigned long)e->pincount); + } + } + + printf("\n"); + + for (i = 0; i < s->nblocks; i += MAP_COLS) + { + uint32_t n = s->nblocks - i; + char row[MAP_COLS + 1]; + + if (n > MAP_COLS) + { + n = MAP_COLS; + } + + memcpy(row, &s->map[i], n); + row[n] = '\0'; + printf(" %4lu |%s|\n", (unsigned long)i, row); + } + + printf("\n '.' free '#' in use 'P' pinned by a live mapping\n"); + + /* The share of free space that a single allocation cannot reach. This is + * the number that says whether compacting is worth doing: at 0% the + * largest possible file already fits, whatever the map looks like. + */ + + frag = 0; + if (s->freeblocks > 0) + { + frag = (uint32_t)(((uint64_t)(s->freeblocks - s->largestrun) * 100) / Review Comment: I think these are necessary because we do the calculation with uint64_t then truncate to 32 bit explicitly ########## testing/fs/xipfs/xipfs_main.c: ########## @@ -0,0 +1,2032 @@ +/**************************************************************************** + * apps/testing/fs/xipfs/xipfs_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <malloc.h> +#include <sched.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <nuttx/fs/ioctl.h> +#include <nuttx/fs/xipfs.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUNTPT CONFIG_TESTING_FS_XIPFS_MOUNTPT +#define MTDDEV CONFIG_TESTING_FS_XIPFS_MTD + +#define PATH(name) MOUNTPT "/" name + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_passed; +static int g_failed; +static FAR uint8_t *g_buffer; +static size_t g_bufsize = 16384; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void report(FAR const char *name, bool ok, FAR const char *detail) +{ + if (ok) + { + g_passed++; + printf(" PASS %s\n", name); + } + else + { + g_failed++; + printf(" FAIL %s: %s\n", name, detail ? detail : ""); + } +} + +#define CHECK(name, cond, detail) report((name), (cond), (detail)) + +/**************************************************************************** + * Name: remount + * + * Description: + * Discard every scrap of in-RAM filesystem state and mount again from the + * medium. Because the rammtd buffer survives, this is exactly what a + * reboot looks like to the filesystem, which is what makes the power loss + * sweep below meaningful without restarting the simulator. + * + ****************************************************************************/ + +static int remount(void) +{ + int ret; + + ret = umount(MOUNTPT); + if (ret < 0 && errno != ENOENT && errno != EINVAL) + { + printf(" umount failed: %d\n", errno); + return -errno; + } + + ret = mount(MTDDEV, MOUNTPT, "xipfs", 0, NULL); + if (ret < 0) + { + return -errno; + } + + return 0; +} + +/**************************************************************************** + * Name: fill_pattern + ****************************************************************************/ + +static void fill_pattern(FAR uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) + { + buf[i] = (uint8_t)(seed + (i * 7)); + } +} + +/**************************************************************************** + * Name: create_file + * + * Description: + * Create a file of a known size. ftruncate is how the exact size is + * declared up front, which is what lets the filesystem reserve the exact + * contiguous extent rather than over-reserving and trimming. + * + ****************************************************************************/ + +static int create_file(FAR const char *path, size_t len, uint8_t seed) +{ + ssize_t nwritten; + int fd; + int ret = 0; + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + return -errno; + } + + if (ftruncate(fd, len) < 0) + { + ret = -errno; + goto out; + } + + fill_pattern(g_buffer, len, seed); + + nwritten = write(fd, g_buffer, len); + if (nwritten != (ssize_t)len) Review Comment: this is necessary to avoid warning ########## testing/fs/xipfs/xipfs_main.c: ########## @@ -0,0 +1,2032 @@ +/**************************************************************************** + * apps/testing/fs/xipfs/xipfs_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <malloc.h> +#include <sched.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <nuttx/fs/ioctl.h> +#include <nuttx/fs/xipfs.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUNTPT CONFIG_TESTING_FS_XIPFS_MOUNTPT +#define MTDDEV CONFIG_TESTING_FS_XIPFS_MTD + +#define PATH(name) MOUNTPT "/" name + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_passed; +static int g_failed; +static FAR uint8_t *g_buffer; +static size_t g_bufsize = 16384; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void report(FAR const char *name, bool ok, FAR const char *detail) +{ + if (ok) + { + g_passed++; + printf(" PASS %s\n", name); + } + else + { + g_failed++; + printf(" FAIL %s: %s\n", name, detail ? detail : ""); + } +} + +#define CHECK(name, cond, detail) report((name), (cond), (detail)) + +/**************************************************************************** + * Name: remount + * + * Description: + * Discard every scrap of in-RAM filesystem state and mount again from the + * medium. Because the rammtd buffer survives, this is exactly what a + * reboot looks like to the filesystem, which is what makes the power loss + * sweep below meaningful without restarting the simulator. + * + ****************************************************************************/ + +static int remount(void) +{ + int ret; + + ret = umount(MOUNTPT); + if (ret < 0 && errno != ENOENT && errno != EINVAL) + { + printf(" umount failed: %d\n", errno); + return -errno; + } + + ret = mount(MTDDEV, MOUNTPT, "xipfs", 0, NULL); + if (ret < 0) + { + return -errno; + } + + return 0; +} + +/**************************************************************************** + * Name: fill_pattern + ****************************************************************************/ + +static void fill_pattern(FAR uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) + { + buf[i] = (uint8_t)(seed + (i * 7)); + } +} + +/**************************************************************************** + * Name: create_file + * + * Description: + * Create a file of a known size. ftruncate is how the exact size is + * declared up front, which is what lets the filesystem reserve the exact + * contiguous extent rather than over-reserving and trimming. + * + ****************************************************************************/ + +static int create_file(FAR const char *path, size_t len, uint8_t seed) +{ + ssize_t nwritten; + int fd; + int ret = 0; + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + return -errno; + } + + if (ftruncate(fd, len) < 0) + { + ret = -errno; + goto out; + } + + fill_pattern(g_buffer, len, seed); + + nwritten = write(fd, g_buffer, len); + if (nwritten != (ssize_t)len) + { + ret = nwritten < 0 ? -errno : -EIO; + } + +out: + if (close(fd) < 0 && ret == 0) + { + ret = -errno; + } + + return ret; +} + +/**************************************************************************** + * Name: verify_file + ****************************************************************************/ + +static bool verify_file(FAR const char *path, size_t len, uint8_t seed) +{ + FAR uint8_t *expect; + ssize_t nread; + bool ok = false; + int fd; + + expect = malloc(len); + if (expect == NULL) + { + return false; + } + + fill_pattern(expect, len, seed); + + fd = open(path, O_RDONLY); + if (fd < 0) + { + free(expect); + return false; + } + + nread = read(fd, g_buffer, len + 1); + if (nread == (ssize_t)len && memcmp(g_buffer, expect, len) == 0) Review Comment: I think this is necessary ########## system/xipfs/xipfs_main.c: ########## @@ -0,0 +1,579 @@ +/**************************************************************************** + * apps/system/xipfs/xipfs_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/ioctl.h> +#include <sys/statfs.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <nuttx/fs/ioctl.h> +#include <nuttx/fs/xipfs.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAP_COLS 50 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* One row of the file table, gathered before anything is printed so that a + * failure part way through does not leave half a report behind. + */ + +struct xipfs_entry_s +{ + /* A path relative to the mountpoint, not one component, so that a file in + * a subdirectory is reported the way the volume names it. + */ + + char name[XIPFS_PATH_MAX + 1]; + uint32_t start; /* Relative to the data region */ + uint32_t nblocks; + uint32_t size; + uint32_t pincount; +}; + +struct xipfs_survey_s +{ + FAR char *map; /* One char per erase block */ + FAR struct xipfs_entry_s *files; + int nfiles; + uint32_t nblocks; /* Blocks in the data region */ + uint32_t blocksize; + uint32_t used; + uint32_t freeblocks; + uint32_t largestrun; + uint32_t nruns; /* Distinct free runs */ + uint32_t pinned; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void show_usage(FAR const char *progname, int exitcode) +{ + fprintf(stderr, "USAGE: %s [-n] [-t <ms>] [<mountpoint>]\n", progname); + fprintf(stderr, "\nCompact a xipfs volume, or report what one looks " + "like.\n"); + fprintf(stderr, "\nWhere:\n"); + fprintf(stderr, "\t-n: Dry run. Report block usage and fragmentation " + "and exit\n"); + fprintf(stderr, "\t without moving anything.\n"); + fprintf(stderr, "\t-t <ms>: Time budget for the compaction pass. " + "0, the default,\n"); + fprintf(stderr, "\t means run to completion.\n"); + fprintf(stderr, "\t<mountpoint>: Default: %s\n", + CONFIG_SYSTEM_XIPFS_MOUNTPOINT); + exit(exitcode); +} + +/**************************************************************************** + * Name: survey_free + ****************************************************************************/ + +static void survey_free(FAR struct xipfs_survey_s *s) +{ + free(s->map); + free(s->files); + s->map = NULL; + s->files = NULL; +} + +/**************************************************************************** + * Name: survey_walk + * + * Description: + * Record where every file below 'dir' physically sits, descending into + * the directories xipfs synthesises from the names. 'rel' is the path of + * 'dir' relative to the mount, which is what a file is recorded under so + * that the table names it the way the volume does. + * + ****************************************************************************/ + +static int survey_walk(FAR const char *dir, FAR const char *rel, + FAR struct xipfs_survey_s *s, FAR int *capacity) +{ + struct xipfs_extent_info_s info; + FAR struct dirent *de; + FAR DIR *dirp; + uint32_t i; + int ret; + + dirp = opendir(dir); + if (dirp == NULL) + { + fprintf(stderr, "ERROR: opendir %s failed: %d\n", dir, errno); + return -errno; + } + + while ((de = readdir(dirp)) != NULL) + { + FAR struct xipfs_entry_s *e; + char path[PATH_MAX]; + char sub[XIPFS_PATH_MAX + 1]; + int fd; + + snprintf(path, sizeof(path), "%s/%s", dir, de->d_name); + + if (rel[0] == '\0') + { + strlcpy(sub, de->d_name, sizeof(sub)); + } + else + { + snprintf(sub, sizeof(sub), "%s/%s", rel, de->d_name); + } + + if (de->d_type == DTYPE_DIRECTORY) + { + ret = survey_walk(path, sub, s, capacity); + if (ret < 0) + { + closedir(dirp); + return ret; + } + + continue; + } + + fd = open(path, O_RDONLY); + if (fd < 0) + { + continue; + } + + ret = ioctl(fd, XIPFSIOC_EXTENTINFO, (unsigned long)(uintptr_t)&info); + close(fd); + + if (ret < 0) + { + continue; + } + + if (s->nfiles == *capacity) + { + FAR void *tmp; + + *capacity *= 2; + tmp = realloc(s->files, *capacity * sizeof(struct xipfs_entry_s)); + if (tmp == NULL) + { + closedir(dirp); + return -ENOMEM; + } + + s->files = tmp; + } + + e = &s->files[s->nfiles++]; + strlcpy(e->name, sub, sizeof(e->name)); + e->start = info.start_block - info.data_start; + e->nblocks = info.nblocks; + e->size = info.size; + e->pincount = info.pincount; + + if (e->pincount > 0) + { + s->pinned += e->nblocks; + } + + for (i = 0; i < e->nblocks; i++) + { + if (e->start + i < s->nblocks) + { + s->map[e->start + i] = e->pincount > 0 ? 'P' : '#'; + } + } + } + + closedir(dirp); + return OK; +} + +/**************************************************************************** + * Name: survey_collect + * + * Description: + * Walk the mount and record where every file physically sits. The block + * counts come from statfs so that an empty volume still reports its + * geometry -- XIPFSIOC_EXTENTINFO can only be issued against a regular + * file, so with no files there is nothing to ask. + * + ****************************************************************************/ + +static int survey_collect(FAR const char *mount, + FAR struct xipfs_survey_s *s) +{ + struct statfs sbuf; + uint32_t run = 0; + uint32_t i; + int capacity = 8; + int ret; + + memset(s, 0, sizeof(*s)); + + if (statfs(mount, &sbuf) < 0) + { + fprintf(stderr, "ERROR: statfs %s failed: %d\n", mount, errno); + return -errno; + } + + s->nblocks = sbuf.f_blocks; + s->blocksize = sbuf.f_bsize; + + if (s->nblocks == 0) + { + fprintf(stderr, "ERROR: %s reports no blocks; not a xipfs mount?\n", + mount); + return -EINVAL; + } + + s->map = malloc(s->nblocks); + if (s->map == NULL) + { + return -ENOMEM; + } + + memset(s->map, '.', s->nblocks); + + s->files = malloc(capacity * sizeof(struct xipfs_entry_s)); + if (s->files == NULL) + { + survey_free(s); + return -ENOMEM; + } + + ret = survey_walk(mount, "", s, &capacity); + if (ret < 0) + { + survey_free(s); + return ret; + } + + /* Reduce the map to the numbers a caller actually decides on: how much is + * free, and how much of that is reachable by a single allocation. + */ + + for (i = 0; i < s->nblocks; i++) + { + if (s->map[i] == '.') + { + s->freeblocks++; + if (run == 0) + { + s->nruns++; + } + + if (++run > s->largestrun) + { + s->largestrun = run; + } + } + else + { + s->used++; + run = 0; + } + } + + return OK; +} + +/**************************************************************************** + * Name: survey_report + ****************************************************************************/ + +static void survey_report(FAR const char *mount, + FAR struct xipfs_survey_s *s) +{ + uint32_t frag; + uint32_t i; + int width; + int f; + + printf("%s: %lu blocks of %lu bytes (%lu KB)\n", + mount, (unsigned long)s->nblocks, (unsigned long)s->blocksize, + (unsigned long)(s->nblocks * (uint64_t)s->blocksize / 1024)); + + if (s->nfiles > 0) + { + /* A name here is a path relative to the mountpoint, so it can be + * longer than one component. Widen the column to the longest one + * rather than let it push the rest of the row out of alignment. + */ + + width = XIPFS_NAME_MAX; + for (f = 0; f < s->nfiles; f++) + { + int len = (int)strlen(s->files[f].name); Review Comment: i think we should keep this to make the conversion explicit ########## testing/fs/xipfs/xipfs_main.c: ########## @@ -0,0 +1,2032 @@ +/**************************************************************************** + * apps/testing/fs/xipfs/xipfs_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <malloc.h> +#include <sched.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <nuttx/fs/ioctl.h> +#include <nuttx/fs/xipfs.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUNTPT CONFIG_TESTING_FS_XIPFS_MOUNTPT +#define MTDDEV CONFIG_TESTING_FS_XIPFS_MTD + +#define PATH(name) MOUNTPT "/" name + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_passed; +static int g_failed; +static FAR uint8_t *g_buffer; +static size_t g_bufsize = 16384; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void report(FAR const char *name, bool ok, FAR const char *detail) +{ + if (ok) + { + g_passed++; + printf(" PASS %s\n", name); + } + else + { + g_failed++; + printf(" FAIL %s: %s\n", name, detail ? detail : ""); + } +} + +#define CHECK(name, cond, detail) report((name), (cond), (detail)) + +/**************************************************************************** + * Name: remount + * + * Description: + * Discard every scrap of in-RAM filesystem state and mount again from the + * medium. Because the rammtd buffer survives, this is exactly what a + * reboot looks like to the filesystem, which is what makes the power loss + * sweep below meaningful without restarting the simulator. + * + ****************************************************************************/ + +static int remount(void) +{ + int ret; + + ret = umount(MOUNTPT); + if (ret < 0 && errno != ENOENT && errno != EINVAL) + { + printf(" umount failed: %d\n", errno); + return -errno; + } + + ret = mount(MTDDEV, MOUNTPT, "xipfs", 0, NULL); + if (ret < 0) + { + return -errno; + } + + return 0; +} + +/**************************************************************************** + * Name: fill_pattern + ****************************************************************************/ + +static void fill_pattern(FAR uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) + { + buf[i] = (uint8_t)(seed + (i * 7)); Review Comment: I think it is better to keep the truncation explicit here ########## examples/nxflatxip/nxflatxip_main.c: ########## @@ -0,0 +1,892 @@ +/**************************************************************************** + * apps/examples/nxflatxip/nxflatxip_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * End to end demonstration of what a writable execute-in-place file system + * is for: + * + * 1. write an NXFLAT module into xipfs at run time, the way a download + * would, + * 2. confirm the file system can hand out a direct flash pointer for it, + * 3. run two instances of it concurrently, + * 4. confirm both ran correctly, that their data did not interfere, and + * that the shared text was pinned in place while they ran. + * + * The same module in a ROMFS image would give the same execute-in-place + * behaviour; what it could not do is arrive after the firmware was built. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include <nuttx/binfmt/binfmt.h> +#include <nuttx/fs/ioctl.h> +#include <nuttx/fs/xipfs.h> +#include <nuttx/symtab.h> + +#include "xipmod_bin.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUNTPT CONFIG_EXAMPLES_NXFLATXIP_MOUNTPT +#define MTDDEV CONFIG_EXAMPLES_NXFLATXIP_MTD +#define MODPATH MOUNTPT "/xipmod" + +/**************************************************************************** + * External Symbols + ****************************************************************************/ + +/* Generated by module/Makefile from the import list mknxflat left in the + * module's thunk file. The NXFLAT loader has no other way to resolve the + * functions the module calls. + */ + +extern const struct symtab_s g_nxflat_exports[]; +extern const int g_nxflat_nexports; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xipmod_report + * + * Description: + * Called by the module, running in place from flash, to report what it + * found. It passes numbers rather than formatting its own output; see + * module/xipmod.c for why. + * + * This is also the direction of the interface that needs r10 to survive: + * the module reaches its own data through that register, and this + * function runs with the module's data base still in it. That is what + * the --fixed-r10 the ARM toolchain adds under CONFIG_PIC is for. + * + ****************************************************************************/ + +void xipmod_report(int seed, FAR const void *text, FAR const void *stack, + long sum, int errors) +{ + syslog(LOG_INFO, " instance seed %d: text %p, stack %p, sum %ld -- %s\n", + seed, text, stack, sum, + errors == 0 ? "data private and intact" : "DATA CORRUPTED"); +} + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stage_module + * + * Description: + * Write the module into the file system exactly as a download would: + * declare the size up front so xipfs reserves an exactly sized + * contiguous extent, then write it once. + * + ****************************************************************************/ + +static int stage_module(void) +{ + ssize_t nwritten; + int fd; + + unlink(MODPATH); + + fd = open(MODPATH, O_WRONLY | O_CREAT | O_TRUNC, 0755); + if (fd < 0) + { + syslog(LOG_INFO, "ERROR: open %s failed: %d\n", MODPATH, errno); + return -errno; + } + + if (ftruncate(fd, g_xipmod_len) < 0) + { + syslog(LOG_INFO, "ERROR: ftruncate failed: %d\n", errno); + close(fd); + return -errno; + } + + nwritten = write(fd, g_xipmod, g_xipmod_len); + close(fd); + + if (nwritten != (ssize_t)g_xipmod_len) + { + syslog(LOG_INFO, "ERROR: short write: %zd of %u\n", nwritten, + g_xipmod_len); + return -EIO; + } + + syslog(LOG_INFO, "staged %u bytes to %s\n", g_xipmod_len, MODPATH); + return 0; +} + +/**************************************************************************** + * Name: extent_info_path + * + * Description: + * Where a file physically lies: its extent, its flash address, and how + * many live mappings are pinning it in place. + * + ****************************************************************************/ + +static int extent_info_path(FAR const char *path, + FAR struct xipfs_extent_info_s *info) +{ + int fd; + int ret; + + fd = open(path, O_RDONLY); + if (fd < 0) + { + return -errno; + } + + ret = ioctl(fd, XIPFSIOC_EXTENTINFO, (unsigned long)(uintptr_t)info); + close(fd); + + return ret < 0 ? -errno : 0; +} + +/**************************************************************************** + * Name: bench_read + * + * Description: + * Time a bulk read straight out of the memory mapped flash, big enough to + * miss the XIP cache. Run once on a freshly booted system, where the + * bootrom's fast read mode is still in effect, and again after a flash + * write has forced the driver to restore XIP itself. The difference is + * the cost of whichever restore path the driver used. + * + ****************************************************************************/ + +#define BENCH_PATH MOUNTPT "/big" +#define BENCH_SIZE (256 * 1024) + +static long bench_read(void) +{ + struct timespec t0; + struct timespec t1; + FAR uint8_t *buf; + ssize_t n; + long total = 0; + int fd; + + buf = malloc(4096); + if (buf == NULL) + { + return -1; + } + + fd = open(BENCH_PATH, O_RDONLY); + if (fd < 0) + { + free(buf); + return -1; + } + + clock_gettime(CLOCK_MONOTONIC, &t0); + + while ((n = read(fd, buf, 4096)) > 0) + { + total += n; + } + + clock_gettime(CLOCK_MONOTONIC, &t1); + + close(fd); + free(buf); + + if (total <= 0) + { + return -1; + } + + return (t1.tv_sec - t0.tv_sec) * 1000 + + (t1.tv_nsec - t0.tv_nsec) / 1000000; +} + +static int bench_main(void) +{ + long before; + long after; + int fd; + + syslog(LOG_INFO, "\nXIP read benchmark (%d KB)\n", BENCH_SIZE / 1024); + + before = bench_read(); + if (before < 0) + { + syslog(LOG_INFO, " create %s first: " + "dd if=/dev/zero of=%s bs=512 count=512\n", + BENCH_PATH, BENCH_PATH); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, " before any flash write : %ld ms\n", before); + + /* Any write forces the driver to tear XIP down and put it back */ + + fd = open(MOUNTPT "/benchw", O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) + { + write(fd, "x", 1); + close(fd); + } + + after = bench_read(); + syslog(LOG_INFO, " after a flash write : %ld ms\n", after); + + if (before > 0) + { + syslog(LOG_INFO, " ratio : %ld.%02ldx\n", + after / before, ((after * 100) / before) % 100); + } + + unlink(MOUNTPT "/benchw"); + return EXIT_SUCCESS; +} + +static int remount(void) +{ + int ret; + + ret = umount(MOUNTPT); + if (ret < 0 && errno != ENOENT && errno != EINVAL) + { + return -errno; + } + + ret = mount(MTDDEV, MOUNTPT, "xipfs", 0, NULL); + return ret < 0 ? -errno : 0; +} + +static void fill_pattern(FAR uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) + { + buf[i] = (uint8_t)(seed + (i * 7)); + } +} + +static int create_file(FAR const char *path, size_t len, uint8_t seed) +{ + FAR uint8_t *buf; + ssize_t nw; + int fd; + int ret = 0; + + buf = malloc(len); + if (buf == NULL) + { + return -ENOMEM; + } + + fill_pattern(buf, len, seed); + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + free(buf); + return -errno; + } + + if (ftruncate(fd, len) < 0) + { + ret = -errno; + } + else + { + nw = write(fd, buf, len); + if (nw != (ssize_t)len) + { + ret = nw < 0 ? -errno : -EIO; + } + } + + close(fd); + free(buf); + return ret; +} + +static bool verify_file(FAR const char *path, size_t len, uint8_t seed) +{ + FAR uint8_t *got; + FAR uint8_t *want; + bool ok = false; + ssize_t n; + int fd; + + got = malloc(len); + want = malloc(len); + if (got == NULL || want == NULL) + { + free(got); + free(want); + return false; + } + + fill_pattern(want, len, seed); + + fd = open(path, O_RDONLY); + if (fd >= 0) + { + n = read(fd, got, len); + ok = (n == (ssize_t)len) && (memcmp(got, want, len) == 0); + close(fd); + } + + free(got); + free(want); + return ok; +} + +/**************************************************************************** + * Name: run_defrag + * + * Description: + * Compact the volume. The command acts on the volume, so it goes through + * a descriptor for the mountpoint directory: a descriptor for a file + * inside it would hold that file open, and an open extent cannot be + * relocated. + * + ****************************************************************************/ + +static int run_defrag(uint32_t max_ms, + FAR struct xipfs_defrag_result_s *result) +{ + struct xipfs_defrag_arg_s arg; + int fd; + int ret; + + memset(&arg, 0, sizeof(arg)); + arg.max_ms = max_ms; + + fd = open(MOUNTPT, O_RDONLY | O_DIRECTORY); + if (fd < 0) + { + return -errno; + } + + ret = ioctl(fd, XIPFSIOC_DEFRAG, (unsigned long)(uintptr_t)&arg); + close(fd); + + if (ret < 0) + { + return -errno; + } + + *result = arg.result; + return 0; +} + +/**************************************************************************** + * Defragmentation showcase + * + * Fragments the volume until an allocation genuinely cannot be satisfied -- + * plenty of free space, but none of it contiguous -- then compacts and + * retries the same allocation. On a real board everything below runs + * against real NOR: each move is a page-at-a-time copy, a metadata commit, + * and an erase of the vacated blocks. + ****************************************************************************/ + +#define DF_FILES 55 +#define DF_MAPCOLS 50 + +static char g_map[512]; + +static int df_mapfill(FAR uint32_t *dstart, FAR uint32_t *dblocks) +{ + struct xipfs_extent_info_s info; + FAR struct dirent *de; + FAR DIR *dirp; + char path[80]; + int files = 0; + uint32_t i; + + memset(g_map, '.', sizeof(g_map)); + *dstart = 0; + *dblocks = 0; + + dirp = opendir(MOUNTPT); + if (dirp == NULL) + { + return -1; + } + + while ((de = readdir(dirp)) != NULL) + { + snprintf(path, sizeof(path), "%s/%s", MOUNTPT, de->d_name); + if (extent_info_path(path, &info) < 0) + { + continue; + } + + *dstart = info.data_start; + *dblocks = info.data_nblocks; + files++; + + for (i = 0; i < info.nblocks; i++) + { + uint32_t idx = info.start_block - info.data_start + i; + if (idx < sizeof(g_map)) + { + g_map[idx] = '#'; + } + } + } + + closedir(dirp); + return files; +} + +static void df_showmap(FAR const char *title, uint32_t dblocks, + FAR uint32_t *freeout, FAR uint32_t *largestout) +{ + uint32_t largest = 0; + uint32_t run = 0; + uint32_t free = 0; + uint32_t i; + char row[DF_MAPCOLS + 1]; + + syslog(LOG_INFO, "\n %s\n", title); + + for (i = 0; i < dblocks; i++) + { + if (g_map[i] == '.') + { + free++; + if (++run > largest) + { + largest = run; + } + } + else + { + run = 0; + } + } + + for (i = 0; i < dblocks; i += DF_MAPCOLS) + { + uint32_t n = dblocks - i; + if (n > DF_MAPCOLS) + { + n = DF_MAPCOLS; + } + + memcpy(row, &g_map[i], n); + row[n] = '\0'; + syslog(LOG_INFO, " %3lu |%s|\n", (unsigned long)i, row); + } + + syslog(LOG_INFO, " free %lu blocks, largest contiguous run %lu\n", + (unsigned long)free, (unsigned long)largest); + + if (freeout != NULL) + { + *freeout = free; + } + + if (largestout != NULL) + { + *largestout = largest; + } +} + +static int df_try_alloc(uint32_t nblocks, uint32_t erasesize) +{ + int fd; + int ret; + + unlink(MOUNTPT "/bigalloc"); + + fd = open(MOUNTPT "/bigalloc", O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + return -errno; + } + + ret = ftruncate(fd, (off_t)nblocks * erasesize); + close(fd); + + if (ret < 0) + { + ret = -errno; + unlink(MOUNTPT "/bigalloc"); + } + + return ret; +} + +/**************************************************************************** + * Name: empty_dir + * + * Description: + * Delete everything below 'dir', descending into subdirectories. + * + * One entry is taken per pass rather than a listing being collected up + * front, because this recurses and the task stack is the default 2 KB: an + * array of names big enough to be useful costs more than a kilobyte a + * frame, and two levels of that overflows and returns through a smashed + * frame. A pass costs an opendir instead, which no one is timing. + * + * The directory is closed before anything is removed, since unlinking + * while iterating skips entries. + * + ****************************************************************************/ + +static void empty_dir(FAR const char *dir) +{ + char path[80]; + bool more = true; + + while (more) + { + FAR struct dirent *de; + FAR DIR *dirp; + bool isdir; + + dirp = opendir(dir); + if (dirp == NULL) + { + return; + } + + de = readdir(dirp); + if (de == NULL) + { + closedir(dirp); + return; + } + + isdir = (de->d_type == DTYPE_DIRECTORY); + snprintf(path, sizeof(path), "%s/%s", dir, de->d_name); + closedir(dirp); + + if (isdir) + { + empty_dir(path); + more = rmdir(path) == 0; + } + else + { + more = unlink(path) == 0; + } + } +} + +static int defrag_main(void) +{ + struct xipfs_defrag_result_s res; + struct xipfs_extent_info_s info; + struct timespec t0; + struct timespec t1; + uint32_t dstart; + uint32_t dblocks; + uint32_t erasesize = 4096; + uint32_t want; + uint32_t freeblk = 0; + uint32_t largest = 0; + char path[80]; + long ms; + int i; + int ret; + + memset(&res, 0, sizeof(res)); + + syslog(LOG_INFO, "\n=== xipfs defragmentation ===\n"); + + /* Clean slate, so the map below accounts for every used block */ + + empty_dir(MOUNTPT); + + syslog(LOG_INFO, "\n[1] writing %d files...\n", DF_FILES); + + for (i = 0; i < DF_FILES; i++) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + if (create_file(path, 2000, (uint8_t)(i + 1)) < 0) + { + syslog(LOG_INFO, " stopped at %d (volume full)\n", i); + break; + } + } + + if (extent_info_path(MOUNTPT "/m00", &info) == 0) + { + erasesize = info.erasesize; + } + + df_mapfill(&dstart, &dblocks); + df_showmap("map, all files present:", dblocks, NULL, NULL); + + syslog(LOG_INFO, "\n[2] deleting every other file to fragment it...\n"); + + for (i = 0; i < DF_FILES; i += 2) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + unlink(path); + } + + df_mapfill(&dstart, &dblocks); + df_showmap("map, fragmented (# used, . free):", dblocks, + &freeblk, &largest); + + /* Bigger than any single hole, but comfortably inside the total free + * space -- so it can only be satisfied once the holes are coalesced. + */ + + want = largest + (freeblk - largest) / 2; + + syslog(LOG_INFO, "\n[3] requesting a %lu-block (%lu KB) contiguous file\n", + (unsigned long)want, (unsigned long)(want * erasesize / 1024)); + + ret = df_try_alloc(want, erasesize); + syslog(LOG_INFO, " result: %s\n", + ret < 0 ? "FAILED -ENOSPC (free space is not contiguous)" : "ok"); + + syslog(LOG_INFO, "\n[4] compacting...\n"); + + clock_gettime(CLOCK_MONOTONIC, &t0); + ret = run_defrag(0, &res); + clock_gettime(CLOCK_MONOTONIC, &t1); + + ms = (t1.tv_sec - t0.tv_sec) * 1000 + (t1.tv_nsec - t0.tv_nsec) / 1000000; + + if (ret < 0) + { + syslog(LOG_INFO, " defrag failed: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, " elapsed : %ld ms\n", ms); + syslog(LOG_INFO, " extents relocated: %lu\n", + (unsigned long)res.extents_moved); + syslog(LOG_INFO, " blocks reclaimed : %lu\n", + (unsigned long)res.blocks_reclaimed); + syslog(LOG_INFO, " largest free run : %lu bytes (%lu blocks)\n", + (unsigned long)res.largest_free_run, + (unsigned long)(res.largest_free_run / erasesize)); + syslog(LOG_INFO, " stop reason : %d (%s)\n", res.reason, + res.reason == XIPFS_DEFRAG_DONE ? "done" : + res.reason == XIPFS_DEFRAG_BLOCKED_OPEN ? "blocked by an open " + "file" : + res.reason == XIPFS_DEFRAG_BLOCKED_PINS ? "blocked by a live " + "XIP mapping" : + "other"); + + df_mapfill(&dstart, &dblocks); + df_showmap("map after defrag:", dblocks, NULL, NULL); + + want = res.largest_free_run / erasesize; + + syslog(LOG_INFO, "\n[5] retrying, sized to the reported largest run" + " (%lu blocks)\n", (unsigned long)want); + ret = df_try_alloc(want, erasesize); + syslog(LOG_INFO, " result: %s\n", + ret < 0 ? "still failed" : "OK -- allocation now fits"); + unlink(MOUNTPT "/bigalloc"); + + syslog(LOG_INFO, "\n[6] verifying every relocated file byte-for-byte\n"); + + ret = 0; + for (i = 1; i < DF_FILES; i += 2) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + if (!verify_file(path, 2000, (uint8_t)(i + 1))) + { + syslog(LOG_INFO, " MISMATCH in %s\n", path); + ret = -1; + } + } + + syslog(LOG_INFO, " %s\n", ret == 0 ? "all survivors intact" : "FAILED"); + + syslog(LOG_INFO, "\n[7] remounting to prove it is durable\n"); + if (remount() < 0) + { + syslog(LOG_INFO, " remount FAILED\n"); + return EXIT_FAILURE; + } + + ret = 0; + for (i = 1; i < DF_FILES; i += 2) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + if (!verify_file(path, 2000, (uint8_t)(i + 1))) + { + ret = -1; + } + } + + syslog(LOG_INFO, " %s\n", + ret == 0 ? "all files intact after remount" : "FAILED"); + + syslog(LOG_INFO, "\n=== defrag complete ===\n"); + return EXIT_SUCCESS; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct xipfs_extent_info_s info; + FAR char *args[3]; + char seed[2][8]; + pid_t pid[2]; + int status; + int ret; + int i; + + if (argc > 1 && strcmp(argv[1], "bench") == 0) + { + return bench_main(); + } + + if (argc > 1 && strcmp(argv[1], "defrag") == 0) + { + return defrag_main(); + } + + syslog(LOG_INFO, + "\n=== NXFLAT module executed in place from xipfs ===\n\n"); + + ret = stage_module(); + if (ret < 0) + { + return EXIT_FAILURE; + } + + ret = extent_info_path(MODPATH, &info); + if (ret < 0) + { + syslog(LOG_INFO, "ERROR: cannot query extent: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, "extent: block %" PRIu32 " x%" PRIu32 ", size %" PRIu32 + ", flash addr 0x%08lx\n", + info.start_block, info.nblocks, info.size, + (unsigned long)info.xipaddr); + + if (info.xipaddr == 0) + { + syslog(LOG_INFO, + "ERROR: the file system cannot expose a flash pointer, so the " + "module would have to be copied to RAM. Does the MTD driver " + "answer BIOC_XIPBASE?\n"); + return EXIT_FAILURE; + } + + /* Launch two instances. They overlap deliberately: each fills its array, + * sleeps, then checks it. Shared data would show up as a mismatch. + */ + + syslog(LOG_INFO, "\nrunning two concurrent instances...\n\n"); + + for (i = 0; i < 2; i++) + { + snprintf(seed[i], sizeof(seed[i]), "%d", i + 1); + + args[0] = (FAR char *)"xipmod"; Review Comment: we need to remove the const here from the string literal. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
