From patchwork Thu Nov 19 14:16:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Weinberger X-Patchwork-Id: 11917581 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 A0A62C6369E for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 468E524181 for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727645AbgKSO1F (ORCPT ); Thu, 19 Nov 2020 09:27:05 -0500 Received: from lilium.sigma-star.at ([109.75.188.150]:58532 "EHLO lilium.sigma-star.at" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727106AbgKSO1E (ORCPT ); Thu, 19 Nov 2020 09:27:04 -0500 X-Greylist: delayed 516 seconds by postgrey-1.27 at vger.kernel.org; Thu, 19 Nov 2020 09:27:03 EST Received: from localhost (localhost [127.0.0.1]) by lilium.sigma-star.at (Postfix) with ESMTP id 898CF181C8900; Thu, 19 Nov 2020 15:18:26 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id 7yQ7veX_WR98; Thu, 19 Nov 2020 15:18:26 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id Dx73T873WR7e; Thu, 19 Nov 2020 15:18:26 +0100 (CET) From: Richard Weinberger To: miklos@szeredi.hu Cc: miquel.raynal@bootlin.com, vigneshr@ti.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, Richard Weinberger Subject: [PATCH 1/5] fuse: Rename FUSE_DIO_CUSE Date: Thu, 19 Nov 2020 15:16:55 +0100 Message-Id: <20201119141659.26176-2-richard@nod.at> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201119141659.26176-1-richard@nod.at> References: <20201119141659.26176-1-richard@nod.at> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org MUSE needs to use this flag too, rename it to FUSE_DIO_NOFS to denote that the DIO operation has no FUSE backed inode. Signed-off-by: Richard Weinberger --- fs/fuse/cuse.c | 4 ++-- fs/fuse/file.c | 4 ++-- fs/fuse/fuse_i.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 45082269e698..55744430b0f0 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -96,7 +96,7 @@ static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to) struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb); loff_t pos = 0; - return fuse_direct_io(&io, to, &pos, FUSE_DIO_CUSE); + return fuse_direct_io(&io, to, &pos, FUSE_DIO_NOFS); } static ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from) @@ -108,7 +108,7 @@ static ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from) * responsible for locking and sanity checks. */ return fuse_direct_io(&io, from, &pos, - FUSE_DIO_WRITE | FUSE_DIO_CUSE); + FUSE_DIO_WRITE | FUSE_DIO_NOFS); } static int cuse_open(struct inode *inode, struct file *file) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index c03034e8c152..697e79032c73 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1409,7 +1409,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, loff_t *ppos, int flags) { int write = flags & FUSE_DIO_WRITE; - int cuse = flags & FUSE_DIO_CUSE; + int nofs = flags & FUSE_DIO_NOFS; struct file *file = io->iocb->ki_filp; struct inode *inode = file->f_mapping->host; struct fuse_file *ff = file->private_data; @@ -1430,7 +1430,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, return -ENOMEM; ia->io = io; - if (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) { + if (!nofs && fuse_range_is_writeback(inode, idx_from, idx_to)) { if (!write) inode_lock(inode); fuse_sync_writes(inode); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d51598017d13..637caddff2a8 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1140,8 +1140,8 @@ int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, /** If set, it is WRITE; otherwise - READ */ #define FUSE_DIO_WRITE (1 << 0) -/** CUSE pass fuse_direct_io() a file which f_mapping->host is not from FUSE */ -#define FUSE_DIO_CUSE (1 << 1) +/** CUSE and MUSE pass fuse_direct_io() a file which f_mapping->host is not from FUSE */ +#define FUSE_DIO_NOFS (1 << 1) ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, loff_t *ppos, int flags); From patchwork Thu Nov 19 14:16:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Weinberger X-Patchwork-Id: 11917579 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 6BDADC63697 for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1657C238E6 for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727426AbgKSO1E (ORCPT ); Thu, 19 Nov 2020 09:27:04 -0500 Received: from lilium.sigma-star.at ([109.75.188.150]:58538 "EHLO lilium.sigma-star.at" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727387AbgKSO1D (ORCPT ); Thu, 19 Nov 2020 09:27:03 -0500 Received: from localhost (localhost [127.0.0.1]) by lilium.sigma-star.at (Postfix) with ESMTP id CEE1F181C88E4; Thu, 19 Nov 2020 15:18:26 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id 8Geu4G_KY9Fm; Thu, 19 Nov 2020 15:18:26 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id 9Y_VR-WpkaVL; Thu, 19 Nov 2020 15:18:26 +0100 (CET) From: Richard Weinberger To: miklos@szeredi.hu Cc: miquel.raynal@bootlin.com, vigneshr@ti.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, Richard Weinberger Subject: [PATCH 2/5] fuse: Export fuse_simple_request Date: Thu, 19 Nov 2020 15:16:56 +0100 Message-Id: <20201119141659.26176-3-richard@nod.at> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201119141659.26176-1-richard@nod.at> References: <20201119141659.26176-1-richard@nod.at> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org MUSE will use this function to issue requests, so export it. Signed-off-by: Richard Weinberger --- fs/fuse/dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 588f8d1240aa..8b7209537683 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -522,6 +522,7 @@ ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args) return ret; } +EXPORT_SYMBOL_GPL(fuse_simple_request); static bool fuse_request_queue_background(struct fuse_req *req) { From patchwork Thu Nov 19 14:16:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Weinberger X-Patchwork-Id: 11917587 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 42716C64E75 for ; Thu, 19 Nov 2020 14:27:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0E3C924199 for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727787AbgKSO1I (ORCPT ); Thu, 19 Nov 2020 09:27:08 -0500 Received: from lilium.sigma-star.at ([109.75.188.150]:58542 "EHLO lilium.sigma-star.at" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727387AbgKSO1G (ORCPT ); Thu, 19 Nov 2020 09:27:06 -0500 Received: from localhost (localhost [127.0.0.1]) by lilium.sigma-star.at (Postfix) with ESMTP id 987F6181C88E7; Thu, 19 Nov 2020 15:18:27 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id vhznnC7JMG0k; Thu, 19 Nov 2020 15:18:26 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id dDO3U2oTBdgd; Thu, 19 Nov 2020 15:18:26 +0100 (CET) From: Richard Weinberger To: miklos@szeredi.hu Cc: miquel.raynal@bootlin.com, vigneshr@ti.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, Richard Weinberger Subject: [PATCH 3/5] fuse: Make cuse_parse_one a common helper Date: Thu, 19 Nov 2020 15:16:57 +0100 Message-Id: <20201119141659.26176-4-richard@nod.at> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201119141659.26176-1-richard@nod.at> References: <20201119141659.26176-1-richard@nod.at> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This function will be used by MUSE too, let's share it. Signed-off-by: Richard Weinberger --- fs/fuse/Kconfig | 4 +++ fs/fuse/Makefile | 1 + fs/fuse/cuse.c | 58 +---------------------------------------- fs/fuse/fuse_i.h | 3 +++ fs/fuse/helper.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 57 deletions(-) create mode 100644 fs/fuse/helper.c diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig index 40ce9a1c12e5..9c8cc1e7b3a5 100644 --- a/fs/fuse/Kconfig +++ b/fs/fuse/Kconfig @@ -18,9 +18,13 @@ config FUSE_FS If you want to develop a userspace FS, or if you want to use a filesystem based on FUSE, answer Y or M. +config FUSE_HELPER + def_bool n + config CUSE tristate "Character device in Userspace support" depends on FUSE_FS + select FUSE_HELPER help This FUSE extension allows character devices to be implemented in userspace. diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index 8c7021fb2cd4..7a5768cce6be 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_VIRTIO_FS) += virtiofs.o fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o fuse-$(CONFIG_FUSE_DAX) += dax.o +fuse-$(CONFIG_FUSE_HELPER) += helper.o virtiofs-y := virtio_fs.o diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 55744430b0f0..24c015547130 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -199,62 +199,6 @@ struct cuse_devinfo { const char *name; }; -/** - * cuse_parse_one - parse one key=value pair - * @pp: i/o parameter for the current position - * @end: points to one past the end of the packed string - * @keyp: out parameter for key - * @valp: out parameter for value - * - * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends - * at @end - 1. This function parses one pair and set *@keyp to the - * start of the key and *@valp to the start of the value. Note that - * the original string is modified such that the key string is - * terminated with '\0'. *@pp is updated to point to the next string. - * - * RETURNS: - * 1 on successful parse, 0 on EOF, -errno on failure. - */ -static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp) -{ - char *p = *pp; - char *key, *val; - - while (p < end && *p == '\0') - p++; - if (p == end) - return 0; - - if (end[-1] != '\0') { - pr_err("info not properly terminated\n"); - return -EINVAL; - } - - key = val = p; - p += strlen(p); - - if (valp) { - strsep(&val, "="); - if (!val) - val = key + strlen(key); - key = strstrip(key); - val = strstrip(val); - } else - key = strstrip(key); - - if (!strlen(key)) { - pr_err("zero length info key specified\n"); - return -EINVAL; - } - - *pp = p; - *keyp = key; - if (valp) - *valp = val; - - return 1; -} - /** * cuse_parse_dev_info - parse device info * @p: device info string @@ -275,7 +219,7 @@ static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo) int rc; while (true) { - rc = cuse_parse_one(&p, end, &key, &val); + rc = fuse_kv_parse_one(&p, end, &key, &val); if (rc < 0) return rc; if (!rc) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 637caddff2a8..d36c71568a80 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1210,4 +1210,7 @@ void fuse_dax_inode_cleanup(struct inode *inode); bool fuse_dax_check_alignment(struct fuse_conn *fc, unsigned int map_alignment); void fuse_dax_cancel_work(struct fuse_conn *fc); +/* helper.c */ +int fuse_kv_parse_one(char **pp, char *end, char **keyp, char **valp); + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/helper.c b/fs/fuse/helper.c new file mode 100644 index 000000000000..35d37338445e --- /dev/null +++ b/fs/fuse/helper.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Helper functions used by CUSE and MUSE + * + * Copyright (C) 2008-2009 SUSE Linux Products GmbH + * Copyright (C) 2008-2009 Tejun Heo + * + */ + +#include +#include + +/** + * fuse_kv_parse_one - parse one key=value pair + * @pp: i/o parameter for the current position + * @end: points to one past the end of the packed string + * @keyp: out parameter for key + * @valp: out parameter for value + * + * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends + * at @end - 1. This function parses one pair and set *@keyp to the + * start of the key and *@valp to the start of the value. Note that + * the original string is modified such that the key string is + * terminated with '\0'. *@pp is updated to point to the next string. + * + * RETURNS: + * 1 on successful parse, 0 on EOF, -errno on failure. + */ +int fuse_kv_parse_one(char **pp, char *end, char **keyp, char **valp) +{ + char *p = *pp; + char *key, *val; + + while (p < end && *p == '\0') + p++; + if (p == end) + return 0; + + if (end[-1] != '\0') { + pr_err("info not properly terminated\n"); + return -EINVAL; + } + + key = val = p; + p += strlen(p); + + if (valp) { + strsep(&val, "="); + if (!val) + val = key + strlen(key); + key = strstrip(key); + val = strstrip(val); + } else + key = strstrip(key); + + if (!strlen(key)) { + pr_err("zero length info key specified\n"); + return -EINVAL; + } + + *pp = p; + *keyp = key; + if (valp) + *valp = val; + + return 1; +} +EXPORT_SYMBOL_GPL(fuse_kv_parse_one); From patchwork Thu Nov 19 14:16:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Weinberger X-Patchwork-Id: 11917585 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 D105AC63798 for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 73A7124199 for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727678AbgKSO1F (ORCPT ); Thu, 19 Nov 2020 09:27:05 -0500 Received: from lilium.sigma-star.at ([109.75.188.150]:58534 "EHLO lilium.sigma-star.at" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727171AbgKSO1E (ORCPT ); Thu, 19 Nov 2020 09:27:04 -0500 Received: from localhost (localhost [127.0.0.1]) by lilium.sigma-star.at (Postfix) with ESMTP id A2F1F181C8902; Thu, 19 Nov 2020 15:18:27 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id 9PdE9r_0My3r; Thu, 19 Nov 2020 15:18:27 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id bbDK0ihAvCod; Thu, 19 Nov 2020 15:18:27 +0100 (CET) From: Richard Weinberger To: miklos@szeredi.hu Cc: miquel.raynal@bootlin.com, vigneshr@ti.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, Richard Weinberger Subject: [PATCH 4/5] mtd: Add MTD_MUSE flag Date: Thu, 19 Nov 2020 15:16:58 +0100 Message-Id: <20201119141659.26176-5-richard@nod.at> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201119141659.26176-1-richard@nod.at> References: <20201119141659.26176-1-richard@nod.at> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This flag will get set if an MTD is implemeted in userspace using MUSE. Signed-off-by: Richard Weinberger --- include/uapi/mtd/mtd-abi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/mtd/mtd-abi.h b/include/uapi/mtd/mtd-abi.h index 65b9db936557..2ad2217e3a96 100644 --- a/include/uapi/mtd/mtd-abi.h +++ b/include/uapi/mtd/mtd-abi.h @@ -105,6 +105,7 @@ struct mtd_write_req { #define MTD_NO_ERASE 0x1000 /* No erase necessary */ #define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */ #define MTD_SLC_ON_MLC_EMULATION 0x4000 /* Emulate SLC behavior on MLC NANDs */ +#define MTD_MUSE 0x8000 /* This MTD is implemented in userspace */ /* Some common devices / combinations of capabilities */ #define MTD_CAP_ROM 0 From patchwork Thu Nov 19 14:16:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Weinberger X-Patchwork-Id: 11917589 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 2527FC6379F for ; Thu, 19 Nov 2020 14:27:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CB28624181 for ; Thu, 19 Nov 2020 14:27:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727715AbgKSO1I (ORCPT ); Thu, 19 Nov 2020 09:27:08 -0500 Received: from lilium.sigma-star.at ([109.75.188.150]:58540 "EHLO lilium.sigma-star.at" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727392AbgKSO1G (ORCPT ); Thu, 19 Nov 2020 09:27:06 -0500 Received: from localhost (localhost [127.0.0.1]) by lilium.sigma-star.at (Postfix) with ESMTP id C7938181E21C3; Thu, 19 Nov 2020 15:18:28 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id vuGU_PlldUSR; Thu, 19 Nov 2020 15:18:27 +0100 (CET) Received: from lilium.sigma-star.at ([127.0.0.1]) by localhost (lilium.sigma-star.at [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id O_yPa03onSx9; Thu, 19 Nov 2020 15:18:27 +0100 (CET) From: Richard Weinberger To: miklos@szeredi.hu Cc: miquel.raynal@bootlin.com, vigneshr@ti.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, Richard Weinberger Subject: [PATCH 5/5] fuse: Implement MUSE: MTD in userspace Date: Thu, 19 Nov 2020 15:16:59 +0100 Message-Id: <20201119141659.26176-6-richard@nod.at> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201119141659.26176-1-richard@nod.at> References: <20201119141659.26176-1-richard@nod.at> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org MUSE allows implementing a MTD in userspace. So far userspace has control over mtd_read, mtd_write, mtd_erase, and mtd_sync. It can also set the following MTD parameters: name, flags, site, writesize and erasesize. That way advanced simulators for many type of flashes can be implemented in userspace. Signed-off-by: Richard Weinberger --- fs/fuse/Kconfig | 11 + fs/fuse/Makefile | 1 + fs/fuse/muse.c | 450 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/fuse.h | 25 ++- 4 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 fs/fuse/muse.c diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig index 9c8cc1e7b3a5..2fc63dc18a53 100644 --- a/fs/fuse/Kconfig +++ b/fs/fuse/Kconfig @@ -56,3 +56,14 @@ config FUSE_DAX If you want to allow mounting a Virtio Filesystem with the "dax" option, answer Y. + +config MUSE + tristate "Memory Technology Device (MTD) in Userspace support" + depends on FUSE_FS + select FUSE_HELPER + select MTD + help + This FUSE extension allows an MTD to be implemented in userspace. + + If you want to develop or use a userspace MTD based on MUSE, + answer Y or M. diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index 7a5768cce6be..67a7af3fb047 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_FUSE_FS) += fuse.o obj-$(CONFIG_CUSE) += cuse.o obj-$(CONFIG_VIRTIO_FS) += virtiofs.o +obj-$(CONFIG_MUSE) += muse.o fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o fuse-$(CONFIG_FUSE_DAX) += dax.o diff --git a/fs/fuse/muse.c b/fs/fuse/muse.c new file mode 100644 index 000000000000..249907be9d98 --- /dev/null +++ b/fs/fuse/muse.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MUSE: MTD in userspace + * Copyright (C) 2020 sigma star gmbh + * Author: Richard Weinberger + */ + +#define pr_fmt(fmt) "MUSE: " fmt + +#include +#include +#include +#include +#include +#include + +#include "fuse_i.h" + +static struct file_operations muse_ctrl_fops; + +struct muse_conn { + struct fuse_mount fm; + struct fuse_conn fc; + struct fuse_file *ff; + struct mtd_info mtd; + struct file *dummy_file; + bool init_done; +}; + +struct muse_init_args { + struct fuse_args_pages ap; + struct muse_init_in in; + struct muse_init_out out; + struct page *page; + struct fuse_page_desc desc; +}; + +static int dummy_file_open(struct inode *inode, struct file *filp) +{ + WARN_ON_ONCE(1); + return -EIO; +} + +static const struct file_operations dummy_file_ops = { + .open = dummy_file_open, +}; + +static void muse_fc_release(struct fuse_conn *fc) +{ + struct muse_conn *mc = container_of(fc, struct muse_conn, fc); + + fput(mc->dummy_file); + kfree_rcu(mc, fc.rcu); +} + +static int muse_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct muse_conn *mc = mtd->priv; + struct fuse_mount *fm = &mc->fm; + struct muse_erase_in inarg; + FUSE_ARGS(args); + + inarg.addr = instr->addr; + inarg.len = instr->len; + + args.opcode = MUSE_ERASE; + args.nodeid = mc->ff->nodeid; + args.in_numargs = 1; + args.in_args[0].size = sizeof(inarg); + args.in_args[0].value = &inarg; + + return fuse_simple_request(fm, &args); +} + +static void muse_mtd_sync(struct mtd_info *mtd) +{ + struct muse_conn *mc = mtd->priv; + struct fuse_mount *fm = &mc->fm; + struct fuse_fsync_in inarg; + FUSE_ARGS(args); + + memset(&inarg, 0, sizeof(inarg)); + inarg.fh = mc->ff->fh; + inarg.fsync_flags = 0; + + /* + * We reuse FUSE_FSYNC to sync the whole MTD. + */ + args.opcode = FUSE_FSYNC; + args.nodeid = mc->ff->nodeid; + args.in_numargs = 1; + args.in_args[0].size = sizeof(inarg); + args.in_args[0].value = &inarg; + + fuse_simple_request(fm, &args); +} + +static int do_dio(struct kiocb *kiocb, struct iov_iter *iter, loff_t *pos, int flags) +{ + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb); + + return fuse_direct_io(&io, iter, pos, flags); +} + +static int muse_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct kvec iov = { .iov_base = buf, .iov_len = len }; + struct muse_conn *mc = mtd->priv; + struct kiocb kiocb; + struct iov_iter to; + loff_t pos = from; + int ret; + + iov_iter_kvec(&to, READ, &iov, 1, len); + init_sync_kiocb(&kiocb, mc->dummy_file); + + ret = do_dio(&kiocb, &to, &pos, FUSE_DIO_NOFS); + + *retlen = len; + return len; +} + +static int muse_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct kvec iov = { .iov_base = (u_char *)buf, .iov_len = len }; + struct muse_conn *mc = mtd->priv; + struct iov_iter from; + struct kiocb kiocb; + loff_t pos = to; + int ret; + + iov_iter_kvec(&from, WRITE, &iov, 1, len); + init_sync_kiocb(&kiocb, mc->dummy_file); + + ret = do_dio(&kiocb, &from, &pos, FUSE_DIO_WRITE | FUSE_DIO_NOFS); + + *retlen = len; + return 0; +} + +static int muse_mtd_get_device(struct mtd_info *mtd) +{ + struct muse_conn *mc = mtd->priv; + + fuse_conn_get(&mc->fc); + + return 0; +} + +static void muse_mtd_put_device(struct mtd_info *mtd) +{ + struct muse_conn *mc = mtd->priv; + + fuse_conn_put(&mc->fc); +} + +struct mtdreq { + const char *name; + struct mtd_info_user mi; +}; + +static int muse_parse_mtdreq(char *p, size_t len, struct mtd_info *mtd) +{ + struct mtdreq req = {}; + char *end = p + len; + char *key, *val; + int ret; + + for (;;) { + ret = fuse_kv_parse_one(&p, end, &key, &val); + if (ret < 0) + goto out; + if (!ret) + break; + + if (strcmp(key, "NAME") == 0) { + req.name = val; + } else if (strcmp(key, "TYPE") == 0) { + req.mi.type = (uint8_t)simple_strtoul(val, NULL, 10); + } else if (strcmp(key, "FLAGS") == 0) { + req.mi.flags = simple_strtoul(val, NULL, 10); + } else if (strcmp(key, "SIZE") == 0) { + req.mi.size = simple_strtoul(val, NULL, 10); + } else if (strcmp(key, "WRITESIZE") == 0) { + req.mi.writesize = simple_strtoul(val, NULL, 10); + } else if (strcmp(key, "ERASESIZE") == 0) { + req.mi.erasesize = simple_strtoul(val, NULL, 10); + } else { + pr_warn("Ignoring unknown MTD param \"%s\"\n", key); + } + } + + ret = -EINVAL; + + if (!req.name) + goto out; + + if (!req.mi.size || !req.mi.writesize || !req.mi.erasesize) + goto out; + + if (req.mi.size % req.mi.writesize) + goto out; + + if (req.mi.size % req.mi.erasesize) + goto out; + + if (req.mi.flags & ~(MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)) + goto out; + + if (req.mi.type == MTD_ABSENT || req.mi.type == MTD_UBIVOLUME) + goto out; + + mtd->name = kstrdup(req.name, GFP_KERNEL); + if (!mtd->name) { + ret = -ENOMEM; + goto out; + } + + mtd->size = req.mi.size; + mtd->erasesize = req.mi.erasesize; + mtd->writesize = req.mi.writesize; + mtd->writebufsize = mtd->writesize; + mtd->type = req.mi.type; + mtd->flags = MTD_MUSE | req.mi.flags; + + ret = 0; +out: + return ret; +} + +static void muse_process_init_reply(struct fuse_mount *fm, + struct fuse_args *args, int error) +{ + struct fuse_conn *fc = fm->fc; + struct muse_init_args *ia = container_of(args, struct muse_init_args, ap.args); + struct muse_conn *mc = container_of(fc, struct muse_conn, fc); + struct fuse_args_pages *ap = &ia->ap; + struct muse_init_out *arg = &ia->out; + struct page *page = ap->pages[0]; + int ret; + + if (error || arg->fuse_major != FUSE_KERNEL_VERSION || arg->fuse_minor < 33) { + goto abort; + } + + fc->minor = arg->fuse_minor; + fc->max_read = max_t(unsigned int, arg->max_read, 4096); + fc->max_write = max_t(unsigned int, arg->max_write, 4096); + + ret = muse_parse_mtdreq(page_address(page), ap->args.out_args[1].size, + &mc->mtd); + if (ret) + goto abort; + + mc->ff = fuse_file_alloc(fm); + if (!mc->ff) + goto abort; + + /* + * HACK: + * fuse_direct_io() expects a file object. + */ + mc->dummy_file = anon_inode_getfile("[muse]", &dummy_file_ops, NULL, O_RDWR); + if (!mc->dummy_file) + goto abort_free_ff; + + mc->dummy_file->private_data = mc->ff; + + mc->ff->fh = 0; + mc->ff->open_flags = FOPEN_DIRECT_IO; + + /* + * With MUSE there are no files, so we use one fuse file for the mtd object + * with nodeid FUSE_ROOT_ID. + */ + mc->ff->nodeid = FUSE_ROOT_ID; + + mc->mtd._erase = muse_mtd_erase; + mc->mtd._read = muse_mtd_read; + mc->mtd._sync = muse_mtd_sync; + mc->mtd._write = muse_mtd_write; + mc->mtd._get_device = muse_mtd_get_device; + mc->mtd._put_device = muse_mtd_put_device; + mc->mtd.priv = mc; + mc->mtd.owner = THIS_MODULE; + + /* + * We want one READ/WRITE op per MTD io. So the MTD pagesize needs + * to fit into max_write/max_read: + */ + if (fc->max_write < mc->mtd.writesize || fc->max_read < mc->mtd.writesize) + goto abort_put_file; + + if (mtd_device_register(&mc->mtd, NULL, 0) != 0) + goto abort_put_file; + + mc->init_done = true; + + kfree(ia); + __free_page(page); + return; + +abort_put_file: + fput(mc->dummy_file); +abort_free_ff: + fuse_file_free(mc->ff); +abort: + fuse_abort_conn(fc); +} + +static int muse_send_init(struct muse_conn *mc) +{ + struct fuse_mount *fm = &mc->fm; + struct fuse_args_pages *ap; + struct muse_init_args *ia; + struct page *page; + int ret = -ENOMEM; + + BUILD_BUG_ON(MUSE_INIT_INFO_MAX > PAGE_SIZE); + + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!page) + goto err; + + ia = kzalloc(sizeof(*ia), GFP_KERNEL); + if (!ia) + goto err_page; + + ap = &ia->ap; + ia->in.fuse_major = FUSE_KERNEL_VERSION; + ia->in.fuse_minor = FUSE_KERNEL_MINOR_VERSION; + ap->args.opcode = MUSE_INIT; + ap->args.in_numargs = 1; + ap->args.in_args[0].size = sizeof(ia->in); + ap->args.in_args[0].value = &ia->in; + ap->args.out_numargs = 2; + ap->args.out_args[0].size = sizeof(ia->out); + ap->args.out_args[0].value = &ia->out; + ap->args.out_args[1].size = MUSE_INIT_INFO_MAX; + ap->args.out_argvar = true; + ap->args.out_pages = true; + ap->num_pages = 1; + ap->pages = &ia->page; + ap->descs = &ia->desc; + ia->page = page; + ia->desc.length = ap->args.out_args[1].size; + ap->args.end = muse_process_init_reply; + + ret = fuse_simple_background(fm, &ap->args, GFP_KERNEL); + if (ret) + goto err_ia; + + return 0; + +err_ia: + kfree(ia); +err_page: + __free_page(page); +err: + return ret; +} + +static int muse_ctrl_open(struct inode *inode, struct file *file) +{ + struct muse_conn *mc; + struct fuse_dev *fud; + int ret; + + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto err; + } + + mc = kzalloc(sizeof(*mc), GFP_KERNEL); + if (!mc) { + ret = -ENOMEM; + goto err; + } + + fuse_conn_init(&mc->fc, &mc->fm, get_user_ns(&init_user_ns), + &fuse_dev_fiq_ops, NULL); + + fud = fuse_dev_alloc_install(&mc->fc); + if (!fud) { + ret = -ENOMEM; + goto err_free; + } + + mc->fc.release = muse_fc_release; + mc->fc.initialized = 1; + + ret = muse_send_init(mc); + if (ret) + goto err_dev; + + file->private_data = fud; + + return 0; + +err_dev: + fuse_dev_free(fud); + fuse_conn_put(&mc->fc); +err_free: + kfree(mc); +err: + return ret; +} + +static int muse_ctrl_release(struct inode *inode, struct file *file) +{ + struct fuse_dev *fud = file->private_data; + struct muse_conn *mc = container_of(fud->fc, struct muse_conn, fc); + + if (mc->init_done) + mtd_device_unregister(&mc->mtd); + + fuse_conn_put(&mc->fc); + + return fuse_dev_release(inode, file); +} + +static struct miscdevice muse_ctrl_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "muse", + .fops = &muse_ctrl_fops, +}; + +static int __init muse_init(void) +{ + muse_ctrl_fops = fuse_dev_operations; + muse_ctrl_fops.owner = THIS_MODULE; + muse_ctrl_fops.open = muse_ctrl_open; + muse_ctrl_fops.release = muse_ctrl_release; + + return misc_register(&muse_ctrl_dev); +} + +static void __exit muse_exit(void) +{ + misc_deregister(&muse_ctrl_dev); +} + +module_init(muse_init); +module_exit(muse_exit); + +MODULE_AUTHOR("Richard Weinberger "); +MODULE_DESCRIPTION("MTD in userspace"); +MODULE_LICENSE("GPLv2"); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 7233502ea991..7862c6df7e63 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -210,7 +210,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 32 +#define FUSE_KERNEL_MINOR_VERSION 33 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -483,6 +483,10 @@ enum fuse_opcode { /* CUSE specific operations */ CUSE_INIT = 4096, + /* MUSE specific operations */ + MUSE_INIT = 8192, + MUSE_ERASE = 8193, + /* Reserved opcodes: helpful to detect structure endian-ness */ CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ @@ -936,4 +940,23 @@ struct fuse_removemapping_one { #define FUSE_REMOVEMAPPING_MAX_ENTRY \ (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) +#define MUSE_INIT_INFO_MAX 4096 + +struct muse_init_in { + uint32_t fuse_major; + uint32_t fuse_minor; +}; + +struct muse_init_out { + uint32_t fuse_major; + uint32_t fuse_minor; + uint32_t max_read; + uint32_t max_write; +}; + +struct muse_erase_in { + uint64_t addr; + uint64_t len; +}; + #endif /* _LINUX_FUSE_H */