From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-11.3 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,NICE_REPLY_A, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 56BD5C41604 for ; Wed, 7 Oct 2020 14:17:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E8463212CC for ; Wed, 7 Oct 2020 14:17:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728467AbgJGORp (ORCPT ); Wed, 7 Oct 2020 10:17:45 -0400 Received: from ivanoab7.miniserver.com ([37.128.132.42]:38044 "EHLO www.kot-begemot.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728177AbgJGORp (ORCPT ); Wed, 7 Oct 2020 10:17:45 -0400 Received: from tun252.jain.kot-begemot.co.uk ([192.168.18.6] helo=jain.kot-begemot.co.uk) by www.kot-begemot.co.uk with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kQAG8-0002rR-11; Wed, 07 Oct 2020 14:17:40 +0000 Received: from jain.kot-begemot.co.uk ([192.168.3.3]) by jain.kot-begemot.co.uk with esmtp (Exim 4.92) (envelope-from ) id 1kQAG3-0001L4-Ib; Wed, 07 Oct 2020 15:17:35 +0100 Subject: Re: [RFC v7 21/21] um: nommu: add block device support of UML To: Hajime Tazaki , linux-um@lists.infradead.org, jdike@addtoit.com, richard@nod.at Cc: tavi.purdila@gmail.com, linux-kernel-library@freelists.org, linux-arch@vger.kernel.org, retrage01@gmail.com References: From: Anton Ivanov Message-ID: Date: Wed, 7 Oct 2020 15:17:31 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.12.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-Language: en-US X-Clacks-Overhead: GNU Terry Pratchett Precedence: bulk List-ID: X-Mailing-List: linux-arch@vger.kernel.org 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 > --- > 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 > > #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 > > 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 > +#include > +#include > +#include > +#include > + > +#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 > #include > > +/* 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 > +#include > +#include > +#include > +#ifdef __MINGW32__ > +#include > +#else > +#include > +#include > +#include > +#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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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') -- Anton R. Ivanov Cambridgegreys Limited. Registered in England. Company Number 10273661 https://www.cambridgegreys.com/