linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2
@ 2020-11-26 23:32 Richard Weinberger
  2020-11-26 23:32 ` [PATCH 1/7] fuse: Export fuse_simple_request Richard Weinberger
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:32 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

First of all, sorry for sending v2 before getting a review.
I found a much better way to process IO and to get rid of all hacks
from v1.


When working with flash devices a common task is emulating them to run various
tests or inspect dumps from real hardware. To achieve that we have plenty of
emulators in the mtd subsystem: mtdram, block2mtd, nandsim.

Each of them implements a adhoc MTD and have various drawbacks.
Over the last years some developers tried to extend them but these attempts
often got rejected because they added just more adhoc feature instead of
addressing overall problems.

MUSE is a novel approach to address the need of advanced MTD emulators.
Advanced means in this context supporting different (vendor specific) image
formats, different ways for fault injection (fuzzing) and recoding/replaying
IOs to emulate power cuts.

The core goal of MUSE is having the complexity on the userspace side and
only a small MTD driver in kernelspace.
While playing with different approaches I realized that FUSE offers everything
we need. So MUSE is a little like CUSE except that it does not implement a
bare character device but an MTD.

To get early feedback I'm sending this series as RFC, so don't consider it as
ready to merge yet.

Open issues are:

1. Init parameter passing
Currently parameter passing borrowed the logic from CUSE and parameters are
passed as stringy key value pairs.
Most MTD paramerters are numbers (erase size, etc..) so passing them via
struct muse_init_out seems more natural.
But I plan to pass also pure string parameters such as an mtdparts command line.
What is the perffered way these days in FUSE?
Am I allowed to embed structs such as struct mtd_info_user (mtd-abi.h) in
muse_init_out?

2. OOB (out of band) handling
So far OOB support is not implemented. I plan to do so.
Mabye just the bare minimum to make jffs2 happy.
I'm still digging into FUSE to find a nice way how to implement
it.

Changes since v1:
- Rewrote IO path, fuse_direct_io() is no longer used.
  Instead of cheating fuse_direct_io() use custom ops to implement
  reading and writing. That way MUSE no longer needs a dummy file object
  nor a fuse file object.
  In MTD all IO is synchronous and operations on kernel buffers, this
  makes IO processing simple for MUSE.
- Support for bad blocks.
- No more (ab)use of FUSE ops such as FUSE_FSYNC.
- Major code cleanup.

This series can also be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git muse_v2

Richard Weinberger (7):
  fuse: Export fuse_simple_request
  fuse: Export IO helpers
  fuse: Make cuse_parse_one a common helper
  mtd: Add MTD_MUSE flag
  fuse: Add MUSE specific defines FUSE interface
  fuse: Implement MUSE: MTD in userspace
  MAINTAINERS: Add entry for MUSE

 MAINTAINERS                |   7 +
 fs/fuse/Kconfig            |  15 +
 fs/fuse/Makefile           |   2 +
 fs/fuse/cuse.c             |  58 +--
 fs/fuse/dev.c              |   1 +
 fs/fuse/file.c             |  16 +-
 fs/fuse/fuse_i.h           |  18 +
 fs/fuse/helper.c           |  70 ++++
 fs/fuse/muse.c             | 730 +++++++++++++++++++++++++++++++++++++
 include/uapi/linux/fuse.h  |  73 +++-
 include/uapi/mtd/mtd-abi.h |   1 +
 11 files changed, 920 insertions(+), 71 deletions(-)
 create mode 100644 fs/fuse/helper.c
 create mode 100644 fs/fuse/muse.c

-- 
2.26.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/7] fuse: Export fuse_simple_request
  2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
@ 2020-11-26 23:32 ` Richard Weinberger
  2020-11-26 23:32 ` [PATCH 2/7] fuse: Export IO helpers Richard Weinberger
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:32 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

MUSE will use this function to issue requests,
so export it.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 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)
 {
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/7] fuse: Export IO helpers
  2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
  2020-11-26 23:32 ` [PATCH 1/7] fuse: Export fuse_simple_request Richard Weinberger
@ 2020-11-26 23:32 ` Richard Weinberger
  2020-11-26 23:32 ` [PATCH 3/7] fuse: Make cuse_parse_one a common helper Richard Weinberger
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:32 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

