Message ID | a8686efcbad3c445d3e5bde054c04a58877915f1.1601960644.git.thehajime@gmail.com |
---|---|
State | Not Applicable |
Headers | show |
Series | [RFC,v7,01/21] um: split build in kernel and host parts | expand |
On 06/10/2020 10:44, Hajime Tazaki wrote: > This commit adds block device support for nommu mode, and also added > several host utilities to run a simple test program > (tools/um/test/disk.c) successfully. > > Signed-off-by: Hajime Tazaki <thehajime@gmail.com> > --- > arch/um/include/asm/xor.h | 3 +- > arch/um/include/shared/as-layout.h | 1 + > arch/um/kernel/um_arch.c | 5 + > arch/um/nommu/include/uapi/asm/host_ops.h | 7 + > arch/um/nommu/include/uapi/asm/unistd.h | 2 + > arch/um/nommu/um/Kconfig | 8 + > arch/um/nommu/um/setup.c | 12 + > tools/um/Targets | 2 + > tools/um/include/lkl.h | 206 ++++++++++ > tools/um/include/lkl_host.h | 1 + > tools/um/lib/Build | 1 + > tools/um/lib/fs.c | 461 ++++++++++++++++++++++ > tools/um/lib/posix-host.c | 1 + > tools/um/lib/utils.c | 3 + > tools/um/tests/Build | 1 + > tools/um/tests/cla.c | 159 ++++++++ > tools/um/tests/cla.h | 33 ++ > tools/um/tests/disk.c | 168 ++++++++ > tools/um/tests/disk.sh | 70 ++++ > tools/um/tests/run.py | 2 + > 20 files changed, 1145 insertions(+), 1 deletion(-) > create mode 100644 tools/um/lib/fs.c > create mode 100644 tools/um/tests/cla.c > create mode 100644 tools/um/tests/cla.h > create mode 100644 tools/um/tests/disk.c > create mode 100755 tools/um/tests/disk.sh > > diff --git a/arch/um/include/asm/xor.h b/arch/um/include/asm/xor.h > index 36b33d62a35d..a5ea458a1ae9 100644 > --- a/arch/um/include/asm/xor.h > +++ b/arch/um/include/asm/xor.h > @@ -4,4 +4,5 @@ > > /* pick an arbitrary one - measuring isn't possible with inf-cpu */ > #define XOR_SELECT_TEMPLATE(x) \ > - (time_travel_mode == TT_MODE_INFCPU ? &xor_block_8regs : NULL) > + (time_travel_mode == TT_MODE_INFCPU || (!IS_ENABLED(CONFIG_MMU)) ? \ > + &xor_block_8regs : NULL) > diff --git a/arch/um/include/shared/as-layout.h b/arch/um/include/shared/as-layout.h > index 5f286ef2721b..4423437a5ace 100644 > --- a/arch/um/include/shared/as-layout.h > +++ b/arch/um/include/shared/as-layout.h > @@ -57,6 +57,7 @@ extern unsigned long host_task_size; > > extern int linux_main(int argc, char **argv); > extern void uml_finishsetup(void); > +extern void uml_set_args(char *args); > > struct siginfo; > extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *); > diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c > index e2cb76c03b25..da6e06cf2808 100644 > --- a/arch/um/kernel/um_arch.c > +++ b/arch/um/kernel/um_arch.c > @@ -41,6 +41,11 @@ static void __init add_arg(char *arg) > strcat(command_line, arg); > } > > +void __init uml_set_args(char *args) > +{ > + strcat(command_line, args); > +} > + > /* > * These fields are initialized at boot time and not changed. > * XXX This structure is used only in the non-SMP case. Maybe this > diff --git a/arch/um/nommu/include/uapi/asm/host_ops.h b/arch/um/nommu/include/uapi/asm/host_ops.h > index 0811494c080c..1b2507502969 100644 > --- a/arch/um/nommu/include/uapi/asm/host_ops.h > +++ b/arch/um/nommu/include/uapi/asm/host_ops.h > @@ -15,6 +15,11 @@ struct lkl_jmp_buf { > * > * These operations must be provided by a host library or by the application > * itself. > + * > + * @um_devices - string containg the list of UML devices in command line > + * format. This string is appended to the kernel command line and > + * is provided here for convenience to be implemented by the host library. > + * > * @print - optional operation that receives console messages > * @panic - called during a kernel panic > * > @@ -65,6 +70,8 @@ struct lkl_jmp_buf { > * > */ > struct lkl_host_operations { > + const char *um_devices; > + > void (*print)(const char *str, int len); > void (*panic)(void); > > diff --git a/arch/um/nommu/include/uapi/asm/unistd.h b/arch/um/nommu/include/uapi/asm/unistd.h > index 1762ebb829f5..320762099a62 100644 > --- a/arch/um/nommu/include/uapi/asm/unistd.h > +++ b/arch/um/nommu/include/uapi/asm/unistd.h > @@ -3,6 +3,8 @@ > #define __UM_NOMMU_UAPI_UNISTD_H > > #define __ARCH_WANT_NEW_STAT > +#define __ARCH_WANT_SET_GET_RLIMIT > + > #include <asm/bitsperlong.h> > > #if __BITS_PER_LONG == 64 > diff --git a/arch/um/nommu/um/Kconfig b/arch/um/nommu/um/Kconfig > index 20b3eaccb6f0..c6a3f472fe75 100644 > --- a/arch/um/nommu/um/Kconfig > +++ b/arch/um/nommu/um/Kconfig > @@ -4,6 +4,10 @@ config UML_NOMMU > select UACCESS_MEMCPY > select ARCH_THREAD_STACK_ALLOCATOR > select ARCH_HAS_SYSCALL_WRAPPER > + select VFAT_FS > + select NLS_CODEPAGE_437 > + select NLS_ISO8859_1 > + select BTRFS_FS > > config 64BIT > bool > @@ -35,3 +39,7 @@ config STACKTRACE_SUPPORT > config PRINTK_TIME > bool > default y > + > +config RAID6_PQ_BENCHMARK > + bool > + default n Why are we touching this? I thought this is already defined in lib/Kconfig? Brgds, A. > diff --git a/arch/um/nommu/um/setup.c b/arch/um/nommu/um/setup.c > index 922188690139..d71d61979ad6 100644 > --- a/arch/um/nommu/um/setup.c > +++ b/arch/um/nommu/um/setup.c > @@ -45,13 +45,25 @@ static void __init *lkl_run_kernel(void *arg) > return NULL; > } > > +static char _cmd_line[COMMAND_LINE_SIZE]; > int __init lkl_start_kernel(struct lkl_host_operations *ops, > const char *fmt, ...) > { > + va_list ap; > int ret; > > lkl_ops = ops; > > + va_start(ap, fmt); > + ret = vsnprintf(_cmd_line, COMMAND_LINE_SIZE, fmt, ap); > + va_end(ap); > + > + if (ops->um_devices) > + strscpy(_cmd_line + ret, ops->um_devices, > + COMMAND_LINE_SIZE - ret); > + > + uml_set_args(_cmd_line); > + > init_sem = lkl_ops->sem_alloc(0); > if (!init_sem) > return -ENOMEM; > diff --git a/tools/um/Targets b/tools/um/Targets > index 2bb90381843c..f5f8ec4b9dbb 100644 > --- a/tools/um/Targets > +++ b/tools/um/Targets > @@ -1,10 +1,12 @@ > ifeq ($(UMMODE),library) > progs-y += tests/boot > +progs-y += tests/disk > else > progs-y += uml/linux > endif > > LDFLAGS_boot-y := -pie > +LDFLAGS_disk-y := -pie > > LDLIBS := -lrt -lpthread -lutil > LDFLAGS_linux-y := -no-pie -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc > diff --git a/tools/um/include/lkl.h b/tools/um/include/lkl.h > index 707e01b64a70..4a9d874bfbf0 100644 > --- a/tools/um/include/lkl.h > +++ b/tools/um/include/lkl.h > @@ -113,6 +113,16 @@ static inline long lkl_sys_creat(const char *file, int mode) > } > #endif > > +#ifdef __lkl__NR_faccessat > +/** > + * lkl_sys_access - wrapper for lkl_sys_faccessat > + */ > +static inline long lkl_sys_access(const char *file, int mode) > +{ > + return lkl_sys_faccessat(LKL_AT_FDCWD, file, mode); > +} > +#endif > + > #ifdef __lkl__NR_mkdirat > /** > * lkl_sys_mkdir - wrapper for lkl_sys_mkdirat > @@ -123,6 +133,36 @@ static inline long lkl_sys_mkdir(const char *path, mode_t mode) > } > #endif > > +#ifdef __lkl__NR_unlinkat > +/** > + * lkl_sys_rmdir - wrapper for lkl_sys_unlinkrat > + */ > +static inline long lkl_sys_rmdir(const char *path) > +{ > + return lkl_sys_unlinkat(LKL_AT_FDCWD, path, LKL_AT_REMOVEDIR); > +} > +#endif > + > +#ifdef __lkl__NR_unlinkat > +/** > + * lkl_sys_unlink - wrapper for lkl_sys_unlinkat > + */ > +static inline long lkl_sys_unlink(const char *path) > +{ > + return lkl_sys_unlinkat(LKL_AT_FDCWD, path, 0); > +} > +#endif > + > +#ifdef __lkl__NR_mknodat > +/** > + * lkl_sys_mknod - wrapper for lkl_sys_mknodat > + */ > +static inline long lkl_sys_mknod(const char *path, mode_t mode, dev_t dev) > +{ > + return lkl_sys_mknodat(LKL_AT_FDCWD, path, mode, dev); > +} > +#endif > + > #ifdef __lkl__NR_epoll_create1 > /** > * lkl_sys_epoll_create - wrapper for lkl_sys_epoll_create1 > @@ -144,6 +184,172 @@ static inline long lkl_sys_epoll_wait(int fd, struct lkl_epoll_event *ev, > } > #endif > > +/** > + * struct lkl_dev_blk_ops - block device host operations, defined in lkl_host.h. > + */ > +struct lkl_dev_blk_ops; > + > +/** > + * lkl_disk - host disk handle > + * > + * @dev - a pointer to private information for this disk backend > + * @fd - a POSIX file descriptor that can be used by preadv/pwritev > + * @handle - an NT file handle that can be used by ReadFile/WriteFile > + */ > +struct lkl_disk { > + void *dev; > + union { > + int fd; > + void *handle; > + }; > + struct lkl_dev_blk_ops *ops; > +}; > + > +/** > + * lkl_disk_add - add a new disk > + * > + * @disk - the host disk handle > + * @returns a disk id (0 is valid) or a strictly negative value in case of error > + */ > +int lkl_disk_add(struct lkl_disk *disk); > + > +/** > + * lkl_disk_remove - remove a disk > + * > + * This function makes a cleanup of the @disk's private information > + * that was initialized by lkl_disk_add before. > + * > + * @disk - the host disk handle > + */ > +int lkl_disk_remove(struct lkl_disk disk); > + > +/** > + * lkl_encode_dev_from_sysfs_blkdev - extract device id from sysfs > + * > + * This function returns the device id for the given sysfs dev node. > + * The content of the node has to be in the form 'MAJOR:MINOR'. > + * Also, this function expects an absolute path which means that sysfs > + * already has to be mounted at the given path > + * > + * @sysfs_path - absolute path to the sysfs dev node > + * @pdevid - pointer to memory where dev id will be returned > + * @returns - 0 on success, a negative value on error > + */ > +int lkl_encode_dev_from_sysfs(const char *sysfs_path, uint32_t *pdevid); > + > +/** > + * lkl_mount_dev - mount a disk > + * > + * This functions creates a device file for the given disk, creates a mount > + * point and mounts the device over the mount point. > + * > + * @disk_id - the disk id identifying the disk to be mounted > + * @part - disk partition or zero for full disk > + * @fs_type - filesystem type > + * @flags - mount flags > + * @opts - additional filesystem specific mount options > + * @mnt_str - a string that will be filled by this function with the path where > + * the filesystem has been mounted > + * @mnt_str_len - size of mnt_str > + * @returns - 0 on success, a negative value on error > + */ > +long lkl_mount_dev(unsigned int disk_id, unsigned int part, const char *fs_type, > + int flags, const char *opts, > + char *mnt_str, unsigned int mnt_str_len); > + > +/** > + * lkl_umount_dev - umount a disk > + * > + * This functions umounts the given disks and removes the device file and the > + * mount point. > + * > + * @disk_id - the disk id identifying the disk to be mounted > + * @part - disk partition or zero for full disk > + * @flags - umount flags > + * @timeout_ms - timeout to wait for the kernel to flush closed files so that > + * umount can succeed > + * @returns - 0 on success, a negative value on error > + */ > +long lkl_umount_dev(unsigned int disk_id, unsigned int part, int flags, > + long timeout_ms); > + > +/** > + * lkl_umount_timeout - umount filesystem with timeout > + * > + * @path - the path to unmount > + * @flags - umount flags > + * @timeout_ms - timeout to wait for the kernel to flush closed files so that > + * umount can succeed > + * @returns - 0 on success, a negative value on error > + */ > +long lkl_umount_timeout(char *path, int flags, long timeout_ms); > + > +/** > + * lkl_opendir - open a directory > + * > + * @path - directory path > + * @err - pointer to store the error in case of failure > + * @returns - a handle to be used when calling lkl_readdir > + */ > +struct lkl_dir *lkl_opendir(const char *path, int *err); > + > +/** > + * lkl_fdopendir - open a directory > + * > + * @fd - file descriptor > + * @err - pointer to store the error in case of failure > + * @returns - a handle to be used when calling lkl_readdir > + */ > +struct lkl_dir *lkl_fdopendir(int fd, int *err); > + > +/** > + * lkl_rewinddir - reset directory stream > + * > + * @dir - the directory handler as returned by lkl_opendir > + */ > +void lkl_rewinddir(struct lkl_dir *dir); > + > +/** > + * lkl_closedir - close the directory > + * > + * @dir - the directory handler as returned by lkl_opendir > + */ > +int lkl_closedir(struct lkl_dir *dir); > + > +/** > + * lkl_readdir - get the next available entry of the directory > + * > + * @dir - the directory handler as returned by lkl_opendir > + * @returns - a lkl_dirent64 entry or NULL if the end of the directory stream is > + * reached or if an error occurred; check lkl_errdir() to distinguish between > + * errors or end of the directory stream > + */ > +struct lkl_linux_dirent64 *lkl_readdir(struct lkl_dir *dir); > + > +/** > + * lkl_errdir - checks if an error occurred during the last lkl_readdir call > + * > + * @dir - the directory handler as returned by lkl_opendir > + * @returns - 0 if no error occurred, or a negative value otherwise > + */ > +int lkl_errdir(struct lkl_dir *dir); > + > +/** > + * lkl_dirfd - gets the file descriptor associated with the directory handle > + * > + * @dir - the directory handle as returned by lkl_opendir > + * @returns - a positive value,which is the LKL file descriptor associated with > + * the directory handle, or a negative value otherwise > + */ > +int lkl_dirfd(struct lkl_dir *dir); > + > +/** > + * lkl_mount_fs - mount a file system type like proc, sys > + * @fstype - file system type. e.g. proc, sys > + * @returns - 0 on success. 1 if it's already mounted. negative on failure. > + */ > +int lkl_mount_fs(char *fstype); > + > #ifdef __cplusplus > } > #endif > diff --git a/tools/um/include/lkl_host.h b/tools/um/include/lkl_host.h > index 85e80eb4ad0d..12dd95616e43 100644 > --- a/tools/um/include/lkl_host.h > +++ b/tools/um/include/lkl_host.h > @@ -10,6 +10,7 @@ extern "C" { > #include <lkl.h> > > extern struct lkl_host_operations lkl_host_ops; > +extern char lkl_um_devs[4096]; > > /** > * lkl_printf - print a message via the host print operation > diff --git a/tools/um/lib/Build b/tools/um/lib/Build > index dddff26a3b4e..29491b40746c 100644 > --- a/tools/um/lib/Build > +++ b/tools/um/lib/Build > @@ -4,3 +4,4 @@ CFLAGS_posix-host.o += -D_FILE_OFFSET_BITS=64 > liblinux-$(CONFIG_UMMODE_LIB) += utils.o > liblinux-$(CONFIG_UMMODE_LIB) += posix-host.o > liblinux-$(CONFIG_UMMODE_LIB) += jmp_buf.o > +liblinux-$(CONFIG_UMMODE_LIB) += fs.o > diff --git a/tools/um/lib/fs.c b/tools/um/lib/fs.c > new file mode 100644 > index 000000000000..948aac9730c2 > --- /dev/null > +++ b/tools/um/lib/fs.c > @@ -0,0 +1,461 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <stdarg.h> > +#include <stdio.h> > +#include <string.h> > +#include <stdlib.h> > +#include <lkl_host.h> > + > +#define MAX_FSTYPE_LEN 50 > + > +static struct lkl_disk *lkl_disks[16]; > +static int registered_blk_dev_idx; > + > +static int lkl_disk_um_add(struct lkl_disk *disk, const char *blkparams) > +{ > + /* concat strings */ > + snprintf(lkl_um_devs + strlen(lkl_um_devs), sizeof(lkl_um_devs), > + " ubd%d=%s", registered_blk_dev_idx, blkparams); > + > + return registered_blk_dev_idx++; > +} > + > +int lkl_disk_add(struct lkl_disk *disk) > +{ > + int ret = -1; > + > + ret = lkl_disk_um_add(disk, disk->dev); > + > + lkl_disks[ret] = disk; > + > + return ret; > +} > + > +int lkl_disk_remove(struct lkl_disk disk) > +{ > + /* FIXME */ > + return 0; > +} > + > +int lkl_mount_fs(char *fstype) > +{ > + char dir[MAX_FSTYPE_LEN+2] = "/"; > + int flags = 0, ret = 0; > + > + strncat(dir, fstype, MAX_FSTYPE_LEN); > + > + /* Create with regular umask */ > + ret = lkl_sys_mkdir(dir, 0xff); > + if (ret && ret != -LKL_EEXIST) { > + lkl_perror("mount_fs mkdir", ret); > + return ret; > + } > + > + /* We have no use for nonzero flags right now */ > + ret = lkl_sys_mount("none", dir, fstype, flags, NULL); > + if (ret && ret != -LKL_EBUSY) { > + lkl_sys_rmdir(dir); > + return ret; > + } > + > + if (ret == -LKL_EBUSY) > + return 1; > + return 0; > +} > + > +static uint32_t new_encode_dev(unsigned int major, unsigned int minor) > +{ > + return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12); > +} > + > +static int startswith(const char *str, const char *pre) > +{ > + return strncmp(pre, str, strlen(pre)) == 0; > +} > + > +static int get_node_with_prefix(const char *path, const char *prefix, > + char *result, unsigned int result_len) > +{ > + struct lkl_dir *dir = NULL; > + struct lkl_linux_dirent64 *dirent; > + int ret; > + > + dir = lkl_opendir(path, &ret); > + if (!dir) > + return ret; > + > + ret = -LKL_ENOENT; > + > + while ((dirent = lkl_readdir(dir))) { > + if (startswith(dirent->d_name, prefix)) { > + if (strlen(dirent->d_name) + 1 > result_len) { > + ret = -LKL_ENOMEM; > + break; > + } > + memcpy(result, dirent->d_name, strlen(dirent->d_name)); > + result[strlen(dirent->d_name)] = '\0'; > + ret = 0; > + break; > + } > + } > + > + lkl_closedir(dir); > + > + return ret; > +} > + > +int lkl_encode_dev_from_sysfs(const char *sysfs_path, uint32_t *pdevid) > +{ > + int ret; > + long fd; > + int major, minor; > + char buf[16] = { 0, }; > + char *bufptr; > + > + fd = lkl_sys_open(sysfs_path, LKL_O_RDONLY, 0); > + if (fd < 0) > + return fd; > + > + ret = lkl_sys_read(fd, buf, sizeof(buf)); > + if (ret < 0) > + goto out_close; > + > + if (ret == sizeof(buf)) { > + ret = -LKL_ENOBUFS; > + goto out_close; > + } > + > + bufptr = strchr(buf, ':'); > + if (bufptr == NULL) { > + ret = -LKL_EINVAL; > + goto out_close; > + } > + bufptr[0] = '\0'; > + bufptr++; > + > + major = atoi(buf); > + minor = atoi(bufptr); > + > + *pdevid = new_encode_dev(major, minor); > + ret = 0; > + > +out_close: > + lkl_sys_close(fd); > + > + return ret; > +} > + > +#define SYSFS_DEV_UMBLK_CMDLINE_PATH \ > + "/sysfs/devices/platform/uml-blkdev.%d" > + > +struct abuf { > + char *mem, *ptr; > + unsigned int len; > +}; > + > +static int snprintf_append(struct abuf *buf, const char *fmt, ...) > +{ > + int ret; > + va_list args; > + > + if (!buf->ptr) > + buf->ptr = buf->mem; > + > + va_start(args, fmt); > + ret = vsnprintf(buf->ptr, buf->len - (buf->ptr - buf->mem), fmt, args); > + va_end(args); > + > + if (ret < 0 || (ret >= (int)(buf->len - (buf->ptr - buf->mem)))) > + return -LKL_ENOMEM; > + > + buf->ptr += ret; > + > + return 0; > +} > + > +static int __lkl_get_blkdev(int disk_id, unsigned int part, uint32_t *pdevid, > + const char *sysfs_path_fmt, const char *drv_prefix, > + const char *disk_prefix) > +{ > + char sysfs_path[LKL_PATH_MAX]; > + char drv_name[LKL_PATH_MAX]; > + char disk_name[LKL_PATH_MAX]; > + struct abuf sysfs_path_buf = { > + .mem = sysfs_path, > + .len = sizeof(sysfs_path), > + }; > + int ret; > + > + if (disk_id < 0) > + return -LKL_EINVAL; > + > + ret = lkl_mount_fs("sysfs"); > + if (ret < 0) > + return ret; > + > + ret = snprintf_append(&sysfs_path_buf, sysfs_path_fmt, disk_id); > + if (ret) > + return ret; > + > + ret = get_node_with_prefix(sysfs_path, drv_prefix, drv_name, > + sizeof(drv_name)); > + if (ret) > + return ret; > + > + ret = snprintf_append(&sysfs_path_buf, "/%s/block", drv_name); > + if (ret) > + return ret; > + > + ret = get_node_with_prefix(sysfs_path, disk_prefix, disk_name, > + sizeof(disk_name)); > + if (ret) > + return ret; > + > + if (!part) > + ret = snprintf_append(&sysfs_path_buf, "/%s/dev", disk_name); > + else > + ret = snprintf_append(&sysfs_path_buf, "/%s/%s%d/dev", > + disk_name, disk_name, part); > + if (ret) > + return ret; > + > + return lkl_encode_dev_from_sysfs(sysfs_path, pdevid); > +} > + > +int lkl_get_blkdev(int disk_id, unsigned int part, uint32_t *pdevid) > +{ > + char *fmt; > + > + fmt = SYSFS_DEV_UMBLK_CMDLINE_PATH; > + return __lkl_get_blkdev(disk_id, part, pdevid, fmt, "", "ubd"); > +} > + > +long lkl_mount_dev(unsigned int disk_id, unsigned int part, > + const char *fs_type, int flags, > + const char *data, char *mnt_str, unsigned int mnt_str_len) > +{ > + char dev_str[] = { "/dev/xxxxxxxx" }; > + unsigned int dev; > + int err; > + char _data[4096]; /* FIXME: PAGE_SIZE is not exported by LKL */ > + > + if (mnt_str_len < sizeof(dev_str)) > + return -LKL_ENOMEM; > + > + err = lkl_get_blkdev(disk_id, part, &dev); > + if (err < 0) > + return err; > + > + snprintf(dev_str, sizeof(dev_str), "/dev/%08x", dev); > + snprintf(mnt_str, mnt_str_len, "/mnt/%08x", dev); > + > + err = lkl_sys_access("/dev", LKL_S_IRWXO); > + if (err < 0) { > + if (err == -LKL_ENOENT) > + err = lkl_sys_mkdir("/dev", 0700); > + if (err < 0) > + return err; > + } > + > + err = lkl_sys_mknod(dev_str, LKL_S_IFBLK | 0600, dev); > + if (err < 0) > + return err; > + > + err = lkl_sys_access("/mnt", LKL_S_IRWXO); > + if (err < 0) { > + if (err == -LKL_ENOENT) > + err = lkl_sys_mkdir("/mnt", 0700); > + if (err < 0) > + return err; > + } > + > + err = lkl_sys_mkdir(mnt_str, 0700); > + if (err < 0) { > + lkl_sys_unlink(dev_str); > + return err; > + } > + > + /* kernel always copies a full page */ > + if (data) { > + strncpy(_data, data, sizeof(_data)); > + _data[sizeof(_data) - 1] = 0; > + } else { > + _data[0] = 0; > + } > + > + err = lkl_sys_mount(dev_str, mnt_str, (char *)fs_type, flags, _data); > + if (err < 0) { > + lkl_sys_unlink(dev_str); > + lkl_sys_rmdir(mnt_str); > + return err; > + } > + > + return 0; > +} > + > +long lkl_umount_timeout(char *path, int flags, long timeout_ms) > +{ > + long incr = 10000000; /* 10 ms */ > + struct lkl_timespec ts = { > + .tv_sec = 0, > + .tv_nsec = incr, > + }; > + long err; > + > + do { > + err = lkl_sys_umount(path, flags); > + if (err == -LKL_EBUSY) { > + lkl_sys_nanosleep((struct __lkl__kernel_timespec *)&ts, > + NULL); > + timeout_ms -= incr / 1000000; > + } > + } while (err == -LKL_EBUSY && timeout_ms > 0); > + > + return err; > +} > + > +long lkl_umount_dev(unsigned int disk_id, unsigned int part, int flags, > + long timeout_ms) > +{ > + char dev_str[] = { "/dev/xxxxxxxx" }; > + char mnt_str[] = { "/mnt/xxxxxxxx" }; > + unsigned int dev; > + int err; > + > + err = lkl_get_blkdev(disk_id, part, &dev); > + if (err < 0) > + return err; > + > + snprintf(dev_str, sizeof(dev_str), "/dev/%08x", dev); > + snprintf(mnt_str, sizeof(mnt_str), "/mnt/%08x", dev); > + > + err = lkl_umount_timeout(mnt_str, flags, timeout_ms); > + if (err) > + return err; > + > + err = lkl_sys_unlink(dev_str); > + if (err) > + return err; > + > + return lkl_sys_rmdir(mnt_str); > +} > + > +struct lkl_dir { > + int fd; > + char buf[1024]; > + char *pos; > + int len; > +}; > + > +static struct lkl_dir *lkl_dir_alloc(int *err) > +{ > + struct lkl_dir *dir = lkl_host_ops.mem_alloc(sizeof(struct lkl_dir)); > + > + if (!dir) { > + *err = -LKL_ENOMEM; > + return NULL; > + } > + > + dir->len = 0; > + dir->pos = NULL; > + > + return dir; > +} > + > +struct lkl_dir *lkl_opendir(const char *path, int *err) > +{ > + struct lkl_dir *dir = lkl_dir_alloc(err); > + > + if (!dir) { > + *err = -LKL_ENOMEM; > + return NULL; > + } > + > + dir->fd = lkl_sys_open(path, LKL_O_RDONLY | LKL_O_DIRECTORY, 0); > + if (dir->fd < 0) { > + *err = dir->fd; > + lkl_host_ops.mem_free(dir); > + return NULL; > + } > + > + *err = 0; > + > + return dir; > +} > + > +struct lkl_dir *lkl_fdopendir(int fd, int *err) > +{ > + struct lkl_dir *dir = lkl_dir_alloc(err); > + > + if (!dir) > + return NULL; > + > + dir->fd = fd; > + > + return dir; > +} > + > +void lkl_rewinddir(struct lkl_dir *dir) > +{ > + lkl_sys_lseek(dir->fd, 0, LKL_SEEK_SET); > + dir->len = 0; > + dir->pos = NULL; > +} > + > +int lkl_closedir(struct lkl_dir *dir) > +{ > + int ret; > + > + ret = lkl_sys_close(dir->fd); > + lkl_host_ops.mem_free(dir); > + > + return ret; > +} > + > +struct lkl_linux_dirent64 *lkl_readdir(struct lkl_dir *dir) > +{ > + struct lkl_linux_dirent64 *de; > + > + if (dir->len < 0) > + return NULL; > + > + if (!dir->pos || dir->pos - dir->buf >= dir->len) > + goto read_buf; > + > +return_de: > + de = (struct lkl_linux_dirent64 *)dir->pos; > + dir->pos += de->d_reclen; > + > + return de; > + > +read_buf: > + dir->pos = NULL; > + de = (struct lkl_linux_dirent64 *)dir->buf; > + dir->len = lkl_sys_getdents64(dir->fd, de, sizeof(dir->buf)); > + if (dir->len <= 0) > + return NULL; > + > + dir->pos = dir->buf; > + goto return_de; > +} > + > +int lkl_errdir(struct lkl_dir *dir) > +{ > + if (dir->len >= 0) > + return 0; > + > + return dir->len; > +} > + > +int lkl_dirfd(struct lkl_dir *dir) > +{ > + return dir->fd; > +} > + > +int lkl_set_fd_limit(unsigned int fd_limit) > +{ > + struct lkl_rlimit rlim = { > + .rlim_cur = fd_limit, > + .rlim_max = fd_limit, > + }; > + return lkl_sys_setrlimit(LKL_RLIMIT_NOFILE, &rlim); > +} > diff --git a/tools/um/lib/posix-host.c b/tools/um/lib/posix-host.c > index b6b5b2902254..8fd88031bf2b 100644 > --- a/tools/um/lib/posix-host.c > +++ b/tools/um/lib/posix-host.c > @@ -263,6 +263,7 @@ static void *tls_get(struct lkl_tls_key *key) > } > > struct lkl_host_operations lkl_host_ops = { > + .um_devices = lkl_um_devs, > .panic = panic, > .print = print, > .mem_alloc = (void *)malloc, > diff --git a/tools/um/lib/utils.c b/tools/um/lib/utils.c > index 4930479a8a35..ac65cd744a14 100644 > --- a/tools/um/lib/utils.c > +++ b/tools/um/lib/utils.c > @@ -4,6 +4,9 @@ > #include <string.h> > #include <lkl_host.h> > > +/* XXX: find a better place */ > +char lkl_um_devs[4096]; > + > static const char * const lkl_err_strings[] = { > "Success", > "Operation not permitted", > diff --git a/tools/um/tests/Build b/tools/um/tests/Build > index 564560486f98..1aa78f9ed7ef 100644 > --- a/tools/um/tests/Build > +++ b/tools/um/tests/Build > @@ -1,3 +1,4 @@ > boot-y += boot.o test.o > +disk-y += disk.o test.o cla.o > > CFLAGS_test.o += -Wno-implicit-fallthrough > diff --git a/tools/um/tests/cla.c b/tools/um/tests/cla.c > new file mode 100644 > index 000000000000..694e3a3822be > --- /dev/null > +++ b/tools/um/tests/cla.c > @@ -0,0 +1,159 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <stdio.h> > +#include <string.h> > +#include <errno.h> > +#include <stdlib.h> > +#ifdef __MINGW32__ > +#include <winsock2.h> > +#else > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <arpa/inet.h> > +#endif > + > +#include "cla.h" > + > +static int cl_arg_parse_bool(struct cl_arg *arg, const char *value) > +{ > + *((int *)arg->store) = 1; > + return 0; > +} > + > +static int cl_arg_parse_str(struct cl_arg *arg, const char *value) > +{ > + *((const char **)arg->store) = value; > + return 0; > +} > + > +static int cl_arg_parse_int(struct cl_arg *arg, const char *value) > +{ > + errno = 0; > + *((int *)arg->store) = strtol(value, NULL, 0); > + return errno == 0; > +} > + > +static int cl_arg_parse_str_set(struct cl_arg *arg, const char *value) > +{ > + const char **set = arg->set; > + int i; > + > + for (i = 0; set[i] != NULL; i++) { > + if (strcmp(set[i], value) == 0) { > + *((int *)arg->store) = i; > + return 0; > + } > + } > + > + return (-1); > +} > + > +static int cl_arg_parse_ipv4(struct cl_arg *arg, const char *value) > +{ > + unsigned int addr; > + > + if (!value) > + return (-1); > + > + addr = inet_addr(value); > + if (addr == INADDR_NONE) > + return (-1); > + *((unsigned int *)arg->store) = addr; > + return 0; > +} > + > +static cl_arg_parser_t parsers[] = { > + [CL_ARG_BOOL] = cl_arg_parse_bool, > + [CL_ARG_INT] = cl_arg_parse_int, > + [CL_ARG_STR] = cl_arg_parse_str, > + [CL_ARG_STR_SET] = cl_arg_parse_str_set, > + [CL_ARG_IPV4] = cl_arg_parse_ipv4, > +}; > + > +static struct cl_arg *find_short_arg(char name, struct cl_arg *args) > +{ > + struct cl_arg *arg; > + > + for (arg = args; arg->short_name != 0; arg++) { > + if (arg->short_name == name) > + return arg; > + } > + > + return NULL; > +} > + > +static struct cl_arg *find_long_arg(const char *name, struct cl_arg *args) > +{ > + struct cl_arg *arg; > + > + for (arg = args; arg->long_name; arg++) { > + if (strcmp(arg->long_name, name) == 0) > + return arg; > + } > + > + return NULL; > +} > + > +static void print_help(struct cl_arg *args) > +{ > + struct cl_arg *arg; > + > + fprintf(stderr, "usage:\n"); > + for (arg = args; arg->long_name; arg++) { > + fprintf(stderr, "-%c, --%-20s %s", arg->short_name, > + arg->long_name, arg->help); > + if (arg->type == CL_ARG_STR_SET) { > + const char **set = arg->set; > + > + fprintf(stderr, " [ "); > + while (*set != NULL) > + fprintf(stderr, "%s ", *(set++)); > + fprintf(stderr, "]"); > + } > + fprintf(stderr, "\n"); > + } > +} > + > +int cla_parse_args(int argc, const char **argv, struct cl_arg *args) > +{ > + int i; > + > + for (i = 1; i < argc; i++) { > + struct cl_arg *arg = NULL; > + cl_arg_parser_t parser; > + > + if (argv[i][0] == '-') { > + if (argv[i][1] != '-') > + arg = find_short_arg(argv[i][1], args); > + else > + arg = find_long_arg(&argv[i][2], args); > + } > + > + if (!arg) { > + fprintf(stderr, "unknown option '%s'\n", argv[i]); > + print_help(args); > + return (-1); > + } > + > + if (arg->type == CL_ARG_USER || arg->type >= CL_ARG_END) > + parser = arg->parser; > + else > + parser = parsers[arg->type]; > + > + if (!parser) { > + fprintf(stderr, "can't parse --'%s'/-'%c'\n", > + arg->long_name, args->short_name); > + return (-1); > + } > + > + if (parser(arg, argv[i + 1]) < 0) { > + fprintf(stderr, "can't parse '%s'\n", argv[i]); > + print_help(args); > + return (-1); > + } > + > + if (arg->has_arg) > + i++; > + } > + > + return 0; > +} > diff --git a/tools/um/tests/cla.h b/tools/um/tests/cla.h > new file mode 100644 > index 000000000000..3d879233681f > --- /dev/null > +++ b/tools/um/tests/cla.h > @@ -0,0 +1,33 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +#ifndef _LKL_TEST_CLA_H > +#define _LKL_TEST_CLA_H > + > +enum cl_arg_type { > + CL_ARG_USER = 0, > + CL_ARG_BOOL, > + CL_ARG_INT, > + CL_ARG_STR, > + CL_ARG_STR_SET, > + CL_ARG_IPV4, > + CL_ARG_END, > +}; > + > +struct cl_arg; > + > +typedef int (*cl_arg_parser_t)(struct cl_arg *arg, const char *value); > + > +struct cl_arg { > + const char *long_name; > + char short_name; > + const char *help; > + int has_arg; > + enum cl_arg_type type; > + void *store; > + void *set; > + cl_arg_parser_t parser; > +}; > + > +int cla_parse_args(int argc, const char **argv, struct cl_arg *args); > + > + > +#endif /* _LKL_TEST_CLA_H */ > diff --git a/tools/um/tests/disk.c b/tools/um/tests/disk.c > new file mode 100644 > index 000000000000..325934935e6a > --- /dev/null > +++ b/tools/um/tests/disk.c > @@ -0,0 +1,168 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <stdio.h> > +#include <unistd.h> > +#include <string.h> > +#include <time.h> > +#include <stdlib.h> > +#include <stdint.h> > +#include <lkl.h> > +#include <lkl_host.h> > +#include <sys/stat.h> > +#include <fcntl.h> > +#include <sys/ioctl.h> > + > +#include "test.h" > +#include "cla.h" > + > +static struct { > + int printk; > + const char *disk; > + const char *fstype; > + int partition; > +} cla; > + > +struct cl_arg args[] = { > + {"disk", 'd', "disk file to use", 1, CL_ARG_STR, &cla.disk}, > + {"partition", 'P', "partition to mount", 1, CL_ARG_INT, &cla.partition}, > + {"type", 't', "filesystem type", 1, CL_ARG_STR, &cla.fstype}, > + {0}, > +}; > + > + > +static struct lkl_disk disk; > +static int disk_id = -1; > + > +int lkl_test_disk_add(void) > +{ > + disk.fd = open(cla.disk, O_RDWR); > + if (disk.fd < 0) > + goto out_unlink; > + > + disk.ops = NULL; > + disk.dev = (char *)cla.disk; > + > + disk_id = lkl_disk_add(&disk); > + if (disk_id < 0) > + goto out_close; > + > + goto out; > + > +out_close: > + close(disk.fd); > + > +out_unlink: > + unlink(cla.disk); > + > +out: > + lkl_test_logf("disk fd/handle %x disk_id %d", disk.fd, disk_id); > + > + if (disk_id >= 0) > + return TEST_SUCCESS; > + > + return TEST_FAILURE; > +} > + > +int lkl_test_disk_remove(void) > +{ > + int ret; > + > + ret = lkl_disk_remove(disk); > + > + close(disk.fd); > + > + if (ret == 0) > + return TEST_SUCCESS; > + > + return TEST_FAILURE; > +} > + > + > +static char mnt_point[32]; > + > +LKL_TEST_CALL(mount_dev, lkl_mount_dev, 0, disk_id, cla.partition, cla.fstype, > + 0, NULL, mnt_point, sizeof(mnt_point)) > + > +static int lkl_test_umount_dev(void) > +{ > + long ret, ret2; > + > + ret = lkl_sys_chdir("/"); > + > + ret2 = lkl_umount_dev(disk_id, cla.partition, 0, 1000); > + > + lkl_test_logf("%ld %ld", ret, ret2); > + > + if (!ret && !ret2) > + return TEST_SUCCESS; > + > + return TEST_FAILURE; > +} > + > +struct lkl_dir *dir; > + > +static int lkl_test_opendir(void) > +{ > + int err; > + > + dir = lkl_opendir(mnt_point, &err); > + > + lkl_test_logf("lkl_opedir(%s) = %d %s\n", mnt_point, err, > + lkl_strerror(err)); > + > + if (err == 0) > + return TEST_SUCCESS; > + > + return TEST_FAILURE; > +} > + > +static int lkl_test_readdir(void) > +{ > + struct lkl_linux_dirent64 *de = lkl_readdir(dir); > + int wr = 0; > + > + while (de) { > + wr += lkl_test_logf("%s ", de->d_name); > + if (wr >= 70) { > + lkl_test_logf("\n"); > + wr = 0; > + break; > + } > + de = lkl_readdir(dir); > + } > + > + if (lkl_errdir(dir) == 0) > + return TEST_SUCCESS; > + > + return TEST_FAILURE; > +} > + > +LKL_TEST_CALL(closedir, lkl_closedir, 0, dir); > +LKL_TEST_CALL(chdir_mnt_point, lkl_sys_chdir, 0, mnt_point); > +LKL_TEST_CALL(start_kernel, lkl_start_kernel, 0, &lkl_host_ops, > + "mem=16M loglevel=8"); > +LKL_TEST_CALL(stop_kernel, lkl_sys_halt, 0); > + > +struct lkl_test tests[] = { > + LKL_TEST(disk_add), > + LKL_TEST(start_kernel), > + LKL_TEST(mount_dev), > + LKL_TEST(chdir_mnt_point), > + LKL_TEST(opendir), > + LKL_TEST(readdir), > + LKL_TEST(closedir), > + LKL_TEST(umount_dev), > + LKL_TEST(stop_kernel), > + LKL_TEST(disk_remove), > + > +}; > + > +int main(int argc, const char **argv) > +{ > + if (cla_parse_args(argc, argv, args) < 0) > + return (-1); > + > + lkl_host_ops.print = lkl_test_log; > + > + return lkl_test_run(tests, sizeof(tests)/sizeof(struct lkl_test), > + "disk %s", cla.fstype); > +} > diff --git a/tools/um/tests/disk.sh b/tools/um/tests/disk.sh > new file mode 100755 > index 000000000000..e2ec6cf69d4b > --- /dev/null > +++ b/tools/um/tests/disk.sh > @@ -0,0 +1,70 @@ > +#!/usr/bin/env bash > +# SPDX-License-Identifier: GPL-2.0 > + > +script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd) > + > +source $script_dir/test.sh > + > +function prepfs() > +{ > + set -e > + > + file=`mktemp` > + > + dd if=/dev/zero of=$file bs=1024 count=204800 > + > + yes | mkfs.$1 $file > + > + if ! [ -z $ANDROID_WDIR ]; then > + adb shell mkdir -p $ANDROID_WDIR > + adb push $file $ANDROID_WDIR > + rm $file > + file=$ANDROID_WDIR/$(basename $file) > + fi > + if ! [ -z $BSD_WDIR ]; then > + $MYSSH mkdir -p $BSD_WDIR > + ssh_copy $file $BSD_WDIR > + rm $file > + file=$BSD_WDIR/$(basename $file) > + fi > + > + export_vars file > +} > + > +function cleanfs() > +{ > + set -e > + > + if ! [ -z $ANDROID_WDIR ]; then > + adb shell rm $1 > + adb shell rm $ANDROID_WDIR/disk > + elif ! [ -z $BSD_WDIR ]; then > + $MYSSH rm $1 > + $MYSSH rm $BSD_WDIR/disk > + else > + rm $1 > + fi > +} > + > +if [ "$1" = "-t" ]; then > + shift > + fstype=$1 > + shift > +fi > + > +if [ -z "$fstype" ]; then > + fstype="ext4" > +fi > + > +if [ -z $(which mkfs.$fstype) ]; then > + lkl_test_plan 0 "disk $fstype" > + echo "no mkfs.$fstype command" > + exit 0 > +fi > + > +lkl_test_plan 1 "disk $fstype" > +lkl_test_run 1 prepfs $fstype > +lkl_test_exec $script_dir/disk -d $file -t $fstype $@ > +lkl_test_plan 1 "disk $fstype" > +lkl_test_run 1 cleanfs $file > + > diff --git a/tools/um/tests/run.py b/tools/um/tests/run.py > index c96ede90b6ad..97d6dedc217c 100755 > --- a/tools/um/tests/run.py > +++ b/tools/um/tests/run.py > @@ -50,6 +50,8 @@ mydir=os.path.dirname(os.path.realpath(__file__)) > > tests = [ > 'boot.sh', > + 'disk.sh -t ext4', > + 'disk.sh -t vfat', > ] > > parser = argparse.ArgumentParser(description='LKL test runner')
On Wed, 07 Oct 2020 23:17:31 +0900, Anton Ivanov wrote: > > diff --git a/arch/um/nommu/um/Kconfig b/arch/um/nommu/um/Kconfig > > index 20b3eaccb6f0..c6a3f472fe75 100644 > > --- a/arch/um/nommu/um/Kconfig > > +++ b/arch/um/nommu/um/Kconfig > > @@ -4,6 +4,10 @@ config UML_NOMMU > > select UACCESS_MEMCPY > > select ARCH_THREAD_STACK_ALLOCATOR > > select ARCH_HAS_SYSCALL_WRAPPER > > + select VFAT_FS > > + select NLS_CODEPAGE_437 > > + select NLS_ISO8859_1 > > + select BTRFS_FS > > config 64BIT > > bool > > @@ -35,3 +39,7 @@ config STACKTRACE_SUPPORT > > config PRINTK_TIME > > bool > > default y > > + > > +config RAID6_PQ_BENCHMARK > > + bool > > + default n > > Why are we touching this? I thought this is already defined in lib/Kconfig? With the scheduler which LKL implements, it has the same issue with what time-travel=inf-cpu has when CONFIG_BTRFS_FS is enable. I tried to follow the way of the commit d65197a (below), but if I added "depends on !RAID6_PQ_BENCHMARK" to config UMMODE_LIB, I got "error: recursive dependency detected!". https://github.com/thehajime/linux/commit/d65197ad52494bed3b5e64708281b8295f76c391#diff-c170aa964ad412630a2b5addf306ff14 Thus, to avoid this situation, I did the above additional config RAID6_PQ_BENCHMARK just for UMMODE_LIB. I plan to rework on Kconfig (UMMODE_LIB and !MMU) and will figure out more appropriate way. -- Hajime
diff --git a/arch/um/include/asm/xor.h b/arch/um/include/asm/xor.h index 36b33d62a35d..a5ea458a1ae9 100644 --- a/arch/um/include/asm/xor.h +++ b/arch/um/include/asm/xor.h @@ -4,4 +4,5 @@ /* pick an arbitrary one - measuring isn't possible with inf-cpu */ #define XOR_SELECT_TEMPLATE(x) \ - (time_travel_mode == TT_MODE_INFCPU ? &xor_block_8regs : NULL) + (time_travel_mode == TT_MODE_INFCPU || (!IS_ENABLED(CONFIG_MMU)) ? \ + &xor_block_8regs : NULL) diff --git a/arch/um/include/shared/as-layout.h b/arch/um/include/shared/as-layout.h index 5f286ef2721b..4423437a5ace 100644 --- a/arch/um/include/shared/as-layout.h +++ b/arch/um/include/shared/as-layout.h @@ -57,6 +57,7 @@ extern unsigned long host_task_size; extern int linux_main(int argc, char **argv); extern void uml_finishsetup(void); +extern void uml_set_args(char *args); struct siginfo; extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *); diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index e2cb76c03b25..da6e06cf2808 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -41,6 +41,11 @@ static void __init add_arg(char *arg) strcat(command_line, arg); } +void __init uml_set_args(char *args) +{ + strcat(command_line, args); +} + /* * These fields are initialized at boot time and not changed. * XXX This structure is used only in the non-SMP case. Maybe this diff --git a/arch/um/nommu/include/uapi/asm/host_ops.h b/arch/um/nommu/include/uapi/asm/host_ops.h index 0811494c080c..1b2507502969 100644 --- a/arch/um/nommu/include/uapi/asm/host_ops.h +++ b/arch/um/nommu/include/uapi/asm/host_ops.h @@ -15,6 +15,11 @@ struct lkl_jmp_buf { * * These operations must be provided by a host library or by the application * itself. + * + * @um_devices - string containg the list of UML devices in command line + * format. This string is appended to the kernel command line and + * is provided here for convenience to be implemented by the host library. + * * @print - optional operation that receives console messages * @panic - called during a kernel panic * @@ -65,6 +70,8 @@ struct lkl_jmp_buf { * */ struct lkl_host_operations { + const char *um_devices; + void (*print)(const char *str, int len); void (*panic)(void); diff --git a/arch/um/nommu/include/uapi/asm/unistd.h b/arch/um/nommu/include/uapi/asm/unistd.h index 1762ebb829f5..320762099a62 100644 --- a/arch/um/nommu/include/uapi/asm/unistd.h +++ b/arch/um/nommu/include/uapi/asm/unistd.h @@ -3,6 +3,8 @@ #define __UM_NOMMU_UAPI_UNISTD_H #define __ARCH_WANT_NEW_STAT +#define __ARCH_WANT_SET_GET_RLIMIT + #include <asm/bitsperlong.h> #if __BITS_PER_LONG == 64 diff --git a/arch/um/nommu/um/Kconfig b/arch/um/nommu/um/Kconfig index 20b3eaccb6f0..c6a3f472fe75 100644 --- a/arch/um/nommu/um/Kconfig +++ b/arch/um/nommu/um/Kconfig @@ -4,6 +4,10 @@ config UML_NOMMU select UACCESS_MEMCPY select ARCH_THREAD_STACK_ALLOCATOR select ARCH_HAS_SYSCALL_WRAPPER + select VFAT_FS + select NLS_CODEPAGE_437 + select NLS_ISO8859_1 + select BTRFS_FS config 64BIT bool @@ -35,3 +39,7 @@ config STACKTRACE_SUPPORT config PRINTK_TIME bool default y + +config RAID6_PQ_BENCHMARK + bool + default n diff --git a/arch/um/nommu/um/setup.c b/arch/um/nommu/um/setup.c index 922188690139..d71d61979ad6 100644 --- a/arch/um/nommu/um/setup.c +++ b/arch/um/nommu/um/setup.c @@ -45,13 +45,25 @@ static void __init *lkl_run_kernel(void *arg) return NULL; } +static char _cmd_line[COMMAND_LINE_SIZE]; int __init lkl_start_kernel(struct lkl_host_operations *ops, const char *fmt, ...) { + va_list ap; int ret; lkl_ops = ops; + va_start(ap, fmt); + ret = vsnprintf(_cmd_line, COMMAND_LINE_SIZE, fmt, ap); + va_end(ap); + + if (ops->um_devices) + strscpy(_cmd_line + ret, ops->um_devices, + COMMAND_LINE_SIZE - ret); + + uml_set_args(_cmd_line); + init_sem = lkl_ops->sem_alloc(0); if (!init_sem) return -ENOMEM; diff --git a/tools/um/Targets b/tools/um/Targets index 2bb90381843c..f5f8ec4b9dbb 100644 --- a/tools/um/Targets +++ b/tools/um/Targets @@ -1,10 +1,12 @@ ifeq ($(UMMODE),library) progs-y += tests/boot +progs-y += tests/disk else progs-y += uml/linux endif LDFLAGS_boot-y := -pie +LDFLAGS_disk-y := -pie LDLIBS := -lrt -lpthread -lutil LDFLAGS_linux-y := -no-pie -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc diff --git a/tools/um/include/lkl.h b/tools/um/include/lkl.h index 707e01b64a70..4a9d874bfbf0 100644 --- a/tools/um/include/lkl.h +++ b/tools/um/include/lkl.h @@ -113,6 +113,16 @@ static inline long lkl_sys_creat(const char *file, int mode) } #endif +#ifdef __lkl__NR_faccessat +/** + * lkl_sys_access - wrapper for lkl_sys_faccessat + */ +static inline long lkl_sys_access(const char *file, int mode) +{ + return lkl_sys_faccessat(LKL_AT_FDCWD, file, mode); +} +#endif + #ifdef __lkl__NR_mkdirat /** * lkl_sys_mkdir - wrapper for lkl_sys_mkdirat @@ -123,6 +133,36 @@ static inline long lkl_sys_mkdir(const char *path, mode_t mode) } #endif +#ifdef __lkl__NR_unlinkat +/** + * lkl_sys_rmdir - wrapper for lkl_sys_unlinkrat + */ +static inline long lkl_sys_rmdir(const char *path) +{ + return lkl_sys_unlinkat(LKL_AT_FDCWD, path, LKL_AT_REMOVEDIR); +} +#endif + +#ifdef __lkl__NR_unlinkat +/** + * lkl_sys_unlink - wrapper for lkl_sys_unlinkat + */ +static inline long lkl_sys_unlink(const char *path) +{ + return lkl_sys_unlinkat(LKL_AT_FDCWD, path, 0); +} +#endif + +#ifdef __lkl__NR_mknodat +/** + * lkl_sys_mknod - wrapper for lkl_sys_mknodat + */ +static inline long lkl_sys_mknod(const char *path, mode_t mode, dev_t dev) +{ + return lkl_sys_mknodat(LKL_AT_FDCWD, path, mode, dev); +} +#endif + #ifdef __lkl__NR_epoll_create1 /** * lkl_sys_epoll_create - wrapper for lkl_sys_epoll_create1 @@ -144,6 +184,172 @@ static inline long lkl_sys_epoll_wait(int fd, struct lkl_epoll_event *ev, } #endif +/** + * struct lkl_dev_blk_ops - block device host operations, defined in lkl_host.h. + */ +struct lkl_dev_blk_ops; + +/** + * lkl_disk - host disk handle + * + * @dev - a pointer to private information for this disk backend + * @fd - a POSIX file descriptor that can be used by preadv/pwritev + * @handle - an NT file handle that can be used by ReadFile/WriteFile + */ +struct lkl_disk { + void *dev; + union { + int fd; + void *handle; + }; + struct lkl_dev_blk_ops *ops; +}; + +/** + * lkl_disk_add - add a new disk + * + * @disk - the host disk handle + * @returns a disk id (0 is valid) or a strictly negative value in case of error + */ +int lkl_disk_add(struct lkl_disk *disk); + +/** + * lkl_disk_remove - remove a disk + * + * This function makes a cleanup of the @disk's private information + * that was initialized by lkl_disk_add before. + * + * @disk - the host disk handle + */ +int lkl_disk_remove(struct lkl_disk disk); + +/** + * lkl_encode_dev_from_sysfs_blkdev - extract device id from sysfs + * + * This function returns the device id for the given sysfs dev node. + * The content of the node has to be in the form 'MAJOR:MINOR'. + * Also, this function expects an absolute path which means that sysfs + * already has to be mounted at the given path + * + * @sysfs_path - absolute path to the sysfs dev node + * @pdevid - pointer to memory where dev id will be returned + * @returns - 0 on success, a negative value on error + */ +int lkl_encode_dev_from_sysfs(const char *sysfs_path, uint32_t *pdevid); + +/** + * lkl_mount_dev - mount a disk + * + * This functions creates a device file for the given disk, creates a mount + * point and mounts the device over the mount point. + * + * @disk_id - the disk id identifying the disk to be mounted + * @part - disk partition or zero for full disk + * @fs_type - filesystem type + * @flags - mount flags + * @opts - additional filesystem specific mount options + * @mnt_str - a string that will be filled by this function with the path where + * the filesystem has been mounted + * @mnt_str_len - size of mnt_str + * @returns - 0 on success, a negative value on error + */ +long lkl_mount_dev(unsigned int disk_id, unsigned int part, const char *fs_type, + int flags, const char *opts, + char *mnt_str, unsigned int mnt_str_len); + +/** + * lkl_umount_dev - umount a disk + * + * This functions umounts the given disks and removes the device file and the + * mount point. + * + * @disk_id - the disk id identifying the disk to be mounted + * @part - disk partition or zero for full disk + * @flags - umount flags + * @timeout_ms - timeout to wait for the kernel to flush closed files so that + * umount can succeed + * @returns - 0 on success, a negative value on error + */ +long lkl_umount_dev(unsigned int disk_id, unsigned int part, int flags, + long timeout_ms); + +/** + * lkl_umount_timeout - umount filesystem with timeout + * + * @path - the path to unmount + * @flags - umount flags + * @timeout_ms - timeout to wait for the kernel to flush closed files so that + * umount can succeed + * @returns - 0 on success, a negative value on error + */ +long lkl_umount_timeout(char *path, int flags, long timeout_ms); + +/** + * lkl_opendir - open a directory + * + * @path - directory path + * @err - pointer to store the error in case of failure + * @returns - a handle to be used when calling lkl_readdir + */ +struct lkl_dir *lkl_opendir(const char *path, int *err); + +/** + * lkl_fdopendir - open a directory + * + * @fd - file descriptor + * @err - pointer to store the error in case of failure + * @returns - a handle to be used when calling lkl_readdir + */ +struct lkl_dir *lkl_fdopendir(int fd, int *err); + +/** + * lkl_rewinddir - reset directory stream + * + * @dir - the directory handler as returned by lkl_opendir + */ +void lkl_rewinddir(struct lkl_dir *dir); + +/** + * lkl_closedir - close the directory + * + * @dir - the directory handler as returned by lkl_opendir + */ +int lkl_closedir(struct lkl_dir *dir); + +/** + * lkl_readdir - get the next available entry of the directory + * + * @dir - the directory handler as returned by lkl_opendir + * @returns - a lkl_dirent64 entry or NULL if the end of the directory stream is + * reached or if an error occurred; check lkl_errdir() to distinguish between + * errors or end of the directory stream + */ +struct lkl_linux_dirent64 *lkl_readdir(struct lkl_dir *dir); + +/** + * lkl_errdir - checks if an error occurred during the last lkl_readdir call + * + * @dir - the directory handler as returned by lkl_opendir + * @returns - 0 if no error occurred, or a negative value otherwise + */ +int lkl_errdir(struct lkl_dir *dir); + +/** + * lkl_dirfd - gets the file descriptor associated with the directory handle + * + * @dir - the directory handle as returned by lkl_opendir + * @returns - a positive value,which is the LKL file descriptor associated with + * the directory handle, or a negative value otherwise + */ +int lkl_dirfd(struct lkl_dir *dir); + +/** + * lkl_mount_fs - mount a file system type like proc, sys + * @fstype - file system type. e.g. proc, sys + * @returns - 0 on success. 1 if it's already mounted. negative on failure. + */ +int lkl_mount_fs(char *fstype); + #ifdef __cplusplus } #endif diff --git a/tools/um/include/lkl_host.h b/tools/um/include/lkl_host.h index 85e80eb4ad0d..12dd95616e43 100644 --- a/tools/um/include/lkl_host.h +++ b/tools/um/include/lkl_host.h @@ -10,6 +10,7 @@ extern "C" { #include <lkl.h> extern struct lkl_host_operations lkl_host_ops; +extern char lkl_um_devs[4096]; /** * lkl_printf - print a message via the host print operation diff --git a/tools/um/lib/Build b/tools/um/lib/Build index dddff26a3b4e..29491b40746c 100644 --- a/tools/um/lib/Build +++ b/tools/um/lib/Build @@ -4,3 +4,4 @@ CFLAGS_posix-host.o += -D_FILE_OFFSET_BITS=64 liblinux-$(CONFIG_UMMODE_LIB) += utils.o liblinux-$(CONFIG_UMMODE_LIB) += posix-host.o liblinux-$(CONFIG_UMMODE_LIB) += jmp_buf.o +liblinux-$(CONFIG_UMMODE_LIB) += fs.o diff --git a/tools/um/lib/fs.c b/tools/um/lib/fs.c new file mode 100644 index 000000000000..948aac9730c2 --- /dev/null +++ b/tools/um/lib/fs.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <lkl_host.h> + +#define MAX_FSTYPE_LEN 50 + +static struct lkl_disk *lkl_disks[16]; +static int registered_blk_dev_idx; + +static int lkl_disk_um_add(struct lkl_disk *disk, const char *blkparams) +{ + /* concat strings */ + snprintf(lkl_um_devs + strlen(lkl_um_devs), sizeof(lkl_um_devs), + " ubd%d=%s", registered_blk_dev_idx, blkparams); + + return registered_blk_dev_idx++; +} + +int lkl_disk_add(struct lkl_disk *disk) +{ + int ret = -1; + + ret = lkl_disk_um_add(disk, disk->dev); + + lkl_disks[ret] = disk; + + return ret; +} + +int lkl_disk_remove(struct lkl_disk disk) +{ + /* FIXME */ + return 0; +} + +int lkl_mount_fs(char *fstype) +{ + char dir[MAX_FSTYPE_LEN+2] = "/"; + int flags = 0, ret = 0; + + strncat(dir, fstype, MAX_FSTYPE_LEN); + + /* Create with regular umask */ + ret = lkl_sys_mkdir(dir, 0xff); + if (ret && ret != -LKL_EEXIST) { + lkl_perror("mount_fs mkdir", ret); + return ret; + } + + /* We have no use for nonzero flags right now */ + ret = lkl_sys_mount("none", dir, fstype, flags, NULL); + if (ret && ret != -LKL_EBUSY) { + lkl_sys_rmdir(dir); + return ret; + } + + if (ret == -LKL_EBUSY) + return 1; + return 0; +} + +static uint32_t new_encode_dev(unsigned int major, unsigned int minor) +{ + return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12); +} + +static int startswith(const char *str, const char *pre) +{ + return strncmp(pre, str, strlen(pre)) == 0; +} + +static int get_node_with_prefix(const char *path, const char *prefix, + char *result, unsigned int result_len) +{ + struct lkl_dir *dir = NULL; + struct lkl_linux_dirent64 *dirent; + int ret; + + dir = lkl_opendir(path, &ret); + if (!dir) + return ret; + + ret = -LKL_ENOENT; + + while ((dirent = lkl_readdir(dir))) { + if (startswith(dirent->d_name, prefix)) { + if (strlen(dirent->d_name) + 1 > result_len) { + ret = -LKL_ENOMEM; + break; + } + memcpy(result, dirent->d_name, strlen(dirent->d_name)); + result[strlen(dirent->d_name)] = '\0'; + ret = 0; + break; + } + } + + lkl_closedir(dir); + + return ret; +} + +int lkl_encode_dev_from_sysfs(const char *sysfs_path, uint32_t *pdevid) +{ + int ret; + long fd; + int major, minor; + char buf[16] = { 0, }; + char *bufptr; + + fd = lkl_sys_open(sysfs_path, LKL_O_RDONLY, 0); + if (fd < 0) + return fd; + + ret = lkl_sys_read(fd, buf, sizeof(buf)); + if (ret < 0) + goto out_close; + + if (ret == sizeof(buf)) { + ret = -LKL_ENOBUFS; + goto out_close; + } + + bufptr = strchr(buf, ':'); + if (bufptr == NULL) { + ret = -LKL_EINVAL; + goto out_close; + } + bufptr[0] = '\0'; + bufptr++; + + major = atoi(buf); + minor = atoi(bufptr); + + *pdevid = new_encode_dev(major, minor); + ret = 0; + +out_close: + lkl_sys_close(fd); + + return ret; +} + +#define SYSFS_DEV_UMBLK_CMDLINE_PATH \ + "/sysfs/devices/platform/uml-blkdev.%d" + +struct abuf { + char *mem, *ptr; + unsigned int len; +}; + +static int snprintf_append(struct abuf *buf, const char *fmt, ...) +{ + int ret; + va_list args; + + if (!buf->ptr) + buf->ptr = buf->mem; + + va_start(args, fmt); + ret = vsnprintf(buf->ptr, buf->len - (buf->ptr - buf->mem), fmt, args); + va_end(args); + + if (ret < 0 || (ret >= (int)(buf->len - (buf->ptr - buf->mem)))) + return -LKL_ENOMEM; + + buf->ptr += ret; + + return 0; +} + +static int __lkl_get_blkdev(int disk_id, unsigned int part, uint32_t *pdevid, + const char *sysfs_path_fmt, const char *drv_prefix, + const char *disk_prefix) +{ + char sysfs_path[LKL_PATH_MAX]; + char drv_name[LKL_PATH_MAX]; + char disk_name[LKL_PATH_MAX]; + struct abuf sysfs_path_buf = { + .mem = sysfs_path, + .len = sizeof(sysfs_path), + }; + int ret; + + if (disk_id < 0) + return -LKL_EINVAL; + + ret = lkl_mount_fs("sysfs"); + if (ret < 0) + return ret; + + ret = snprintf_append(&sysfs_path_buf, sysfs_path_fmt, disk_id); + if (ret) + return ret; + + ret = get_node_with_prefix(sysfs_path, drv_prefix, drv_name, + sizeof(drv_name)); + if (ret) + return ret; + + ret = snprintf_append(&sysfs_path_buf, "/%s/block", drv_name); + if (ret) + return ret; + + ret = get_node_with_prefix(sysfs_path, disk_prefix, disk_name, + sizeof(disk_name)); + if (ret) + return ret; + + if (!part) + ret = snprintf_append(&sysfs_path_buf, "/%s/dev", disk_name); + else + ret = snprintf_append(&sysfs_path_buf, "/%s/%s%d/dev", + disk_name, disk_name, part); + if (ret) + return ret; + + return lkl_encode_dev_from_sysfs(sysfs_path, pdevid); +} + +int lkl_get_blkdev(int disk_id, unsigned int part, uint32_t *pdevid) +{ + char *fmt; + + fmt = SYSFS_DEV_UMBLK_CMDLINE_PATH; + return __lkl_get_blkdev(disk_id, part, pdevid, fmt, "", "ubd"); +} + +long lkl_mount_dev(unsigned int disk_id, unsigned int part, + const char *fs_type, int flags, + const char *data, char *mnt_str, unsigned int mnt_str_len) +{ + char dev_str[] = { "/dev/xxxxxxxx" }; + unsigned int dev; + int err; + char _data[4096]; /* FIXME: PAGE_SIZE is not exported by LKL */ + + if (mnt_str_len < sizeof(dev_str)) + return -LKL_ENOMEM; + + err = lkl_get_blkdev(disk_id, part, &dev); + if (err < 0) + return err; + + snprintf(dev_str, sizeof(dev_str), "/dev/%08x", dev); + snprintf(mnt_str, mnt_str_len, "/mnt/%08x", dev); + + err = lkl_sys_access("/dev", LKL_S_IRWXO); + if (err < 0) { + if (err == -LKL_ENOENT) + err = lkl_sys_mkdir("/dev", 0700); + if (err < 0) + return err; + } + + err = lkl_sys_mknod(dev_str, LKL_S_IFBLK | 0600, dev); + if (err < 0) + return err; + + err = lkl_sys_access("/mnt", LKL_S_IRWXO); + if (err < 0) { + if (err == -LKL_ENOENT) + err = lkl_sys_mkdir("/mnt", 0700); + if (err < 0) + return err; + } + + err = lkl_sys_mkdir(mnt_str, 0700); + if (err < 0) { + lkl_sys_unlink(dev_str); + return err; + } + + /* kernel always copies a full page */ + if (data) { + strncpy(_data, data, sizeof(_data)); + _data[sizeof(_data) - 1] = 0; + } else { + _data[0] = 0; + } + + err = lkl_sys_mount(dev_str, mnt_str, (char *)fs_type, flags, _data); + if (err < 0) { + lkl_sys_unlink(dev_str); + lkl_sys_rmdir(mnt_str); + return err; + } + + return 0; +} + +long lkl_umount_timeout(char *path, int flags, long timeout_ms) +{ + long incr = 10000000; /* 10 ms */ + struct lkl_timespec ts = { + .tv_sec = 0, + .tv_nsec = incr, + }; + long err; + + do { + err = lkl_sys_umount(path, flags); + if (err == -LKL_EBUSY) { + lkl_sys_nanosleep((struct __lkl__kernel_timespec *)&ts, + NULL); + timeout_ms -= incr / 1000000; + } + } while (err == -LKL_EBUSY && timeout_ms > 0); + + return err; +} + +long lkl_umount_dev(unsigned int disk_id, unsigned int part, int flags, + long timeout_ms) +{ + char dev_str[] = { "/dev/xxxxxxxx" }; + char mnt_str[] = { "/mnt/xxxxxxxx" }; + unsigned int dev; + int err; + + err = lkl_get_blkdev(disk_id, part, &dev); + if (err < 0) + return err; + + snprintf(dev_str, sizeof(dev_str), "/dev/%08x", dev); + snprintf(mnt_str, sizeof(mnt_str), "/mnt/%08x", dev); + + err = lkl_umount_timeout(mnt_str, flags, timeout_ms); + if (err) + return err; + + err = lkl_sys_unlink(dev_str); + if (err) + return err; + + return lkl_sys_rmdir(mnt_str); +} + +struct lkl_dir { + int fd; + char buf[1024]; + char *pos; + int len; +}; + +static struct lkl_dir *lkl_dir_alloc(int *err) +{ + struct lkl_dir *dir = lkl_host_ops.mem_alloc(sizeof(struct lkl_dir)); + + if (!dir) { + *err = -LKL_ENOMEM; + return NULL; + } + + dir->len = 0; + dir->pos = NULL; + + return dir; +} + +struct lkl_dir *lkl_opendir(const char *path, int *err) +{ + struct lkl_dir *dir = lkl_dir_alloc(err); + + if (!dir) { + *err = -LKL_ENOMEM; + return NULL; + } + + dir->fd = lkl_sys_open(path, LKL_O_RDONLY | LKL_O_DIRECTORY, 0); + if (dir->fd < 0) { + *err = dir->fd; + lkl_host_ops.mem_free(dir); + return NULL; + } + + *err = 0; + + return dir; +} + +struct lkl_dir *lkl_fdopendir(int fd, int *err) +{ + struct lkl_dir *dir = lkl_dir_alloc(err); + + if (!dir) + return NULL; + + dir->fd = fd; + + return dir; +} + +void lkl_rewinddir(struct lkl_dir *dir) +{ + lkl_sys_lseek(dir->fd, 0, LKL_SEEK_SET); + dir->len = 0; + dir->pos = NULL; +} + +int lkl_closedir(struct lkl_dir *dir) +{ + int ret; + + ret = lkl_sys_close(dir->fd); + lkl_host_ops.mem_free(dir); + + return ret; +} + +struct lkl_linux_dirent64 *lkl_readdir(struct lkl_dir *dir) +{ + struct lkl_linux_dirent64 *de; + + if (dir->len < 0) + return NULL; + + if (!dir->pos || dir->pos - dir->buf >= dir->len) + goto read_buf; + +return_de: + de = (struct lkl_linux_dirent64 *)dir->pos; + dir->pos += de->d_reclen; + + return de; + +read_buf: + dir->pos = NULL; + de = (struct lkl_linux_dirent64 *)dir->buf; + dir->len = lkl_sys_getdents64(dir->fd, de, sizeof(dir->buf)); + if (dir->len <= 0) + return NULL; + + dir->pos = dir->buf; + goto return_de; +} + +int lkl_errdir(struct lkl_dir *dir) +{ + if (dir->len >= 0) + return 0; + + return dir->len; +} + +int lkl_dirfd(struct lkl_dir *dir) +{ + return dir->fd; +} + +int lkl_set_fd_limit(unsigned int fd_limit) +{ + struct lkl_rlimit rlim = { + .rlim_cur = fd_limit, + .rlim_max = fd_limit, + }; + return lkl_sys_setrlimit(LKL_RLIMIT_NOFILE, &rlim); +} diff --git a/tools/um/lib/posix-host.c b/tools/um/lib/posix-host.c index b6b5b2902254..8fd88031bf2b 100644 --- a/tools/um/lib/posix-host.c +++ b/tools/um/lib/posix-host.c @@ -263,6 +263,7 @@ static void *tls_get(struct lkl_tls_key *key) } struct lkl_host_operations lkl_host_ops = { + .um_devices = lkl_um_devs, .panic = panic, .print = print, .mem_alloc = (void *)malloc, diff --git a/tools/um/lib/utils.c b/tools/um/lib/utils.c index 4930479a8a35..ac65cd744a14 100644 --- a/tools/um/lib/utils.c +++ b/tools/um/lib/utils.c @@ -4,6 +4,9 @@ #include <string.h> #include <lkl_host.h> +/* XXX: find a better place */ +char lkl_um_devs[4096]; + static const char * const lkl_err_strings[] = { "Success", "Operation not permitted", diff --git a/tools/um/tests/Build b/tools/um/tests/Build index 564560486f98..1aa78f9ed7ef 100644 --- a/tools/um/tests/Build +++ b/tools/um/tests/Build @@ -1,3 +1,4 @@ boot-y += boot.o test.o +disk-y += disk.o test.o cla.o CFLAGS_test.o += -Wno-implicit-fallthrough diff --git a/tools/um/tests/cla.c b/tools/um/tests/cla.c new file mode 100644 index 000000000000..694e3a3822be --- /dev/null +++ b/tools/um/tests/cla.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#ifdef __MINGW32__ +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include "cla.h" + +static int cl_arg_parse_bool(struct cl_arg *arg, const char *value) +{ + *((int *)arg->store) = 1; + return 0; +} + +static int cl_arg_parse_str(struct cl_arg *arg, const char *value) +{ + *((const char **)arg->store) = value; + return 0; +} + +static int cl_arg_parse_int(struct cl_arg *arg, const char *value) +{ + errno = 0; + *((int *)arg->store) = strtol(value, NULL, 0); + return errno == 0; +} + +static int cl_arg_parse_str_set(struct cl_arg *arg, const char *value) +{ + const char **set = arg->set; + int i; + + for (i = 0; set[i] != NULL; i++) { + if (strcmp(set[i], value) == 0) { + *((int *)arg->store) = i; + return 0; + } + } + + return (-1); +} + +static int cl_arg_parse_ipv4(struct cl_arg *arg, const char *value) +{ + unsigned int addr; + + if (!value) + return (-1); + + addr = inet_addr(value); + if (addr == INADDR_NONE) + return (-1); + *((unsigned int *)arg->store) = addr; + return 0; +} + +static cl_arg_parser_t parsers[] = { + [CL_ARG_BOOL] = cl_arg_parse_bool, + [CL_ARG_INT] = cl_arg_parse_int, + [CL_ARG_STR] = cl_arg_parse_str, + [CL_ARG_STR_SET] = cl_arg_parse_str_set, + [CL_ARG_IPV4] = cl_arg_parse_ipv4, +}; + +static struct cl_arg *find_short_arg(char name, struct cl_arg *args) +{ + struct cl_arg *arg; + + for (arg = args; arg->short_name != 0; arg++) { + if (arg->short_name == name) + return arg; + } + + return NULL; +} + +static struct cl_arg *find_long_arg(const char *name, struct cl_arg *args) +{ + struct cl_arg *arg; + + for (arg = args; arg->long_name; arg++) { + if (strcmp(arg->long_name, name) == 0) + return arg; + } + + return NULL; +} + +static void print_help(struct cl_arg *args) +{ + struct cl_arg *arg; + + fprintf(stderr, "usage:\n"); + for (arg = args; arg->long_name; arg++) { + fprintf(stderr, "-%c, --%-20s %s", arg->short_name, + arg->long_name, arg->help); + if (arg->type == CL_ARG_STR_SET) { + const char **set = arg->set; + + fprintf(stderr, " [ "); + while (*set != NULL) + fprintf(stderr, "%s ", *(set++)); + fprintf(stderr, "]"); + } + fprintf(stderr, "\n"); + } +} + +int cla_parse_args(int argc, const char **argv, struct cl_arg *args) +{ + int i; + + for (i = 1; i < argc; i++) { + struct cl_arg *arg = NULL; + cl_arg_parser_t parser; + + if (argv[i][0] == '-') { + if (argv[i][1] != '-') + arg = find_short_arg(argv[i][1], args); + else + arg = find_long_arg(&argv[i][2], args); + } + + if (!arg) { + fprintf(stderr, "unknown option '%s'\n", argv[i]); + print_help(args); + return (-1); + } + + if (arg->type == CL_ARG_USER || arg->type >= CL_ARG_END) + parser = arg->parser; + else + parser = parsers[arg->type]; + + if (!parser) { + fprintf(stderr, "can't parse --'%s'/-'%c'\n", + arg->long_name, args->short_name); + return (-1); + } + + if (parser(arg, argv[i + 1]) < 0) { + fprintf(stderr, "can't parse '%s'\n", argv[i]); + print_help(args); + return (-1); + } + + if (arg->has_arg) + i++; + } + + return 0; +} diff --git a/tools/um/tests/cla.h b/tools/um/tests/cla.h new file mode 100644 index 000000000000..3d879233681f --- /dev/null +++ b/tools/um/tests/cla.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _LKL_TEST_CLA_H +#define _LKL_TEST_CLA_H + +enum cl_arg_type { + CL_ARG_USER = 0, + CL_ARG_BOOL, + CL_ARG_INT, + CL_ARG_STR, + CL_ARG_STR_SET, + CL_ARG_IPV4, + CL_ARG_END, +}; + +struct cl_arg; + +typedef int (*cl_arg_parser_t)(struct cl_arg *arg, const char *value); + +struct cl_arg { + const char *long_name; + char short_name; + const char *help; + int has_arg; + enum cl_arg_type type; + void *store; + void *set; + cl_arg_parser_t parser; +}; + +int cla_parse_args(int argc, const char **argv, struct cl_arg *args); + + +#endif /* _LKL_TEST_CLA_H */ diff --git a/tools/um/tests/disk.c b/tools/um/tests/disk.c new file mode 100644 index 000000000000..325934935e6a --- /dev/null +++ b/tools/um/tests/disk.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <stdint.h> +#include <lkl.h> +#include <lkl_host.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include "test.h" +#include "cla.h" + +static struct { + int printk; + const char *disk; + const char *fstype; + int partition; +} cla; + +struct cl_arg args[] = { + {"disk", 'd', "disk file to use", 1, CL_ARG_STR, &cla.disk}, + {"partition", 'P', "partition to mount", 1, CL_ARG_INT, &cla.partition}, + {"type", 't', "filesystem type", 1, CL_ARG_STR, &cla.fstype}, + {0}, +}; + + +static struct lkl_disk disk; +static int disk_id = -1; + +int lkl_test_disk_add(void) +{ + disk.fd = open(cla.disk, O_RDWR); + if (disk.fd < 0) + goto out_unlink; + + disk.ops = NULL; + disk.dev = (char *)cla.disk; + + disk_id = lkl_disk_add(&disk); + if (disk_id < 0) + goto out_close; + + goto out; + +out_close: + close(disk.fd); + +out_unlink: + unlink(cla.disk); + +out: + lkl_test_logf("disk fd/handle %x disk_id %d", disk.fd, disk_id); + + if (disk_id >= 0) + return TEST_SUCCESS; + + return TEST_FAILURE; +} + +int lkl_test_disk_remove(void) +{ + int ret; + + ret = lkl_disk_remove(disk); + + close(disk.fd); + + if (ret == 0) + return TEST_SUCCESS; + + return TEST_FAILURE; +} + + +static char mnt_point[32]; + +LKL_TEST_CALL(mount_dev, lkl_mount_dev, 0, disk_id, cla.partition, cla.fstype, + 0, NULL, mnt_point, sizeof(mnt_point)) + +static int lkl_test_umount_dev(void) +{ + long ret, ret2; + + ret = lkl_sys_chdir("/"); + + ret2 = lkl_umount_dev(disk_id, cla.partition, 0, 1000); + + lkl_test_logf("%ld %ld", ret, ret2); + + if (!ret && !ret2) + return TEST_SUCCESS; + + return TEST_FAILURE; +} + +struct lkl_dir *dir; + +static int lkl_test_opendir(void) +{ + int err; + + dir = lkl_opendir(mnt_point, &err); + + lkl_test_logf("lkl_opedir(%s) = %d %s\n", mnt_point, err, + lkl_strerror(err)); + + if (err == 0) + return TEST_SUCCESS; + + return TEST_FAILURE; +} + +static int lkl_test_readdir(void) +{ + struct lkl_linux_dirent64 *de = lkl_readdir(dir); + int wr = 0; + + while (de) { + wr += lkl_test_logf("%s ", de->d_name); + if (wr >= 70) { + lkl_test_logf("\n"); + wr = 0; + break; + } + de = lkl_readdir(dir); + } + + if (lkl_errdir(dir) == 0) + return TEST_SUCCESS; + + return TEST_FAILURE; +} + +LKL_TEST_CALL(closedir, lkl_closedir, 0, dir); +LKL_TEST_CALL(chdir_mnt_point, lkl_sys_chdir, 0, mnt_point); +LKL_TEST_CALL(start_kernel, lkl_start_kernel, 0, &lkl_host_ops, + "mem=16M loglevel=8"); +LKL_TEST_CALL(stop_kernel, lkl_sys_halt, 0); + +struct lkl_test tests[] = { + LKL_TEST(disk_add), + LKL_TEST(start_kernel), + LKL_TEST(mount_dev), + LKL_TEST(chdir_mnt_point), + LKL_TEST(opendir), + LKL_TEST(readdir), + LKL_TEST(closedir), + LKL_TEST(umount_dev), + LKL_TEST(stop_kernel), + LKL_TEST(disk_remove), + +}; + +int main(int argc, const char **argv) +{ + if (cla_parse_args(argc, argv, args) < 0) + return (-1); + + lkl_host_ops.print = lkl_test_log; + + return lkl_test_run(tests, sizeof(tests)/sizeof(struct lkl_test), + "disk %s", cla.fstype); +} diff --git a/tools/um/tests/disk.sh b/tools/um/tests/disk.sh new file mode 100755 index 000000000000..e2ec6cf69d4b --- /dev/null +++ b/tools/um/tests/disk.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0 + +script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd) + +source $script_dir/test.sh + +function prepfs() +{ + set -e + + file=`mktemp` + + dd if=/dev/zero of=$file bs=1024 count=204800 + + yes | mkfs.$1 $file + + if ! [ -z $ANDROID_WDIR ]; then + adb shell mkdir -p $ANDROID_WDIR + adb push $file $ANDROID_WDIR + rm $file + file=$ANDROID_WDIR/$(basename $file) + fi + if ! [ -z $BSD_WDIR ]; then + $MYSSH mkdir -p $BSD_WDIR + ssh_copy $file $BSD_WDIR + rm $file + file=$BSD_WDIR/$(basename $file) + fi + + export_vars file +} + +function cleanfs() +{ + set -e + + if ! [ -z $ANDROID_WDIR ]; then + adb shell rm $1 + adb shell rm $ANDROID_WDIR/disk + elif ! [ -z $BSD_WDIR ]; then + $MYSSH rm $1 + $MYSSH rm $BSD_WDIR/disk + else + rm $1 + fi +} + +if [ "$1" = "-t" ]; then + shift + fstype=$1 + shift +fi + +if [ -z "$fstype" ]; then + fstype="ext4" +fi + +if [ -z $(which mkfs.$fstype) ]; then + lkl_test_plan 0 "disk $fstype" + echo "no mkfs.$fstype command" + exit 0 +fi + +lkl_test_plan 1 "disk $fstype" +lkl_test_run 1 prepfs $fstype +lkl_test_exec $script_dir/disk -d $file -t $fstype $@ +lkl_test_plan 1 "disk $fstype" +lkl_test_run 1 cleanfs $file + diff --git a/tools/um/tests/run.py b/tools/um/tests/run.py index c96ede90b6ad..97d6dedc217c 100755 --- a/tools/um/tests/run.py +++ b/tools/um/tests/run.py @@ -50,6 +50,8 @@ mydir=os.path.dirname(os.path.realpath(__file__)) tests = [ 'boot.sh', + 'disk.sh -t ext4', + 'disk.sh -t vfat', ] parser = argparse.ArgumentParser(description='LKL test runner')
This commit adds block device support for nommu mode, and also added several host utilities to run a simple test program (tools/um/test/disk.c) successfully. Signed-off-by: Hajime Tazaki <thehajime@gmail.com> --- arch/um/include/asm/xor.h | 3 +- arch/um/include/shared/as-layout.h | 1 + arch/um/kernel/um_arch.c | 5 + arch/um/nommu/include/uapi/asm/host_ops.h | 7 + arch/um/nommu/include/uapi/asm/unistd.h | 2 + arch/um/nommu/um/Kconfig | 8 + arch/um/nommu/um/setup.c | 12 + tools/um/Targets | 2 + tools/um/include/lkl.h | 206 ++++++++++ tools/um/include/lkl_host.h | 1 + tools/um/lib/Build | 1 + tools/um/lib/fs.c | 461 ++++++++++++++++++++++ tools/um/lib/posix-host.c | 1 + tools/um/lib/utils.c | 3 + tools/um/tests/Build | 1 + tools/um/tests/cla.c | 159 ++++++++ tools/um/tests/cla.h | 33 ++ tools/um/tests/disk.c | 168 ++++++++ tools/um/tests/disk.sh | 70 ++++ tools/um/tests/run.py | 2 + 20 files changed, 1145 insertions(+), 1 deletion(-) create mode 100644 tools/um/lib/fs.c create mode 100644 tools/um/tests/cla.c create mode 100644 tools/um/tests/cla.h create mode 100644 tools/um/tests/disk.c create mode 100755 tools/um/tests/disk.sh