MUSE will use this functions in its IO path,
so export them.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 fs/fuse/file.c   | 16 +++-------------
 fs/fuse/fuse_i.h | 16 ++++++++++++++++
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index c03034e8c152..ed91ca8b1203 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -20,8 +20,8 @@
 #include <linux/uio.h>
 #include <linux/fs.h>
 
-static struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
-				      struct fuse_page_desc **desc)
+struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
+			       struct fuse_page_desc **desc)
 {
 	struct page **pages;
 
@@ -31,6 +31,7 @@ static struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
 
 	return pages;
 }
+EXPORT_SYMBOL_GPL(fuse_pages_alloc);
 
 static int fuse_send_open(struct fuse_mount *fm, u64 nodeid, struct file *file,
 			  int opcode, struct fuse_open_out *outargp)
@@ -1338,17 +1339,6 @@ static inline void fuse_page_descs_length_init(struct fuse_page_desc *descs,
 		descs[i].length = PAGE_SIZE - descs[i].offset;
 }
 
-static inline unsigned long fuse_get_user_addr(const struct iov_iter *ii)
-{
-	return (unsigned long)ii->iov->iov_base + ii->iov_offset;
-}
-
-static inline size_t fuse_get_frag_size(const struct iov_iter *ii,
-					size_t max_size)
-{
-	return min(iov_iter_single_seg_count(ii), max_size);
-}
-
 static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
 			       size_t *nbytesp, int write,
 			       unsigned int max_pages)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index d51598017d13..d23954908610 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -31,6 +31,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/refcount.h>
 #include <linux/user_namespace.h>
+#include <linux/uio.h>
 
 /** Default max number of pages that can be used in a single read request */
 #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
@@ -858,6 +859,17 @@ static inline u64 fuse_get_attr_version(struct fuse_conn *fc)
 	return atomic64_read(&fc->attr_version);
 }
 
+static inline unsigned long fuse_get_user_addr(const struct iov_iter *ii)
+{
+	return (unsigned long)ii->iov->iov_base + ii->iov_offset;
+}
+
+static inline size_t fuse_get_frag_size(const struct iov_iter *ii,
+					size_t max_size)
+{
+	return min(iov_iter_single_seg_count(ii), max_size);
+}
+
 /** Device operations */
 extern const struct file_operations fuse_dev_operations;
 
@@ -1210,4 +1222,8 @@ 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);
 
+/* file.c */
+struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
+			       struct fuse_page_desc **desc);
+
 #endif /* _FS_FUSE_I_H */
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/7] fuse: Make cuse_parse_one a common helper
  2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
  2020-11-26 23:32 ` [PATCH 1/7] fuse: Export fuse_simple_request Richard Weinberger
  2020-11-26 23:32 ` [PATCH 2/7] fuse: Export IO helpers Richard Weinberger
@ 2020-11-26 23:32 ` Richard Weinberger
  2020-11-26 23:32 ` [PATCH 4/7] mtd: Add MTD_MUSE flag Richard Weinberger
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:32 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

This function will be used by MUSE too, let's share it.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 fs/fuse/Kconfig  |  4 +++
 fs/fuse/Makefile |  1 +
 fs/fuse/cuse.c   | 58 +--------------------------------------
 fs/fuse/fuse_i.h |  2 ++
 fs/fuse/helper.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 78 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 45082269e698..fe8515844064 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 d23954908610..8eba93cd8fb8 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1225,5 +1225,7 @@ void fuse_dax_cancel_work(struct fuse_conn *fc);
 /* file.c */
 struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
 			       struct fuse_page_desc **desc);
+/* 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..0c828daf8e8a
--- /dev/null
+++ b/fs/fuse/helper.c
@@ -0,0 +1,70 @@
+// 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 <tj@kernel.org>
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+
+#include "fuse_i.h"
+
+/**
+ * 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);
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/7] mtd: Add MTD_MUSE flag
  2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
                   ` (2 preceding siblings ...)
  2020-11-26 23:32 ` [PATCH 3/7] fuse: Make cuse_parse_one a common helper Richard Weinberger
@ 2020-11-26 23:32 ` Richard Weinberger
  2020-11-26 23:32 ` [PATCH 5/7] fuse: Add MUSE specific defines FUSE interface Richard Weinberger
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:32 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

This flag will get set if an MTD is implemeted in userspace
using MUSE.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 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
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/7] fuse: Add MUSE specific defines FUSE interface
  2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
                   ` (3 preceding siblings ...)
  2020-11-26 23:32 ` [PATCH 4/7] mtd: Add MTD_MUSE flag Richard Weinberger
@ 2020-11-26 23:32 ` Richard Weinberger
  2020-11-26 23:32 ` [PATCH 6/7] fuse: Implement MUSE: MTD in userspace Richard Weinberger
  2020-11-26 23:33 ` [PATCH 7/7] MAINTAINERS: Add entry for MUSE Richard Weinberger
  6 siblings, 0 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:32 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

Raise the FUSE API minor version to 33 and add all
MUSE specific operations and data structures.

MUSE_INIT: Initialize a new connection and install the MTD
MUSE_ERASE: Erase a block
MUSE_READ: Read a page
MUSE_WRITE: Write a page
MUSE_MARKBAD: Mark a block as bad
MUSE_ISBAD: Check whether a block is bad
MUSE_SYNC: Flush all cached data

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 include/uapi/linux/fuse.h | 73 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 72 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 7233502ea991..2f7cbe5ce434 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -175,6 +175,10 @@
  *
  *  7.32
  *  - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
+ *
+ *  7.33
+ *  - add support for MUSE: MUSE_INIT, MUSE_ERASE, MUSE_READ, MUSE_WRITE,
+ *    MUSE_MARKBAD, MUSE_ISBAD and MUSE_SYNC
  */
 
 #ifndef _LINUX_FUSE_H
@@ -210,7 +214,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 +487,15 @@ enum fuse_opcode {
 	/* CUSE specific operations */
 	CUSE_INIT		= 4096,
 
+	/* MUSE specific operations */
+	MUSE_INIT		= 8192,
+	MUSE_ERASE		= 8193,
+	MUSE_READ		= 8194,
+	MUSE_WRITE		= 8195,
+	MUSE_MARKBAD		= 8196,
+	MUSE_ISBAD		= 8197,
+	MUSE_SYNC		= 8198,
+
 	/* 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 +949,62 @@ 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;
+};
+
+struct muse_read_in {
+	uint64_t	dataaddr;
+	uint64_t	datalen;
+	uint32_t	flags;
+	uint32_t	padding;
+};
+
+struct muse_read_out {
+	uint64_t	datalen;
+	uint32_t	soft_error;
+	uint32_t	padding;
+};
+
+struct muse_write_in {
+	uint64_t	dataaddr;
+	uint64_t	datalen;
+	uint32_t	flags;
+	uint32_t	padding;
+};
+
+struct muse_write_out {
+	uint64_t	datalen;
+	uint32_t	soft_error;
+	uint32_t	padding;
+};
+
+struct muse_markbad_in {
+	uint64_t	addr;
+};
+
+struct muse_isbad_in {
+	uint64_t	addr;
+};
+
+struct muse_isbad_out {
+	uint32_t	result;
+	uint32_t	padding;
+};
+
 #endif /* _LINUX_FUSE_H */
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/7] fuse: Implement MUSE: MTD in userspace
  2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
                   ` (4 preceding siblings ...)
  2020-11-26 23:32 ` [PATCH 5/7] fuse: Add MUSE specific defines FUSE interface Richard Weinberger
@ 2020-11-26 23:32 ` Richard Weinberger
  2020-11-26 23:33 ` [PATCH 7/7] MAINTAINERS: Add entry for MUSE Richard Weinberger
  6 siblings, 0 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:32 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

MUSE allows implementing a MTD in userspace.
So far userspace has control over mtd_read, mtd_write, mtd_erase,
mtd_block_isbad, mtd_block_markbad, 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 <richard@nod.at>
---
 fs/fuse/Kconfig  |  11 +
 fs/fuse/Makefile |   1 +
 fs/fuse/muse.c   | 730 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 742 insertions(+)
 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..b947f5aa2e1c
--- /dev/null
+++ b/fs/fuse/muse.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MUSE: MTD in userspace
+ * Copyright (C) 2020 sigma star gmbh
+ * Author: Richard Weinberger <richard@nod.at>
+ */
+
+#define pr_fmt(fmt) "MUSE: " fmt
+
+#include <linux/fuse.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+
+#include "fuse_i.h"
+
+static struct file_operations muse_ctrl_fops;
+
+/*
+ * struct muse_conn - MUSE connection object.
+ *
+ * @fm: FUSE mount object.
+ * @fc: FUSE connection object.
+ * @mtd: MTD object.
+ * @init_done: true when the MTD was registered.
+ *
+ * Describes a connection to a userspace server.
+ * Each connection implements a single MTD.
+ */
+struct muse_conn {
+	struct fuse_mount fm;
+	struct fuse_conn fc;
+	struct mtd_info mtd;
+	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 void muse_fc_release(struct fuse_conn *fc)
+{
+	struct muse_conn *mc = container_of(fc, struct muse_conn, fc);
+
+	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);
+	ssize_t ret;
+
+	inarg.addr = instr->addr;
+	inarg.len = instr->len;
+
+	args.opcode = MUSE_ERASE;
+	args.nodeid = FUSE_ROOT_ID;
+	args.in_numargs = 1;
+	args.in_args[0].size = sizeof(inarg);
+	args.in_args[0].value = &inarg;
+
+	ret = fuse_simple_request(fm, &args);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int muse_mtd_markbad(struct mtd_info *mtd, loff_t addr)
+{
+	struct muse_conn *mc = mtd->priv;
+	struct fuse_mount *fm = &mc->fm;
+	struct muse_markbad_in inarg;
+	FUSE_ARGS(args);
+	ssize_t ret;
+
+	inarg.addr = addr;
+
+	args.opcode = MUSE_MARKBAD;
+	args.nodeid = FUSE_ROOT_ID;
+	args.in_numargs = 1;
+	args.in_args[0].size = sizeof(inarg);
+	args.in_args[0].value = &inarg;
+
+	ret = fuse_simple_request(fm, &args);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int muse_mtd_isbad(struct mtd_info *mtd, loff_t addr)
+{
+	struct muse_conn *mc = mtd->priv;
+	struct fuse_mount *fm = &mc->fm;
+	struct muse_isbad_in inarg;
+	struct muse_isbad_out outarg;
+	FUSE_ARGS(args);
+	ssize_t ret;
+
+	inarg.addr = addr;
+
+	args.opcode = MUSE_ISBAD;
+	args.nodeid = FUSE_ROOT_ID;
+	args.in_numargs = 1;
+	args.in_args[0].size = sizeof(inarg);
+	args.in_args[0].value = &inarg;
+	args.out_numargs = 1;
+	args.out_args[0].size = sizeof(outarg);
+	args.out_args[0].value = &outarg;
+
+	ret = fuse_simple_request(fm, &args);
+	if (ret < 0)
+		return ret;
+
+	return outarg.result;
+}
+
+static void muse_mtd_sync(struct mtd_info *mtd)
+{
+	struct muse_conn *mc = mtd->priv;
+	struct fuse_mount *fm = &mc->fm;
+	FUSE_ARGS(args);
+
+	args.opcode = MUSE_SYNC;
+	args.nodeid = FUSE_ROOT_ID;
+	args.in_numargs = 0;
+
+	fuse_simple_request(fm, &args);
+}
+
+static ssize_t muse_send_write(struct fuse_args_pages *ap, struct fuse_mount *fm,
+			       loff_t from, size_t count, int *soft_error)
+{
+	struct fuse_args *args = &ap->args;
+	ssize_t ret;
+
+	struct muse_write_in in;
+	struct muse_write_out out;
+
+	in.dataaddr = from;
+	in.datalen = count;
+	in.flags = 0;
+	args->opcode = MUSE_WRITE;
+	args->nodeid = FUSE_ROOT_ID;
+	args->in_numargs = 2;
+	args->in_args[0].size = sizeof(in);
+	args->in_args[0].value = &in;
+	/*
+	 * args->in_args[1].value was set in set_ap_inout_bufs()
+	 */
+	args->in_args[1].size = count;
+	args->out_numargs = 1;
+	args->out_args[0].size = sizeof(out);
+	args->out_args[0].value = &out;
+
+	ret = fuse_simple_request(fm, &ap->args);
+	if (ret < 0)
+		goto out;
+
+	ret = out.datalen;
+	*soft_error = out.soft_error;
+
+out:
+	return ret;
+}
+
+static ssize_t muse_send_read(struct fuse_args_pages *ap, struct fuse_mount *fm,
+			      loff_t from, size_t count, int *soft_error)
+{
+	struct fuse_args *args = &ap->args;
+	ssize_t ret;
+
+	struct muse_read_in in;
+	struct muse_read_out out;
+
+	in.dataaddr = from;
+	in.datalen = count;
+	in.flags = 0;
+	args->opcode = MUSE_READ;
+	args->nodeid = FUSE_ROOT_ID;
+	args->in_numargs = 1;
+	args->in_args[0].size = sizeof(in);
+	args->in_args[0].value = &in;
+	args->out_argvar = true;
+	args->out_numargs = 2;
+	args->out_args[0].size = sizeof(out);
+	args->out_args[0].value = &out;
+	/*
+	 * args->out_args[1].value was set in set_ap_inout_bufs()
+	 */
+	args->out_args[1].size = count;
+
+	ret = fuse_simple_request(fm, &ap->args);
+	if (ret < 0)
+		goto out;
+
+	ret = out.datalen;
+	*soft_error = out.soft_error;
+
+out:
+	return ret;
+}
+
+/*
+ * set_ap_inout_bufs - Set in/out buffers for fuse args
+ *
+ * @ap: FUSE args pages object
+ * @iter: IOV iter which describes source/destination of the IO operation
+ * @count: Inputs the max amount of data we can process,
+ *	   outputs the amount of data @iter has left.
+ * @write: If non-zero, this is a write operation, read otherwise.
+ *
+ * This function takes a IOV iter object and sets up FUSE args pointer.
+ * Since in MTD all buffers are kernel memory we can directly use
+ * fuse_get_user_addr().
+ */
+static void set_ap_inout_bufs(struct fuse_args_pages *ap, struct iov_iter *iter,
+				size_t *count, int write)
+{
+	unsigned long addr;
+	size_t frag_size;
+
+	addr = fuse_get_user_addr(iter);
+	frag_size = fuse_get_frag_size(iter, *count);
+
+	if (write)
+		ap->args.in_args[1].value = (void *)addr;
+	else
+		ap->args.out_args[1].value = (void *)addr;
+
+	iov_iter_advance(iter, frag_size);
+	*count = frag_size;
+}
+
+/*
+ * muse_do_io - MUSE main IO processing function.
+ *
+ * @mc: MUSE connection object.
+ * @ops: MTD read/write operation object.
+ * @pos: Where to start reading/writing on the MTD.
+ * @retcode: Outputs the return code for the MTD subsystem.
+ * @write: If non-zero, this is a write operation, read otherwise.
+ *
+ * This function is responsible for processing reads and writes to the MTD.
+ * It directly takes @pos and @ops from the MTD subsystem.
+ * All IO is synchronous and buffers provided by @ops have to be kernel memory.
+ * Each MUSE_READ/MUSE_WRITE operation is at most mtd->writebuffer long,
+ * such that the userspace server can assume that each operaion affects at most
+ * one page.
+ * The userspace server can inject also custom errors into the IO path,
+ * mostly -EUCLEAN to signal fixed bit-flips or -EBADMSG for uncorrectable
+ * bit-flips.
+ *
+ * It returns the amount of processed bytes and via @retcode the return code
+ * for the MTD subsystem.
+ */
+static ssize_t muse_do_io(struct muse_conn *mc, struct mtd_oob_ops *ops,
+			  loff_t pos, int *retcode, int write)
+{
+	struct kvec iov = { .iov_base = ops->datbuf, .iov_len = ops->len };
+	struct fuse_mount *fm = &mc->fm;
+	struct fuse_conn *fc = fm->fc;
+	size_t fc_max_io = write ? fc->max_write : fc->max_read;
+	size_t count;
+	size_t retlen = 0;
+	struct fuse_args_pages ap;
+	unsigned int max_pages;
+	int bitflips = 0;
+	int eccerrors = 0;
+	ssize_t ret = 0;
+	struct iov_iter iter;
+
+	/*
+	 * TODO: Implement OOB support
+	 */
+	if (ops->mode != MTD_OPS_PLACE_OOB || ops->ooblen) {
+		ret = -ENOTSUPP;
+		goto out;
+	}
+
+	iov_iter_kvec(&iter, write ? WRITE : READ, &iov, 1, ops->len);
+
+	/*
+	 * A full page needs to fit into a single FUSE request.
+	 */
+	if (fc_max_io < mc->mtd.writebufsize) {
+		ret = -ENOBUFS;
+		goto out;
+	}
+
+	count = iov_iter_count(&iter);
+
+	max_pages = iov_iter_npages(&iter, fc->max_pages);
+	memset(&ap, 0, sizeof(ap));
+
+	ap.pages = fuse_pages_alloc(max_pages, GFP_KERNEL, &ap.descs);
+	if (!ap.pages) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	*retcode = 0;
+
+	while (count) {
+		size_t nbytes = min_t(size_t, count, mc->mtd.writebufsize);
+		int soft_error;
+
+		set_ap_inout_bufs(&ap, &iter, &nbytes, write);
+
+		if (write)
+			ret = muse_send_write(&ap, fm, pos, nbytes, &soft_error);
+		else
+			ret = muse_send_read(&ap, fm, pos, nbytes, &soft_error);
+
+		kfree(ap.pages);
+		ap.pages = NULL;
+
+		if (ret < 0) {
+			iov_iter_revert(&iter, nbytes);
+			break;
+		}
+
+		if (soft_error) {
+			/*
+			 * Userspace wants to inject an error code.
+			 */
+
+			if (write) {
+				/*
+				 * For writes, take it as-is.
+				 */
+				ret = soft_error;
+				break;
+			}
+
+			/*
+			 * -EUCLEAN and -EBADMSG are special for reads
+			 * in MTD, it expects from a device to return all
+			 * requsted data even if there are (un)correctable errors.
+			 * The upper layer, such as UBI, has to deal with them.
+			 */
+			if (soft_error == -EUCLEAN) {
+				bitflips++;
+			} else if (soft_error == -EBADMSG) {
+				eccerrors++;
+			} else {
+				ret = soft_error;
+				break;
+			}
+		}
+
+		/*
+		 * No short reads are allowed in MTD.
+		 */
+		if (ret != nbytes) {
+			iov_iter_revert(&iter, nbytes - ret);
+			ret = -EIO;
+			break;
+		}
+
+		count -= ret;
+		retlen += ret;
+		pos += ret;
+
+		if (count) {
+			max_pages = iov_iter_npages(&iter, fc->max_pages);
+			memset(&ap, 0, sizeof(ap));
+			ap.pages = fuse_pages_alloc(max_pages, GFP_KERNEL, &ap.descs);
+			if (!ap.pages)
+				break;
+		}
+	}
+
+	kfree(ap.pages);
+
+	if (bitflips)
+		*retcode = -EUCLEAN;
+	if (eccerrors)
+		*retcode = -EBADMSG;
+
+out:
+	/*
+	 * If ret is set, it must be a fatal error which overrides
+	 * -EUCLEAN and -EBADMSG.
+	 */
+	if (ret < 0)
+		*retcode = ret;
+
+	return retlen;
+}
+
+static int muse_mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
+{
+	struct muse_conn *mc = mtd->priv;
+	int retcode;
+
+	ops->retlen = muse_do_io(mc, ops, from, &retcode, 0);
+
+	return retcode;
+}
+
+static int muse_mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
+{
+	struct muse_conn *mc = mtd->priv;
+	int retcode;
+
+	ops->retlen = muse_do_io(mc, ops, to, &retcode, 1);
+
+	return retcode;
+}
+
+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) {
+			ret = kstrtoul(val, 10, &req.mi.type);
+			if (ret)
+				goto out;
+		} else if (strcmp(key, "FLAGS") == 0) {
+			ret = kstrtoul(val, 10, &req.mi.flags);
+			if (ret)
+				goto out;
+		} else if (strcmp(key, "SIZE") == 0) {
+			ret = kstrtoul(val, 10, &req.mi.size);
+			if (ret)
+				goto out;
+		} else if (strcmp(key, "WRITESIZE") == 0) {
+			ret = kstrtoul(val, 10, &req.mi.writesize);
+			if (ret)
+				goto out;
+		} else if (strcmp(key, "ERASESIZE") == 0) {
+			ret = kstrtoul(val, 10, &req.mi.erasesize);
+			if (ret)
+				goto out;
+		} 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;
+
+	/*
+	 * MTD_ABSENT and MTD_UBIVOLUME and special, and can only be used by
+	 * internal MTD drivers. Allowing userspace to emulate them asks for
+	 * trouble.
+	 */
+	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 *mia = 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 = &mia->ap;
+	struct muse_init_out *arg = &mia->out;
+	struct page *page = ap->pages[0];
+	struct mtd_info *mtd = &mc->mtd;
+	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, mtd);
+	if (ret)
+		goto abort;
+
+	mtd->_erase = muse_mtd_erase;
+	mtd->_sync = muse_mtd_sync;
+	mtd->_read_oob = muse_mtd_read_oob;
+	mtd->_write_oob = muse_mtd_write_oob;
+	mtd->_get_device = muse_mtd_get_device;
+	mtd->_put_device = muse_mtd_put_device;
+
+	/*
+	 * Bad blocks make only sense on NAND devices.
+	 * As soon _block_isbad is set, upper layer such as
+	 * UBI expects a working _block_isbad, so userspace
+	 * has to implement MUSE_ISBAD.
+	 */
+	if (mtd_type_is_nand(mtd)) {
+		mtd->_block_isbad = muse_mtd_isbad;
+		mtd->_block_markbad = muse_mtd_markbad;
+	}
+
+	mtd->priv = 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 < mtd->writebufsize || fc->max_read < mtd->writebufsize)
+		goto abort;
+
+	if (mtd_device_register(mtd, NULL, 0) != 0)
+		goto abort;
+
+	mc->init_done = true;
+
+	kfree(mia);
+	__free_page(page);
+	return;
+
+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 *mia;
+	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;
+
+	mia = kzalloc(sizeof(*mia), GFP_KERNEL);
+	if (!mia)
+		goto err_page;
+
+	ap = &mia->ap;
+	mia->in.fuse_major = FUSE_KERNEL_VERSION;
+	mia->in.fuse_minor = FUSE_KERNEL_MINOR_VERSION;
+	ap->args.opcode = MUSE_INIT;
+	ap->args.in_numargs = 1;
+	ap->args.in_args[0].size = sizeof(mia->in);
+	ap->args.in_args[0].value = &mia->in;
+	ap->args.out_numargs = 2;
+	ap->args.out_args[0].size = sizeof(mia->out);
+	ap->args.out_args[0].value = &mia->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 = &mia->page;
+	ap->descs = &mia->desc;
+	mia->page = page;
+	mia->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(mia);
+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;
+
+	/*
+	 * Paranoia check.
+	 */
+	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 <richard@nod.at>");
+MODULE_DESCRIPTION("MTD in userspace");
+MODULE_LICENSE("GPL");
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 7/7] MAINTAINERS: Add entry for MUSE
  2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
                   ` (5 preceding siblings ...)
  2020-11-26 23:32 ` [PATCH 6/7] fuse: Implement MUSE: MTD in userspace Richard Weinberger
@ 2020-11-26 23:33 ` Richard Weinberger
  6 siblings, 0 replies; 8+ messages in thread
From: Richard Weinberger @ 2020-11-26 23:33 UTC (permalink / raw)
  To: miklos
  Cc: miquel.raynal, vigneshr, linux-fsdevel, linux-kernel, linux-mtd,
	Richard Weinberger

Since MUSE lifes in fs/fuse/, make sure that linux-mtd@ is CC'ed
on patches such that MTD related aspects of changes can be reviewed.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 94ac10a153c7..92359bb8d133 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11959,6 +11959,13 @@ L:	linux-usb@vger.kernel.org
 S:	Maintained
 F:	drivers/usb/musb/
 
+MUSE: MTD IN USERSPACE DRIVER
+M:	Richard Weinberger <richard@nod.at>
+L:	linux-mtd@lists.infradead.org
+L:	linux-fsdevel@vger.kernel.org
+S:	Maintained
+F:	fs/fuse/muse.c
+
 MXL301RF MEDIA DRIVER
 M:	Akihiro Tsukada <tskd08@gmail.com>
 L:	linux-media@vger.kernel.org
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-11-26 23:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-26 23:32 [PATCH 0/7] [RFC] MUSE: Userspace backed MTD v2 Richard Weinberger
2020-11-26 23:32 ` [PATCH 1/7] fuse: Export fuse_simple_request Richard Weinberger
2020-11-26 23:32 ` [PATCH 2/7] fuse: Export IO helpers Richard Weinberger
2020-11-26 23:32 ` [PATCH 3/7] fuse: Make cuse_parse_one a common helper Richard Weinberger
2020-11-26 23:32 ` [PATCH 4/7] mtd: Add MTD_MUSE flag Richard Weinberger
2020-11-26 23:32 ` [PATCH 5/7] fuse: Add MUSE specific defines FUSE interface Richard Weinberger
2020-11-26 23:32 ` [PATCH 6/7] fuse: Implement MUSE: MTD in userspace Richard Weinberger
2020-11-26 23:33 ` [PATCH 7/7] MAINTAINERS: Add entry for MUSE Richard Weinberger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).