intel-gfx.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [Intel-gfx] [PATCH] lib: Add a YAML emitter
@ 2020-02-13 23:23 Chris Wilson
  2020-02-14  0:13 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for " Patchwork
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Chris Wilson @ 2020-02-13 23:23 UTC (permalink / raw)
  To: intel-gfx; +Cc: Jani Nikula

Provide a library to generate correct YAML for use in structured debugfs
or similar information dumps. This will be useful to pull our large
information dumps into a forwards compatible, parse-able file-format by
forcing some structure upon ourselves!

Originally from https://github.com/yaml/libyaml/blob/master/src/emitter.c
under a permissive MIT licence.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>

---
Converting to kerneldoc is about the last major task. It uses an
opencoded stack struct which might be nice to convert to a library,
maybe just use XArray?

It has been dogfooded to convert i915_engine_info (and friends)
https://patchwork.freedesktop.org/patch/353209/?series=73403&rev=1
---
 include/linux/yaml-events.h |  259 ++++
 include/linux/yaml.h        |  188 +++
 lib/Kconfig                 |   11 +
 lib/Makefile                |    2 +
 lib/yaml/Makefile           |    8 +
 lib/yaml/test-yaml.c        |  123 ++
 lib/yaml/yaml-emitter.c     | 2539 +++++++++++++++++++++++++++++++++++
 lib/yaml/yaml-emitter.h     |  140 ++
 lib/yaml/yaml-events.c      |  424 ++++++
 lib/yaml/yaml-simple.c      |  227 ++++
 10 files changed, 3921 insertions(+)
 create mode 100644 include/linux/yaml-events.h
 create mode 100644 include/linux/yaml.h
 create mode 100644 lib/yaml/Makefile
 create mode 100644 lib/yaml/test-yaml.c
 create mode 100644 lib/yaml/yaml-emitter.c
 create mode 100644 lib/yaml/yaml-emitter.h
 create mode 100644 lib/yaml/yaml-events.c
 create mode 100644 lib/yaml/yaml-simple.c

diff --git a/include/linux/yaml-events.h b/include/linux/yaml-events.h
new file mode 100644
index 000000000000..33d1bb227e65
--- /dev/null
+++ b/include/linux/yaml-events.h
@@ -0,0 +1,259 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef __LINUX_YAML_EVENTS_H__
+#define __LINUX_YAML_EVENTS_H__
+
+#include <linux/gfp.h>
+#include <linux/types.h>
+#include <linux/list.h>
+
+/** The version directive data. */
+struct yaml_version_directive {
+	int major;
+	int minor;
+};
+
+/** The tag directive data. */
+struct yaml_tag_directive {
+	char *handle;
+	char *prefix;
+};
+
+/** The pointer position. */
+struct yaml_mark {
+	size_t index;
+	size_t line;
+	size_t column;
+};
+
+enum yaml_event_type {
+	YAML_NO_EVENT = 0,
+
+	YAML_STREAM_START_EVENT,
+	YAML_STREAM_END_EVENT,
+
+	YAML_DOCUMENT_START_EVENT,
+	YAML_DOCUMENT_END_EVENT,
+
+	YAML_ALIAS_EVENT,
+	YAML_SCALAR_EVENT,
+
+	YAML_SEQUENCE_START_EVENT,
+	YAML_SEQUENCE_END_EVENT,
+
+	YAML_MAPPING_START_EVENT,
+	YAML_MAPPING_END_EVENT
+};
+
+struct yaml_event {
+	enum yaml_event_type type;
+	struct list_head link;
+
+	union {
+		/** The stream parameters (for YAML_STREAM_START_EVENT). */
+		struct {
+		} stream_start;
+
+		/** The document parameters (for YAML_DOCUMENT_START_EVENT). */
+		struct {
+			struct yaml_version_directive *version;
+
+			struct {
+				struct yaml_tag_directive *start;
+				struct yaml_tag_directive *end;
+			} tags;
+
+			int implicit;
+		} document_start;
+
+		/** The document end parameters (for YAML_DOCUMENT_END_EVENT). */
+		struct {
+			int implicit;
+		} document_end;
+
+		/** The alias parameters (for YAML_ALIAS_EVENT). */
+		struct {
+			char *anchor;
+		} alias;
+
+		/** The scalar parameters (for YAML_SCALAR_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			char *value;
+			size_t length;
+			int plain_implicit;
+			int quoted_implicit;
+			enum yaml_scalar_style style;
+		} scalar;
+
+		/** The sequence parameters (for YAML_SEQUENCE_START_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			int implicit;
+			enum yaml_sequence_style style;
+		} sequence_start;
+
+		/** The mapping parameters (for YAML_MAPPING_START_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			int implicit;
+			enum yaml_mapping_style style;
+		} mapping_start;
+	};
+
+	struct yaml_mark start_mark;
+	struct yaml_mark end_mark;
+};
+
+struct yaml_event *yaml_stream_start_event_create(gfp_t gfp);
+struct yaml_event *yaml_stream_end_event_create(gfp_t gfp);
+
+/**
+ * Create the DOCUMENT-START event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[in]       version_directive       The %YAML directive value or
+ *                                          @c NULL.
+ * @param[in]       tag_directives_start    The beginning of the %TAG
+ *                                          directives list.
+ * @param[in]       tag_directives_end      The end of the %TAG directives
+ *                                          list.
+ * @param[in]       implicit                If the document start indicator is
+ *                                          implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_document_start_event_create(struct yaml_version_directive *version,
+				 struct yaml_tag_directive *start,
+				 struct yaml_tag_directive *end,
+				 int implicit,
+				 gfp_t gfp);
+
+/**
+ * Create the DOCUMENT-END event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[in]       implicit    If the document end indicator is implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_document_end_event_create(int implicit, gfp_t gfp);
+
+/**
+ * Create an ALIAS event.
+ *
+ * @param[in]       anchor      The anchor value.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_alias_event_create(const char *anchor, gfp_t gfp);
+
+/**
+ * Create a SCALAR event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or one of the @a plain_implicit and
+ * @a quoted_implicit flags must be set.
+ *
+ * @param[in]       anchor          The scalar anchor or @c NULL.
+ * @param[in]       tag             The scalar tag or @c NULL.
+ * @param[in]       value           The scalar value.
+ * @param[in]       length          The length of the scalar value.
+ * @param[in]       plain_implicit  If the tag may be omitted for the plain
+ *                                  style.
+ * @param[in]       quoted_implicit If the tag may be omitted for any
+ *                                  non-plain style.
+ * @param[in]       style           The scalar style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+			 const char *value, ssize_t length, bool steal,
+			 int plain_implicit, int quoted_implicit,
+			 enum yaml_scalar_style style,
+			 gfp_t gfp);
+
+/**
+ * Create a SEQUENCE-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[in]       anchor      The sequence anchor or @c NULL.
+ * @param[in]       tag         The sequence tag or @c NULL.
+ * @param[in]       implicit    If the tag may be omitted.
+ * @param[in]       style       The sequence style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_sequence_start_event_create(const char *anchor,
+				 const char *tag,
+				 int implicit,
+				 enum yaml_sequence_style style,
+				 gfp_t gfp);
+
+/**
+ * Create a SEQUENCE-END event.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_sequence_end_event_create(gfp_t gfp);
+
+/**
+ * Create a MAPPING-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[in]       anchor      The mapping anchor or @c NULL.
+ * @param[in]       tag         The mapping tag or @c NULL.
+ * @param[in]       implicit    If the tag may be omitted.
+ * @param[in]       style       The mapping style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_mapping_start_event_create(const char *anchor,
+				const char *tag,
+				int implicit,
+				enum yaml_mapping_style style,
+				gfp_t gfp);
+
+/**
+ * Create a MAPPING-END event.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_mapping_end_event_create(gfp_t gfp);
+
+/**
+ * Free any memory allocated for an event object.
+ *
+ * @param[in,out]   event   An event object.
+ */
+
+void yaml_event_delete(struct yaml_event *event);
+
+#endif /* __LINUX_YAML_EVENTS_H__ */
diff --git a/include/linux/yaml.h b/include/linux/yaml.h
new file mode 100644
index 000000000000..a262f4a793e9
--- /dev/null
+++ b/include/linux/yaml.h
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef __LINUX_YAML_H__
+#define __LINUX_YAML_H__
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct seq_file;
+
+struct yaml_emitter;
+struct yaml_event;
+
+/** Scalar styles. */
+enum yaml_scalar_style {
+	YAML_ANY_SCALAR_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_PLAIN_SCALAR_STYLE,
+
+	YAML_SINGLE_QUOTED_SCALAR_STYLE,
+	YAML_DOUBLE_QUOTED_SCALAR_STYLE,
+
+	YAML_LITERAL_SCALAR_STYLE,
+	YAML_FOLDED_SCALAR_STYLE,
+};
+
+/** Sequence styles. */
+enum yaml_sequence_style {
+	YAML_ANY_SEQUENCE_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_BLOCK_SEQUENCE_STYLE,
+	YAML_FLOW_SEQUENCE_STYLE,
+};
+
+/** Mapping styles. */
+enum yaml_mapping_style {
+	YAML_ANY_MAPPING_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_BLOCK_MAPPING_STYLE,
+	YAML_FLOW_MAPPING_STYLE
+};
+
+typedef int (*yaml_write_t)(void *data, const u8 *buffer, size_t size);
+
+/**
+ * Initialize an emitter.
+ *
+ * This function creates a new emitter object.  An application is responsible
+ * for destroying the object using the yaml_emitter_delete() function.
+ *
+ * @param[out]      emitter     An empty parser object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_emitter *yaml_emitter_create(gfp_t gfp);
+
+/**
+ * Destroy an emitter.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ */
+void yaml_emitter_destroy(struct yaml_emitter *emitter);
+
+/**
+ * Set a string output.
+ *
+ * The emitter will write the output characters to the @a output buffer of the
+ * size @a size.  The emitter will set @a size_written to the number of written
+ * bytes.  If the buffer is smaller than required, the emitter produces the
+ * YAML_WRITE_ERROR error.
+ *
+ * @param[in,out]   emitter         An emitter object.
+ * @param[in]       output          An output buffer.
+ * @param[in]       size            The buffer size.
+ * @param[in]       size_written    The pointer to save the number of written
+ *                                  bytes.
+ */
+void yaml_emitter_set_string(struct yaml_emitter *emitter,
+			     u8 *output, size_t size,
+			     size_t *size_written);
+
+void yaml_emitter_set_seq_file(struct yaml_emitter *emitter,
+			       struct seq_file *file);
+
+void yaml_emitter_set_output(struct yaml_emitter *emitter,
+			     yaml_write_t handler,
+			     void *data);
+
+/**
+ * Set if the output should be in the "canonical" format as in the YAML
+ * specification.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       canonical   If the output is canonical.
+ */
+void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical);
+
+/**
+ * Set the indentation increment.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       indent      The indentation increment (1 < . < 10).
+ */
+void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent);
+
+/**
+ * Set the preferred line width. @c -1 means unlimited.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       width       The preferred line width.
+ */
+void yaml_emitter_set_width(struct yaml_emitter *emitter, int width);
+
+/**
+ * Set if unescaped non-ASCII characters are allowed.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       unicode     If unescaped Unicode characters are allowed.
+ */
+void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode);
+
+/**
+ * Emit an event.
+ *
+ * The event object may be generated using the yaml_parser_parse() function.
+ * The emitter takes the responsibility for the event object and destroys its
+ * content after it is emitted. The event object is destroyed even if the
+ * function fails.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in,out]   event       An event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event);
+
+/**
+ * Flush the accumulated characters to the output.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem);
+
+gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp);
+
+struct yaml_emitter *yaml_open(yaml_write_t handler, void *data);
+struct yaml_emitter *yaml_open_file(struct seq_file *m);
+struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *written);
+
+void __yaml_mapping_start(struct yaml_emitter *emitter,
+			  enum yaml_mapping_style style);
+static inline void yaml_mapping_start(struct yaml_emitter *emitter)
+{
+	__yaml_mapping_start(emitter, YAML_ANY_MAPPING_STYLE);
+}
+void yaml_mapping_end(struct yaml_emitter *emitter);
+
+void __yaml_sequence_start(struct yaml_emitter *emitter,
+			   enum yaml_sequence_style style);
+static inline void yaml_sequence_start(struct yaml_emitter *emitter)
+{
+	__yaml_sequence_start(emitter, YAML_ANY_SEQUENCE_STYLE);
+}
+void yaml_sequence_end(struct yaml_emitter *emitter);
+
+__printf(2, 3)
+void yaml_alias_printf(struct yaml_emitter *emitter, const char *fmt, ...);
+
+__printf(2, 3)
+void yaml_scalar_printf(struct yaml_emitter *emitter, const char *fmt, ...);
+
+__printf(3, 4)
+void yaml_pair_printf(struct yaml_emitter *emitter,
+		      const char *name, const char *fmt, ...);
+
+void yaml_ascii85_encode(struct yaml_emitter *emitter,
+			 const void *data, size_t len, bool compressed);
+
+int yaml_close(struct yaml_emitter *emitter, const char **problem);
+
+#endif /* __LINUX_YAML_H__ */
diff --git a/lib/Kconfig b/lib/Kconfig
index 0cf875fd627c..da15068d60b5 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -648,6 +648,17 @@ config OBJAGG
 config STRING_SELFTEST
 	tristate "Test string functions"
 
+config YAML
+	tristate
+
+config YAML_EMITTER
+	tristate
+	select YAML
+
+config TEST_YAML
+	tristate "Small test module for libyaml"
+	depends on YAML
+
 endmenu
 
 config GENERIC_IOREMAP
diff --git a/lib/Makefile b/lib/Makefile
index 5d64890d6b6a..6805e56d2db8 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -241,6 +241,8 @@ obj-$(CONFIG_ASN1) += asn1_decoder.o
 
 obj-$(CONFIG_FONT_SUPPORT) += fonts/
 
+obj-$(CONFIG_YAML) += yaml/
+
 hostprogs	:= gen_crc32table
 hostprogs	+= gen_crc64table
 clean-files	:= crc32table.h
diff --git a/lib/yaml/Makefile b/lib/yaml/Makefile
new file mode 100644
index 000000000000..f5e8c1795914
--- /dev/null
+++ b/lib/yaml/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+yaml-objs = yaml-events.o
+yaml-objs-$(CONFIG_YAML_EMITTER) += yaml-emitter.o yaml-simple.o
+yaml-objs += $(yaml-objs-m)
+
+obj-$(CONFIG_YAML) += yaml.o
+obj-$(CONFIG_TEST_YAML) += test-yaml.o
diff --git a/lib/yaml/test-yaml.c b/lib/yaml/test-yaml.c
new file mode 100644
index 000000000000..5c3c7a928859
--- /dev/null
+++ b/lib/yaml/test-yaml.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL
+/*
+ * Copyright (c) 2020 Intel Corporation
+ */
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+struct dentry *root;
+
+#if IS_ENABLED(CONFIG_YAML_EMITTER)
+static int y_events_show(struct seq_file *m, void *data)
+{
+	struct yaml_emitter *emitter;
+	struct yaml_event *event;
+	const char *problem;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return -ENOMEM;
+
+	yaml_emitter_set_seq_file(emitter, m);
+
+	event = yaml_stream_start_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_start_event_create(NULL, NULL, NULL, true,
+						 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_mapping_start_event_create(NULL, NULL, true,
+						YAML_FLOW_MAPPING_STYLE,
+						GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_scalar_event_create(NULL, NULL,
+					 "General", -1, false,
+					 true, true, YAML_PLAIN_SCALAR_STYLE,
+					 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_scalar_event_create(NULL, NULL,
+					 "Kenobi", -1, false,
+					 true, true, YAML_PLAIN_SCALAR_STYLE,
+					 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_mapping_end_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_end_event_create(true, GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_stream_end_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	if (yaml_emitter_flush(emitter, &problem))
+		pr_err("test-yaml events failed: %s\n", problem);
+
+	yaml_emitter_destroy(emitter);
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(y_events);
+
+static int y_simple_show(struct seq_file *m, void *data)
+{
+	struct yaml_emitter *yaml;
+	const char *problem;
+
+	yaml = yaml_open_file(m);
+	if (!yaml)
+		return -ENOMEM;
+
+	yaml_mapping_start(yaml); {
+		yaml_scalar_printf(yaml, "map");
+		yaml_mapping_start(yaml); {
+			yaml_scalar_printf(yaml, "Hello");
+			yaml_mapping_start(yaml); {
+				yaml_pair_printf(yaml,
+						 "General", "%s", "Kenobi");
+			} yaml_mapping_end(yaml);
+		} yaml_mapping_end(yaml);
+
+		yaml_scalar_printf(yaml, "sequence");
+		yaml_sequence_start(yaml); {
+			yaml_scalar_printf(yaml, "1");
+			yaml_scalar_printf(yaml, "2");
+			yaml_scalar_printf(yaml, "3");
+			yaml_ascii85_encode(yaml, "4", 2, false);
+		} yaml_sequence_end(yaml);
+	} yaml_mapping_end(yaml);
+
+	if (yaml_close(yaml, &problem))
+		pr_err("test-yaml simple failed: %s\n", problem);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(y_simple);
+#endif
+
+static int __init test_yaml_init(void)
+{
+	root = debugfs_create_dir("libyaml", NULL);
+	if (!root)
+		return -ENOMEM;
+
+#if IS_ENABLED(CONFIG_YAML_EMITTER)
+	debugfs_create_file("emit-events", 0444, root, NULL, &y_events_fops);
+	debugfs_create_file("emit-simple", 0444, root, NULL, &y_simple_fops);
+#endif
+
+	return 0;
+}
+
+static void __exit test_yaml_exit(void)
+{
+	debugfs_remove(root);
+}
+
+module_init(test_yaml_init);
+module_exit(test_yaml_exit);
+MODULE_LICENSE("GPL");
diff --git a/lib/yaml/yaml-emitter.c b/lib/yaml/yaml-emitter.c
new file mode 100644
index 000000000000..a58116045624
--- /dev/null
+++ b/lib/yaml/yaml-emitter.c
@@ -0,0 +1,2539 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+#include "yaml-emitter.h"
+
+#define INITIAL_STACK_SIZE  16
+
+struct yaml_string {
+	const char *start;
+	const char *end;
+	const char *pos;
+};
+
+#define STRING_INIT(str, len) { (str), (str) + (len), (str) }
+
+#define YAML_STRING(name, str) \
+	struct yaml_string name = STRING_INIT(str, strlen(str))
+
+#define CHECK_AT(s, octet, offset)					\
+	((s).pos[offset] == (char)(octet))
+#define CHECK(s, octet) (CHECK_AT((s), (octet), 0))
+
+#define IS_ALPHA_AT(s, offset)						\
+	(isalnum((s).pos[offset]) ||					\
+	 (s).pos[offset] == '_' ||					\
+	 (s).pos[offset] == '-')
+#define IS_ALPHA(s)	IS_ALPHA_AT((s), 0)
+
+#define IS_ASCII_AT(s, offset) ((s).pos[offset] <= '\x7F')
+#define IS_ASCII(s)    IS_ASCII_AT((s), 0)
+
+#define IS_PRINTABLE_AT(s, offset) isprint((s).pos[offset])
+#define IS_PRINTABLE(s)    IS_PRINTABLE_AT((s), 0)
+
+#define IS_Z_AT(s, offset)    CHECK_AT((s), '\0', (offset))
+#define IS_Z(s)    IS_Z_AT((s), 0)
+
+#define IS_BOM_AT(s, offset)						\
+	(CHECK_AT((s), '\xEF', (offset) + 0) &&				\
+	 CHECK_AT((s), '\xBB', (offset) + 1) &&				\
+	 CHECK_AT((s), '\xBF', (offset) + 2))  /* BOM (#xFEFF) */
+#define IS_BOM(s)  IS_BOM_AT(s, 0)
+
+#define IS_SPACE_AT(s, offset)  CHECK_AT((s), ' ', (offset))
+#define IS_SPACE(s)	IS_SPACE_AT((s), 0)
+
+#define IS_TAB_AT(s, offset)    CHECK_AT((s), '\t', (offset))
+#define IS_TAB(s)	IS_TAB_AT((s), 0)
+
+#define IS_BLANK_AT(s, offset)						\
+	(IS_SPACE_AT((s), (offset)) || IS_TAB_AT((s), (offset)))
+#define IS_BLANK(s)	IS_BLANK_AT((s), 0)
+
+#define IS_BREAK_AT(s, offset)						\
+	(CHECK_AT((s), '\r', (offset)) || /* CR (#xD)*/			\
+	 CHECK_AT((s), '\n', (offset)) || /* LF (#xA) */		\
+	 (CHECK_AT((s), '\xC2', (offset)) &&				\
+	  CHECK_AT((s), '\x85', (offset) + 1)) || /* NEL (#x85) */	\
+	 (CHECK_AT((s), '\xE2', (offset)) &&				\
+	  CHECK_AT((s), '\x80', (offset) + 1) &&			\
+	  CHECK_AT((s), '\xA8', (offset) + 2)) ||   /* LS (#x2028) */	\
+	 (CHECK_AT((s), '\xE2', (offset)) &&				\
+	  CHECK_AT((s), '\x80', (offset) + 1) &&			\
+	    CHECK_AT((s), '\xA9', (offset) + 2)))  /* PS (#x2029) */
+#define IS_BREAK(s)	IS_BREAK_AT((s), 0)
+
+#define IS_CRLF_AT(s, offset)						\
+	(CHECK_AT((s), '\r', (offset)) && CHECK_AT((s), '\n', (offset) + 1))
+#define IS_CRLF(s)	IS_CRLF_AT((s), 0)
+
+#define IS_BREAKZ_AT(s, offset)						\
+	(IS_BREAK_AT((s), (offset)) || IS_Z_AT((s), (offset)))
+#define IS_BREAKZ(s)	IS_BREAKZ_AT((s), 0)
+
+#define IS_SPACEZ_AT(s, offset)						\
+	(IS_SPACE_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset)))
+#define IS_SPACEZ(s)	IS_SPACEZ_AT((s), 0)
+
+#define IS_BLANKZ_AT(s, offset)						\
+	(IS_BLANK_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset)))
+#define IS_BLANKZ(s)	IS_BLANKZ_AT((s), 0)
+
+static inline int __utf8_width(const struct yaml_string *s, int idx)
+{
+	return ((s->pos[idx] & 0x80) == 0x00 ? 1 :
+		(s->pos[idx] & 0xE0) == 0xC0 ? 2 :
+		(s->pos[idx] & 0xF0) == 0xE0 ? 3 :
+		(s->pos[idx] & 0xF8) == 0xF0 ? 4 :
+		0);
+}
+
+static inline int utf8_width(const struct yaml_string *s)
+{
+	return __utf8_width(s, 0);
+}
+
+#define ADVANCE(s)	((s).pos += utf8_width((&s)))
+
+static bool yaml_stack_extend(void **start, void **top, void **end);
+
+#define EMPTY(context, stack) ((stack).start == (stack).top)
+
+#define PUSH(context, stack, value)					\
+	(((stack).top != (stack).end ||					\
+	  !yaml_stack_extend((void **)&(stack).start,			\
+			     (void **)&(stack).top, (void **)&(stack).end)) ? \
+	 (*((stack).top++) = value, 0) :				\
+	 ((context)->errno = -ENOMEM))
+
+#define POP(context, stack) (*(--(stack).top))
+
+static int
+__set_error(struct yaml_emitter *emitter, const char *problem, int err)
+{
+	WARN_ON(emitter->errno);
+
+	emitter->errno = err;
+	emitter->problem = problem;
+
+	return err;
+}
+
+static int set_error(struct yaml_emitter *emitter, const char *problem)
+{
+	return __set_error(emitter, problem, EINVAL);
+}
+
+static int flush(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (unlikely(emitter->errno))
+		return emitter->errno;
+
+	emitter->buf.last = emitter->buf.pos;
+	emitter->buf.pos = emitter->buf.start;
+
+	if (emitter->buf.start == emitter->buf.last)
+		return 0;
+
+	err = emitter->write_handler(emitter->write_data,
+				     emitter->buf.start,
+				     emitter->buf.last - emitter->buf.start);
+	if (unlikely(err < 0))
+		return __set_error(emitter, "write error", err);
+
+	emitter->buf.last = emitter->buf.start;
+	emitter->buf.pos = emitter->buf.start;
+	return 0;
+}
+
+static int maybe_flush(struct yaml_emitter *emitter, int len)
+{
+	if (emitter->buf.pos + len < emitter->buf.end)
+		return 0;
+
+	return flush(emitter);
+}
+
+static int put(struct yaml_emitter *emitter, const char value)
+{
+	int err;
+
+	err = maybe_flush(emitter, 5);
+	if (err)
+		return err;
+
+	*emitter->buf.pos++ = value;
+	emitter->column++;
+	return 0;
+}
+
+static int put_break(struct yaml_emitter *emitter)
+{
+	int err;
+
+	err =  put(emitter, '\n');
+	if (err)
+		return err;
+
+	emitter->column = 0;
+	return 0;
+}
+
+static int put_char(struct yaml_emitter *emitter, struct yaml_string *str)
+{
+	const int width = utf8_width(str);
+	int err;
+
+	err = maybe_flush(emitter, width);
+	if (err)
+		return err;
+
+	memcpy(emitter->buf.pos, str->pos, width);
+	emitter->buf.pos += width;
+	str->pos += width;
+
+	emitter->column++;
+	return 0;
+}
+
+static int put_char_break(struct yaml_emitter *emitter, struct yaml_string *str)
+{
+	const int width = utf8_width(str);
+	int err;
+
+	err = maybe_flush(emitter, width);
+	if (err)
+		return err;
+
+	memcpy(emitter->buf.pos, str->pos, width);
+	emitter->buf.pos += width;
+	str->pos += width;
+
+	emitter->column = 0;
+	return 0;
+}
+
+static struct yaml_event *next_event(const struct yaml_emitter *emitter)
+{
+	return list_first_entry(&emitter->events, struct yaml_event, link);
+}
+
+static bool need_more_events(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event;
+	int acc;
+
+	if (list_empty(&emitter->events))
+		return true;
+
+	acc = 0;
+	switch (next_event(emitter)->type) {
+	case YAML_DOCUMENT_START_EVENT:
+		acc = 1;
+		break;
+	case YAML_SEQUENCE_START_EVENT:
+		acc = 2;
+		break;
+	case YAML_MAPPING_START_EVENT:
+		acc = 3;
+		break;
+
+	default:
+		return false;
+	}
+
+	if (emitter->num_events > acc)
+		return false;
+
+	acc = 0;
+	list_for_each_entry(event, &emitter->events, link) {
+		switch (event->type) {
+		case YAML_STREAM_START_EVENT:
+		case YAML_DOCUMENT_START_EVENT:
+		case YAML_SEQUENCE_START_EVENT:
+		case YAML_MAPPING_START_EVENT:
+			acc++;
+			break;
+
+		case YAML_STREAM_END_EVENT:
+		case YAML_DOCUMENT_END_EVENT:
+		case YAML_SEQUENCE_END_EVENT:
+		case YAML_MAPPING_END_EVENT:
+			acc--;
+			break;
+
+		default:
+			break;
+		}
+
+		if (!acc)
+			return false;
+	}
+
+	return true;
+}
+
+static int
+analyze_version_directive(struct yaml_emitter *emitter,
+			  struct yaml_version_directive version)
+{
+	if (version.major != 1 || version.minor != 1)
+		return set_error(emitter, "incompatible %YAML directive");
+
+	return 0;
+}
+
+static int
+analyze_tag_directive(struct yaml_emitter *emitter,
+		      struct yaml_tag_directive tag)
+{
+	YAML_STRING(handle, tag.handle);
+	YAML_STRING(prefix, tag.prefix);
+
+	if (handle.start == handle.end)
+		return set_error(emitter, "tag handle must not be empty");
+
+	if (handle.start[0] != '!')
+		return set_error(emitter, "tag handle must start with '!'");
+
+	if (handle.end[-1] != '!')
+		return set_error(emitter, "tag handle must end with '!'");
+
+	handle.pos++;
+
+	while (handle.pos < handle.end - 1) {
+		if (!IS_ALPHA(handle))
+			return set_error(emitter,
+					 "tag handle must contain alphanumerical characters only");
+
+		ADVANCE(handle);
+	}
+
+	if (prefix.start == prefix.end)
+		return set_error(emitter, "tag prefix must not be empty");
+
+	return 0;
+}
+
+static int
+analyze_anchor(struct yaml_emitter *emitter, const char *anchor, int alias)
+{
+	YAML_STRING(string, anchor);
+
+	if (string.start == string.end) {
+		return set_error(emitter, alias ?
+				 "alias value must not be empty" :
+				 "anchor value must not be empty");
+	}
+
+	while (string.pos != string.end) {
+		if (!IS_ALPHA(string)) {
+			return set_error(emitter, alias ?
+					 "alias value must contain alphanumerical characters only" :
+					 "anchor value must contain alphanumerical characters only");
+		}
+		ADVANCE(string);
+	}
+
+	emitter->anchor_data.anchor = string.start;
+	emitter->anchor_data.anchor_length = string.end - string.start;
+	emitter->anchor_data.alias = alias;
+	return 0;
+}
+
+static int analyze_tag(struct yaml_emitter *emitter, const char *str)
+{
+	YAML_STRING(string, str);
+	struct yaml_tag_directive *tag;
+
+	if (string.start == string.end)
+		return set_error(emitter, "tag value must not be empty");
+
+	for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) {
+		size_t prefix_length = strlen(tag->prefix);
+
+		if (prefix_length < string.end - string.start &&
+		    strncmp(tag->prefix, string.start, prefix_length) == 0) {
+			emitter->tag_data.handle = tag->handle;
+			emitter->tag_data.handle_length = strlen(tag->handle);
+			emitter->tag_data.suffix = string.start + prefix_length;
+			emitter->tag_data.suffix_length =
+				(string.end - string.start) - prefix_length;
+			return 0;
+		}
+	}
+
+	emitter->tag_data.suffix = string.start;
+	emitter->tag_data.suffix_length = string.end - string.start;
+	return 0;
+}
+
+static int
+analyze_scalar(struct yaml_emitter *emitter, const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+
+	int block_indicators = 0;
+	int flow_indicators = 0;
+	int line_breaks = 0;
+	int special_characters = 0;
+
+	int leading_space = 0;
+	int leading_break = 0;
+	int trailing_space = 0;
+	int trailing_break = 0;
+	int break_space = 0;
+	int space_break = 0;
+
+	int preceded_by_whitespace = 0;
+	int followed_by_whitespace = 0;
+	int previous_space = 0;
+	int previous_break = 0;
+
+	emitter->scalar_data.value = value;
+	emitter->scalar_data.length = length;
+
+	if (string.start == string.end) {
+		emitter->scalar_data.multiline = 0;
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 1;
+		emitter->scalar_data.single_quoted_allowed = 1;
+		emitter->scalar_data.block_allowed = 0;
+		return 0;
+	}
+
+	if ((CHECK_AT(string, '-', 0) &&
+	     CHECK_AT(string, '-', 1) &&
+	     CHECK_AT(string, '-', 2)) ||
+	    (CHECK_AT(string, '.', 0) &&
+	     CHECK_AT(string, '.', 1) &&
+	     CHECK_AT(string, '.', 2))) {
+		block_indicators = 1;
+		flow_indicators = 1;
+	}
+
+	preceded_by_whitespace = 1;
+	followed_by_whitespace = IS_BLANKZ_AT(string, utf8_width(&string));
+
+	while (string.pos != string.end) {
+		if (string.start == string.pos) {
+			if (CHECK(string, '#') || CHECK(string, ',') ||
+			    CHECK(string, '[') || CHECK(string, ']') ||
+			    CHECK(string, '{') || CHECK(string, '}') ||
+			    CHECK(string, '&') || CHECK(string, '*') ||
+			    CHECK(string, '!') || CHECK(string, '|') ||
+			    CHECK(string, '>') || CHECK(string, '\'') ||
+			    CHECK(string, '"') || CHECK(string, '%') ||
+			    CHECK(string, '@') || CHECK(string, '`')) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+
+			if (CHECK(string, '?') || CHECK(string, ':')) {
+				flow_indicators = 1;
+				if (followed_by_whitespace)
+					block_indicators = 1;
+			}
+
+			if (CHECK(string, '-') && followed_by_whitespace) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+		} else {
+			if (CHECK(string, ',') || CHECK(string, '?') ||
+			    CHECK(string, '[') || CHECK(string, ']') ||
+			    CHECK(string, '{') || CHECK(string, '}'))
+				flow_indicators = 1;
+
+			if (CHECK(string, ':')) {
+				flow_indicators = 1;
+				if (followed_by_whitespace)
+					block_indicators = 1;
+			}
+
+			if (CHECK(string, '#') && preceded_by_whitespace) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+		}
+
+		if (!IS_PRINTABLE(string) ||
+		    (!IS_ASCII(string) && !emitter->unicode)) {
+			special_characters = 1;
+		}
+
+		if (IS_BREAK(string))
+			line_breaks = 1;
+
+		if (IS_SPACE(string)) {
+			if (string.start == string.pos)
+				leading_space = 1;
+
+			if (string.pos + utf8_width(&string) == string.end)
+				trailing_space = 1;
+
+			if (previous_break)
+				break_space = 1;
+
+			previous_space = 1;
+			previous_break = 0;
+		} else if (IS_BREAK(string)) {
+			if (string.start == string.pos)
+				leading_break = 1;
+
+			if (string.pos + utf8_width(&string) == string.end)
+				trailing_break = 1;
+
+			if (previous_space)
+				space_break = 1;
+
+			previous_space = 0;
+			previous_break = 1;
+		} else {
+			previous_space = 0;
+			previous_break = 0;
+		}
+
+		preceded_by_whitespace = IS_BLANKZ(string);
+		ADVANCE(string);
+		if (string.pos != string.end)
+			followed_by_whitespace =
+				IS_BLANKZ_AT(string, utf8_width(&string));
+	}
+
+	emitter->scalar_data.multiline = line_breaks;
+
+	emitter->scalar_data.flow_plain_allowed = 1;
+	emitter->scalar_data.block_plain_allowed = 1;
+	emitter->scalar_data.single_quoted_allowed = 1;
+	emitter->scalar_data.block_allowed = 1;
+
+	if (leading_space ||
+	    leading_break ||
+	    trailing_space ||
+	    trailing_break) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+	}
+
+	if (trailing_space)
+		emitter->scalar_data.block_allowed = 0;
+
+	if (break_space) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+		emitter->scalar_data.single_quoted_allowed = 0;
+	}
+
+	if (space_break || special_characters) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+		emitter->scalar_data.single_quoted_allowed = 0;
+		emitter->scalar_data.block_allowed = 0;
+	}
+
+	if (line_breaks) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+	}
+
+	if (flow_indicators)
+		emitter->scalar_data.flow_plain_allowed = 0;
+
+	if (block_indicators)
+		emitter->scalar_data.block_plain_allowed = 0;
+
+	return 0;
+}
+
+static int
+analyze_event(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	emitter->anchor_data.anchor = NULL;
+	emitter->anchor_data.anchor_length = 0;
+	emitter->tag_data.handle = NULL;
+	emitter->tag_data.handle_length = 0;
+	emitter->tag_data.suffix = NULL;
+	emitter->tag_data.suffix_length = 0;
+	emitter->scalar_data.value = NULL;
+	emitter->scalar_data.length = 0;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		return analyze_anchor(emitter, event->alias.anchor, 1);
+
+	case YAML_SCALAR_EVENT:
+		if (event->scalar.anchor) {
+			err = analyze_anchor(emitter, event->scalar.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->scalar.tag &&
+		    (emitter->canonical ||
+		     (!event->scalar.plain_implicit &&
+		      !event->scalar.quoted_implicit))) {
+			err = analyze_tag(emitter, event->scalar.tag);
+			if (err)
+				return err;
+		}
+
+		return analyze_scalar(emitter,
+				      event->scalar.value,
+				      event->scalar.length);
+
+	case YAML_SEQUENCE_START_EVENT:
+		if (event->sequence_start.anchor) {
+			err = analyze_anchor(emitter,
+					     event->sequence_start.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->sequence_start.tag &&
+		    (emitter->canonical || !event->sequence_start.implicit)) {
+			err = analyze_tag(emitter,
+					  event->sequence_start.tag);
+			if (err)
+				return err;
+		}
+
+		return 0;
+
+	case YAML_MAPPING_START_EVENT:
+		if (event->mapping_start.anchor) {
+			err = analyze_anchor(emitter,
+					     event->mapping_start.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->mapping_start.tag &&
+		    (emitter->canonical || !event->mapping_start.implicit)) {
+			err = analyze_tag(emitter, event->mapping_start.tag);
+			if (err)
+				return err;
+		}
+
+		return 0;
+
+	default:
+		return 0;
+	}
+}
+
+static bool check_empty_document(const struct yaml_emitter *emitter)
+{
+	return false;
+}
+
+static bool check_empty_sequence(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event;
+
+	if (emitter->num_events < 2)
+		return false;
+
+	event = next_event(emitter);
+	return (event->type == YAML_SEQUENCE_START_EVENT &&
+		list_next_entry(event, link)->type == YAML_SEQUENCE_END_EVENT);
+}
+
+static bool check_empty_mapping(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event;
+
+	if (emitter->num_events < 2)
+		return false;
+
+	event = next_event(emitter);
+	return (event->type == YAML_MAPPING_START_EVENT &&
+		list_next_entry(event, link)->type == YAML_MAPPING_END_EVENT);
+}
+
+static bool check_simple_key(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event = next_event(emitter);
+	size_t length = 0;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		length += emitter->anchor_data.anchor_length;
+		break;
+
+	case YAML_SCALAR_EVENT:
+		if (emitter->scalar_data.multiline)
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length +
+			emitter->scalar_data.length;
+		break;
+
+	case YAML_SEQUENCE_START_EVENT:
+		if (!check_empty_sequence(emitter))
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length;
+		break;
+
+	case YAML_MAPPING_START_EVENT:
+		if (!check_empty_mapping(emitter))
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length;
+		break;
+
+	default:
+		return false;
+	}
+
+	return length <= 128;
+}
+
+static int increase_indent(struct yaml_emitter *emitter,
+			   bool flow, bool indentless)
+{
+	int err;
+
+	err = PUSH(emitter, emitter->indents, emitter->indent);
+	if (err)
+		return err;
+
+	if (emitter->indent < 0)
+		emitter->indent = flow ? emitter->best_indent : 0;
+	else if (!indentless)
+		emitter->indent += emitter->best_indent;
+
+	return 0;
+}
+
+static int write_indent(struct yaml_emitter *emitter)
+{
+	int indent = (emitter->indent >= 0) ? emitter->indent : 0;
+	int err;
+
+	if (!emitter->indention ||
+	    emitter->column > indent ||
+	    (emitter->column == indent && !emitter->whitespace)) {
+		err = put_break(emitter);
+		if (err)
+			return err;
+	}
+
+	while (emitter->column < indent) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 1;
+	emitter->indention = 1;
+	return 0;
+}
+
+static int write_indicator(struct yaml_emitter *emitter,
+			   const char *indicator, int need_whitespace,
+			   int is_whitespace, int is_indention)
+{
+	YAML_STRING(string, indicator);
+	int err;
+
+	if (need_whitespace && !emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = is_whitespace;
+	emitter->indention = (emitter->indention && is_indention);
+	emitter->open_ended = 0;
+	return 0;
+}
+
+static int write_anchor(struct yaml_emitter *emitter,
+			const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag_handle(struct yaml_emitter *emitter,
+			    const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	if (!emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag_content(struct yaml_emitter *emitter,
+			     const char *value, size_t length,
+			     int need_whitespace)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	if (need_whitespace && !emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		if (IS_ALPHA(string) ||
+		    CHECK(string, ';') ||
+		    CHECK(string, '/') ||
+		    CHECK(string, '?') ||
+		    CHECK(string, ':') ||
+		    CHECK(string, '@') ||
+		    CHECK(string, '&') ||
+		    CHECK(string, '=') ||
+		    CHECK(string, '+') ||
+		    CHECK(string, '$') ||
+		    CHECK(string, ',') ||
+		    CHECK(string, '_') ||
+		    CHECK(string, '.') ||
+		    CHECK(string, '~') ||
+		    CHECK(string, '*') ||
+		    CHECK(string, '\'') ||
+		    CHECK(string, '(') ||
+		    CHECK(string, ')') ||
+		    CHECK(string, '[') ||
+		    CHECK(string, ']')) {
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+		} else {
+			int width = utf8_width(&string);
+
+			while (width--) {
+				unsigned int value = *string.pos++;
+				int c;
+
+				err = put(emitter, '%');
+				if (err)
+					return err;
+
+				c = value >> 4;
+				c += c < 10 ? '0' : 'A' - 10;
+				err = put(emitter, c);
+				if (err)
+					return err;
+
+				c = value & 0x0f;
+				c += c < 10 ? '0' : 'A' - 10;
+				err = put(emitter, c);
+				if (err)
+					return err;
+			}
+		}
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (!emitter->tag_data.handle && !emitter->tag_data.suffix)
+		return 0;
+
+	if (emitter->tag_data.handle) {
+		err = write_tag_handle(emitter,
+				       emitter->tag_data.handle,
+				       emitter->tag_data.handle_length);
+		if (err)
+			return err;
+
+		if (emitter->tag_data.suffix) {
+			err = write_tag_content(emitter,
+						emitter->tag_data.suffix,
+						emitter->tag_data.suffix_length,
+						0);
+			if (err)
+				return err;
+		}
+	} else {
+		err = write_indicator(emitter, "!<", 1, 0, 0);
+		if (err)
+			return err;
+
+		err = write_tag_content(emitter,
+					emitter->tag_data.suffix,
+					emitter->tag_data.suffix_length,
+					0);
+		if (err)
+			return err;
+
+		err = write_indicator(emitter, ">", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int write_plain_scalar(struct yaml_emitter *emitter,
+			      const char *value,
+			      size_t length,
+			      int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int breaks = 0;
+	int err;
+
+	if (!emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		if (IS_SPACE(string)) {
+			if (allow_breaks && !spaces &&
+			    emitter->column > emitter->best_width &&
+			    !IS_SPACE_AT(string, 1)) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else if (IS_BREAK(string)) {
+			if (!breaks && CHECK(string, '\n')) {
+				err = put_break(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			spaces = 0;
+			breaks = 0;
+		}
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	if (emitter->root_context)
+		emitter->open_ended = 1;
+	return 0;
+}
+
+static int
+write_single_quoted_scalar(struct yaml_emitter *emitter,
+			   const char *value,
+			   size_t length,
+			   int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int breaks = 0;
+	int err;
+
+	err = write_indicator(emitter, "'", 1, 0, 0);
+	if (err)
+		return err;
+
+	while (string.pos != string.end) {
+		if (IS_SPACE(string)) {
+			if (allow_breaks && !spaces &&
+			    emitter->column > emitter->best_width &&
+			    string.pos != string.start &&
+			    string.pos != string.end - 1 &&
+			    !IS_SPACE_AT(string, 1)) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else if (IS_BREAK(string)) {
+			if (!breaks && CHECK(string, '\n')) {
+				err = put_break(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char_breaks(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			if (CHECK(string, '\'')) {
+				err = put(emitter, '\'');
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			spaces = 0;
+			breaks = 0;
+		}
+	}
+
+	if (breaks) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	err = write_indicator(emitter, "'", 0, 0, 0);
+	if (err)
+		return err;
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int
+write_double_quoted_scalar(struct yaml_emitter *emitter,
+			   const char *value, size_t length, int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int err;
+
+	err = write_indicator(emitter, "\"", 1, 0, 0);
+	if (err)
+		return err;
+
+	while (string.pos != string.end) {
+		if (!IS_PRINTABLE(string) ||
+		    (!emitter->unicode && !IS_ASCII(string)) ||
+		    IS_BOM(string) ||
+		    IS_BREAK(string) ||
+		    CHECK(string, '"') ||
+		    CHECK(string, '\\')) {
+			u8 octet = string.pos[0];
+			unsigned int width;
+			unsigned int value;
+			int k;
+
+			width = (octet & 0x80) == 0x00 ? 1 :
+				(octet & 0xE0) == 0xC0 ? 2 :
+				(octet & 0xF0) == 0xE0 ? 3 :
+				(octet & 0xF8) == 0xF0 ? 4 : 0;
+			value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+				(octet & 0xE0) == 0xC0 ? octet & 0x1F :
+				(octet & 0xF0) == 0xE0 ? octet & 0x0F :
+				(octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+			for (k = 1; k < (int)width; k++) {
+				octet = string.pos[k];
+				value = (value << 6) + (octet & 0x3F);
+			}
+			string.pos += width;
+
+			err = put(emitter, '\\');
+			if (err)
+				return err;
+
+			switch (value) {
+			case 0x00:
+				err = put(emitter, '0');
+				if (err)
+					return err;
+				break;
+
+			case 0x07:
+				err = put(emitter, 'a');
+				if (err)
+					return err;
+				break;
+
+			case 0x08:
+				err = put(emitter, 'b');
+				if (err)
+					return err;
+				break;
+
+			case 0x09:
+				err = put(emitter, 't');
+				if (err)
+					return err;
+				break;
+
+			case 0x0A:
+				err = put(emitter, 'n');
+				if (err)
+					return err;
+				break;
+
+			case 0x0B:
+				err = put(emitter, 'v');
+				if (err)
+					return err;
+				break;
+
+			case 0x0C:
+				err = put(emitter, 'f');
+				if (err)
+					return err;
+				break;
+
+			case 0x0D:
+				err = put(emitter, 'r');
+				if (err)
+					return err;
+				break;
+
+			case 0x1B:
+				err = put(emitter, 'e');
+				if (err)
+					return err;
+				break;
+
+			case 0x22:
+				err = put(emitter, '\"');
+				if (err)
+					return err;
+				break;
+
+			case 0x5C:
+				err = put(emitter, '\\');
+				if (err)
+					return err;
+				break;
+
+			case 0x85:
+				err = put(emitter, 'N');
+				if (err)
+					return err;
+				break;
+
+			case 0xA0:
+				err = put(emitter, '_');
+				if (err)
+					return err;
+				break;
+
+			case 0x2028:
+				err = put(emitter, 'L');
+				if (err)
+					return err;
+				break;
+
+			case 0x2029:
+				err = put(emitter, 'P');
+				if (err)
+					return err;
+				break;
+
+			default:
+				if (value <= 0xFF) {
+					err = put(emitter, 'x');
+					if (err)
+						return err;
+					width = 2;
+				} else if (value <= 0xFFFF) {
+					err = put(emitter, 'u');
+					if (err)
+						return err;
+					width = 4;
+				} else {
+					err = put(emitter, 'U');
+					if (err)
+						return err;
+					width = 8;
+				}
+
+				for (k = (width - 1) * 4; k >= 0; k -= 4) {
+					int digit;
+
+					digit = (value >> k) & 0x0F;
+					digit += digit < 10 ? '0' : 'A' - 10;
+					err = put(emitter, digit);
+					if (err)
+						return err;
+				}
+			}
+			spaces = 0;
+		} else if (IS_SPACE(string)) {
+			if (allow_breaks &&
+			    !spaces &&
+			    emitter->column > emitter->best_width &&
+			    string.pos != string.start &&
+			    string.pos != string.end - 1) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				if (IS_SPACE_AT(string, 1)) {
+					err = put(emitter, '\\');
+					if (err)
+						return err;
+				}
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else {
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+			spaces = 0;
+		}
+	}
+
+	err = write_indicator(emitter, "\"", 0, 0, 0);
+	if (err)
+		return err;
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int
+write_block_scalar_hints(struct yaml_emitter *emitter,
+			 struct yaml_string string)
+{
+	char indent_hint[2];
+	const char *chomp_hint = NULL;
+	int err;
+
+	if (IS_SPACE(string) || IS_BREAK(string)) {
+		indent_hint[0] = '0' + (char)emitter->best_indent;
+		indent_hint[1] = '\0';
+		err = write_indicator(emitter, indent_hint, 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	emitter->open_ended = 0;
+
+	string.pos = string.end;
+	if (string.start == string.pos) {
+		chomp_hint = "-";
+	} else {
+		do {
+			string.pos--;
+		} while ((*string.pos & 0xC0) == 0x80);
+
+		if (!IS_BREAK(string)) {
+			chomp_hint = "-";
+		} else if (string.start == string.pos) {
+			chomp_hint = "+";
+			emitter->open_ended = 1;
+		} else {
+			do {
+				string.pos--;
+			} while ((*string.pos & 0xC0) == 0x80);
+
+			if (IS_BREAK(string)) {
+				chomp_hint = "+";
+				emitter->open_ended = 1;
+			}
+		}
+	}
+
+	if (chomp_hint) {
+		err = write_indicator(emitter, chomp_hint, 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int
+write_literal_scalar(struct yaml_emitter *emitter,
+		     const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int breaks = 1;
+	int err;
+
+	err = write_indicator(emitter, "|", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = write_block_scalar_hints(emitter, string);
+	if (err)
+		return err;
+
+	err = put_break(emitter);
+	if (err)
+		return err;
+
+	emitter->indention = 1;
+	emitter->whitespace = 1;
+
+	while (string.pos != string.end) {
+		if (IS_BREAK(string)) {
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			breaks = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int
+write_folded_scalar(struct yaml_emitter *emitter,
+		    const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int breaks = 1;
+	int leading_spaces = 1;
+	int err;
+
+	err = write_indicator(emitter, ">", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = write_block_scalar_hints(emitter, string);
+	if (err)
+		return err;
+
+	err = put_break(emitter);
+	if (err)
+		return err;
+
+	emitter->indention = 1;
+	emitter->whitespace = 1;
+
+	while (string.pos != string.end) {
+		if (IS_BREAK(string)) {
+			if (!breaks && !leading_spaces && CHECK(string, '\n')) {
+				int k;
+
+				k = 0;
+				while (IS_BREAK_AT(string, k))
+					k += __utf8_width(&string, k);
+
+				if (!IS_BLANKZ_AT(string, k)) {
+					err = put_break(emitter);
+					if (err)
+						return err;
+				}
+			}
+
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				leading_spaces = IS_BLANK(string);
+			}
+
+			if (!breaks &&
+			    IS_SPACE(string) &&
+			    !IS_SPACE_AT(string, 1) &&
+			    emitter->column > emitter->best_width) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			emitter->indention = 0;
+			breaks = 0;
+		}
+	}
+
+	return err;
+}
+
+static int
+write_scalar(struct yaml_emitter *emitter)
+{
+	switch (emitter->scalar_data.style) {
+	case YAML_PLAIN_SCALAR_STYLE:
+		return write_plain_scalar(emitter,
+					  emitter->scalar_data.value,
+					  emitter->scalar_data.length,
+					  !emitter->simple_key_context);
+
+	case YAML_SINGLE_QUOTED_SCALAR_STYLE:
+		return write_single_quoted_scalar(emitter,
+						  emitter->scalar_data.value,
+						  emitter->scalar_data.length,
+						  !emitter->simple_key_context);
+
+	case YAML_DOUBLE_QUOTED_SCALAR_STYLE:
+		return write_double_quoted_scalar(emitter,
+						  emitter->scalar_data.value,
+						  emitter->scalar_data.length,
+						  !emitter->simple_key_context);
+
+	case YAML_LITERAL_SCALAR_STYLE:
+		return write_literal_scalar(emitter,
+					    emitter->scalar_data.value,
+					    emitter->scalar_data.length);
+
+	case YAML_FOLDED_SCALAR_STYLE:
+		return write_folded_scalar(emitter,
+					   emitter->scalar_data.value,
+					   emitter->scalar_data.length);
+
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+static int emit_anchor(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (!emitter->anchor_data.anchor)
+		return 0;
+
+	err = write_indicator(emitter,
+			      emitter->anchor_data.alias ? "*" : "&",
+			      1, 0, 0);
+	if (err)
+		return err;
+
+	return write_anchor(emitter,
+			    emitter->anchor_data.anchor,
+			    emitter->anchor_data.anchor_length);
+}
+
+static int emit_stream_start(struct yaml_emitter *emitter,
+			     const struct yaml_event *event)
+{
+	if (event->type == YAML_STREAM_START_EVENT) {
+		if (emitter->best_indent < 2 || emitter->best_indent > 9)
+			emitter->best_indent  = 2;
+
+		if (emitter->best_width >= 0 &&
+		    emitter->best_width <= emitter->best_indent * 2)
+			emitter->best_width = 80;
+
+		if (emitter->best_width < 0)
+			emitter->best_width = INT_MAX;
+
+		emitter->indent = -1;
+
+		emitter->column = 0;
+		emitter->whitespace = 1;
+		emitter->indention = 1;
+
+		emitter->state = EMIT_FIRST_DOCUMENT_START_STATE;
+		return 0;
+	}
+
+	return set_error(emitter, "expected STREAM-START");
+}
+
+static int
+append_tag_directive(struct yaml_emitter *emitter,
+		     struct yaml_tag_directive value,
+		     int allow_duplicates)
+{
+	struct yaml_tag_directive *tag;
+	struct yaml_tag_directive copy = {};
+	int err;
+
+	for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) {
+		if (strcmp(value.handle, tag->handle) == 0) {
+			if (allow_duplicates)
+				return 0;
+
+			return set_error(emitter, "duplicate %TAG directive");
+		}
+	}
+
+	copy.handle = kstrdup(value.handle, GFP_KERNEL);
+	copy.prefix = kstrdup(value.prefix, GFP_KERNEL);
+	if (!copy.handle || !copy.prefix) {
+		emitter->errno = -ENOMEM;
+		err = -ENOMEM;
+		goto error;
+	}
+
+	err = PUSH(emitter, emitter->tags, copy);
+	if (err)
+		goto error;
+
+	return 0;
+
+error:
+	kfree(copy.handle);
+	kfree(copy.prefix);
+	return err;
+}
+
+static int
+emit_document_start(struct yaml_emitter *emitter,
+		    const struct yaml_event *event,
+		    int first)
+{
+	int err;
+
+	if (event->type == YAML_DOCUMENT_START_EVENT) {
+		struct yaml_tag_directive default_tag_directives[] = {
+			{ "!", "!" },
+			{ "!!", "tag:yaml.org,2002:" },
+			{ NULL, NULL }
+		};
+		struct yaml_tag_directive *tag;
+		int implicit;
+
+		if (event->document_start.version) {
+			err = analyze_version_directive(emitter,
+							*event->document_start.version);
+			if (err)
+				return err;
+		}
+
+		for (tag = event->document_start.tags.start;
+		     tag != event->document_start.tags.end;
+		     tag++) {
+			err = analyze_tag_directive(emitter, *tag);
+			if (err)
+				return err;
+
+			err = append_tag_directive(emitter, *tag, 0);
+			if (err)
+				return err;
+		}
+
+		for (tag = default_tag_directives;
+		     tag->handle; tag++) {
+			err = append_tag_directive(emitter, *tag, 1);
+			if (err)
+				return err;
+		}
+
+		implicit = event->document_start.implicit;
+		if (!first || emitter->canonical)
+			implicit = 0;
+
+		if ((event->document_start.version ||
+		     event->document_start.tags.start
+		     != event->document_start.tags.end) &&
+		    emitter->open_ended) {
+			err = write_indicator(emitter, "...", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		if (event->document_start.version) {
+			implicit = 0;
+			err = write_indicator(emitter, "%YAML", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indicator(emitter, "1.1", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		if (event->document_start.tags.start !=
+		    event->document_start.tags.end) {
+			implicit = 0;
+			for (tag = event->document_start.tags.start;
+			     tag != event->document_start.tags.end;
+			     tag++) {
+				err = write_indicator(emitter, "%TAG", 1, 0, 0);
+				if (err)
+					return err;
+
+				err = write_tag_handle(emitter,
+						       tag->handle,
+						       strlen(tag->handle));
+				if (err)
+					return err;
+
+				err = write_tag_content(emitter,
+							tag->prefix,
+							strlen(tag->prefix),
+							1);
+				if (err)
+					return err;
+
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+		}
+
+		if (check_empty_document(emitter))
+			implicit = 0;
+
+		if (!implicit) {
+			err = write_indent(emitter);
+			if (err)
+				return err;
+
+			err = write_indicator(emitter, "---", 1, 0, 0);
+			if (err)
+				return err;
+
+			if (emitter->canonical) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+		}
+
+		emitter->state = EMIT_DOCUMENT_CONTENT_STATE;
+		return 0;
+	}
+
+	if (event->type == YAML_STREAM_END_EVENT) {
+		err = flush(emitter);
+		if (err)
+			return err;
+
+		emitter->state = EMIT_END_STATE;
+		return 0;
+	}
+
+	return set_error(emitter, "expected DOCUMENT-START or STREAM-END");
+}
+
+static int
+emit_document_end(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	if (event->type == YAML_DOCUMENT_END_EVENT) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+
+		if (!event->document_end.implicit) {
+			err = write_indicator(emitter, "...", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = flush(emitter);
+		if (err)
+			return err;
+
+		emitter->state = EMIT_DOCUMENT_START_STATE;
+
+		while (!EMPTY(emitter, emitter->tags)) {
+			struct yaml_tag_directive tag =
+				POP(emitter, emitter->tags);
+
+			kfree(tag.handle);
+			kfree(tag.prefix);
+		}
+
+		return 0;
+	}
+
+	return set_error(emitter, "expected DOCUMENT-END");
+}
+
+static int
+emit_alias(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	emitter->state = POP(emitter, emitter->states);
+	return 0;
+}
+
+static int
+select_scalar_style(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	enum yaml_scalar_style style = event->scalar.style;
+	bool no_tag = !emitter->tag_data.handle && !emitter->tag_data.suffix;
+
+	if (no_tag &&
+	    !event->scalar.plain_implicit &&
+	    !event->scalar.quoted_implicit)
+		return set_error(emitter,
+				 "neither tag nor implicit flags are specified");
+
+	if (style == YAML_ANY_SCALAR_STYLE)
+		style = YAML_PLAIN_SCALAR_STYLE;
+
+	if (emitter->canonical)
+		style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+	if (emitter->simple_key_context && emitter->scalar_data.multiline)
+		style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+	if (style == YAML_PLAIN_SCALAR_STYLE) {
+		if ((emitter->flow_level && !emitter->scalar_data.flow_plain_allowed) ||
+		    (!emitter->flow_level && !emitter->scalar_data.block_plain_allowed))
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+
+		if (!emitter->scalar_data.length &&
+		    (emitter->flow_level || emitter->simple_key_context))
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+
+		if (no_tag && !event->scalar.plain_implicit)
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (style == YAML_SINGLE_QUOTED_SCALAR_STYLE) {
+		if (!emitter->scalar_data.single_quoted_allowed)
+			style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (style == YAML_LITERAL_SCALAR_STYLE ||
+	    style == YAML_FOLDED_SCALAR_STYLE) {
+		if (!emitter->scalar_data.block_allowed ||
+		    emitter->flow_level ||
+		    emitter->simple_key_context)
+			style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (no_tag && !event->scalar.quoted_implicit &&
+	    style != YAML_PLAIN_SCALAR_STYLE) {
+		emitter->tag_data.handle = "!";
+		emitter->tag_data.handle_length = 1;
+	}
+
+	emitter->scalar_data.style = style;
+	return 0;
+}
+
+static int
+emit_scalar(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = select_scalar_style(emitter, event);
+	if (err)
+		return err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	err = increase_indent(emitter, 1, 0);
+	if (err)
+		return err;
+
+	err = write_scalar(emitter);
+	if (err)
+		return err;
+
+	emitter->indent = POP(emitter, emitter->indents);
+	emitter->state = POP(emitter, emitter->states);
+	return 0;
+}
+
+static int
+emit_sequence_start(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	if (emitter->flow_level || emitter->canonical ||
+	    event->sequence_start.style == YAML_FLOW_SEQUENCE_STYLE ||
+	    check_empty_sequence(emitter)) {
+		emitter->state = EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE;
+	} else {
+		emitter->state = EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE;
+	}
+
+	return 0;
+}
+
+static int
+emit_mapping_start(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	if (emitter->flow_level ||
+	    emitter->canonical ||
+	    event->mapping_start.style == YAML_FLOW_MAPPING_STYLE ||
+	    check_empty_mapping(emitter)) {
+		emitter->state = EMIT_FLOW_MAPPING_FIRST_KEY_STATE;
+	} else {
+		emitter->state = EMIT_BLOCK_MAPPING_FIRST_KEY_STATE;
+	}
+
+	return 0;
+}
+
+static int
+emit_node(struct yaml_emitter *emitter, const struct yaml_event *event,
+	  int root, int sequence, int mapping, int simple_key)
+{
+	emitter->root_context = root;
+	emitter->sequence_context = sequence;
+	emitter->mapping_context = mapping;
+	emitter->simple_key_context = simple_key;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		return emit_alias(emitter, event);
+
+	case YAML_SCALAR_EVENT:
+		return emit_scalar(emitter, event);
+
+	case YAML_SEQUENCE_START_EVENT:
+		return emit_sequence_start(emitter, event);
+
+	case YAML_MAPPING_START_EVENT:
+		return emit_mapping_start(emitter, event);
+
+	default:
+		return set_error(emitter,
+				 "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS");
+	}
+
+	return 0;
+}
+
+static int
+emit_document_content(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = PUSH(emitter, emitter->states, EMIT_DOCUMENT_END_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 1, 0, 0, 0);
+}
+
+static int
+emit_flow_sequence_item(struct yaml_emitter *emitter,
+			const struct yaml_event *event,
+			bool first)
+{
+	int err;
+
+	if (first) {
+		err = write_indicator(emitter, "[", 1, 1, 0);
+		if (err)
+			return err;
+
+		err = increase_indent(emitter, 1, 0);
+		if (err)
+			return err;
+
+		emitter->flow_level++;
+	}
+
+	if (event->type == YAML_SEQUENCE_END_EVENT) {
+		emitter->flow_level--;
+		emitter->indent = POP(emitter, emitter->indents);
+
+		if (emitter->canonical && !first) {
+			err = write_indicator(emitter, ",", 0, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, "]", 0, 0, 0);
+		if (err)
+			return err;
+
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	if (!first) {
+		err = write_indicator(emitter, ",", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (emitter->canonical || emitter->column > emitter->best_width) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_SEQUENCE_ITEM_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_flow_mapping_key(struct yaml_emitter *emitter,
+		      const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = write_indicator(emitter, "{", 1, 1, 0);
+		if (err)
+			return err;
+
+		err = increase_indent(emitter, 1, 0);
+		if (err)
+			return err;
+
+		emitter->flow_level++;
+	}
+
+	if (event->type == YAML_MAPPING_END_EVENT) {
+		emitter->flow_level--;
+		emitter->indent = POP(emitter, emitter->indents);
+
+		if (emitter->canonical && !first) {
+			err = write_indicator(emitter, ",", 0, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, "}", 0, 0, 0);
+		if (err)
+			return err;
+
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	if (!first) {
+		err = write_indicator(emitter, ",", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (emitter->canonical || emitter->column > emitter->best_width) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	if (!emitter->canonical && check_simple_key(emitter)) {
+		err = PUSH(emitter, emitter->states,
+			   EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 1);
+	}
+
+	err = write_indicator(emitter, "?", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_VALUE_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emit_flow_mapping_value(struct yaml_emitter *emitter,
+			const struct yaml_event *event, int simple)
+{
+	int err;
+
+	if (simple) {
+		err = write_indicator(emitter, ":", 0, 0, 0);
+		if (err)
+			return err;
+	} else {
+		if (emitter->canonical ||
+		    emitter->column > emitter->best_width) {
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, ":", 1, 0, 0);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_KEY_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emit_block_sequence_item(struct yaml_emitter *emitter,
+			 const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = increase_indent(emitter, 0,
+				      emitter->mapping_context &&
+				      !emitter->indention);
+		if (err)
+			return err;
+	}
+
+	if (event->type == YAML_SEQUENCE_END_EVENT) {
+		emitter->indent = POP(emitter, emitter->indents);
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	err = write_indent(emitter);
+	if (err)
+		return err;
+
+	err = write_indicator(emitter, "-", 1, 0, 1);
+	if (err)
+		return err;
+
+	err = PUSH(emitter, emitter->states, EMIT_BLOCK_SEQUENCE_ITEM_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_block_mapping_key(struct yaml_emitter *emitter,
+		       const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = increase_indent(emitter, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (event->type == YAML_MAPPING_END_EVENT) {
+		emitter->indent = POP(emitter, emitter->indents);
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	err = write_indent(emitter);
+	if (err)
+		return err;
+
+	if (check_simple_key(emitter)) {
+		err = PUSH(emitter, emitter->states,
+			   EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 1);
+	} else {
+		err = write_indicator(emitter, "?", 1, 0, 1);
+		if (err)
+			return err;
+
+		err = PUSH(emitter, emitter->states,
+			   EMIT_BLOCK_MAPPING_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 0);
+	}
+}
+
+static int
+emit_block_mapping_value(struct yaml_emitter *emitter,
+			 const struct yaml_event *event, int simple)
+{
+	int err;
+
+	if (simple) {
+		err = write_indicator(emitter, ":", 0, 0, 0);
+		if (err)
+			return err;
+	} else {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+
+		err = write_indicator(emitter, ":", 1, 0, 1);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_BLOCK_MAPPING_KEY_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emitter_state_machine(struct yaml_emitter *emitter,
+		      const struct yaml_event *event)
+{
+	switch (emitter->state) {
+	case EMIT_STREAM_START_STATE:
+		return emit_stream_start(emitter, event);
+
+	case EMIT_FIRST_DOCUMENT_START_STATE:
+		return emit_document_start(emitter, event, true);
+
+	case EMIT_DOCUMENT_START_STATE:
+		return emit_document_start(emitter, event, false);
+
+	case EMIT_DOCUMENT_CONTENT_STATE:
+		return emit_document_content(emitter, event);
+
+	case EMIT_DOCUMENT_END_STATE:
+		return emit_document_end(emitter, event);
+
+	case EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
+		return emit_flow_sequence_item(emitter, event, true);
+
+	case EMIT_FLOW_SEQUENCE_ITEM_STATE:
+		return emit_flow_sequence_item(emitter, event, false);
+
+	case EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
+		return emit_flow_mapping_key(emitter, event, true);
+
+	case EMIT_FLOW_MAPPING_KEY_STATE:
+		return emit_flow_mapping_key(emitter, event, false);
+
+	case EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
+		return emit_flow_mapping_value(emitter, event, true);
+
+	case EMIT_FLOW_MAPPING_VALUE_STATE:
+		return emit_flow_mapping_value(emitter, event, false);
+
+	case EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
+		return emit_block_sequence_item(emitter, event, true);
+
+	case EMIT_BLOCK_SEQUENCE_ITEM_STATE:
+		return emit_block_sequence_item(emitter, event, false);
+
+	case EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
+		return emit_block_mapping_key(emitter, event, true);
+
+	case EMIT_BLOCK_MAPPING_KEY_STATE:
+		return emit_block_mapping_key(emitter, event, false);
+
+	case EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
+		return emit_block_mapping_value(emitter, event, true);
+
+	case EMIT_BLOCK_MAPPING_VALUE_STATE:
+		return emit_block_mapping_value(emitter, event, false);
+
+	case EMIT_END_STATE:
+		return set_error(emitter, "expected nothing after STREAM-END");
+
+	default:
+		BUG();
+	}
+
+	return 0;
+}
+
+void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event)
+{
+	if (!event || !emitter || emitter->errno) {
+		yaml_event_delete(event);
+		return;
+	}
+
+	list_add_tail(&event->link, &emitter->events);
+	emitter->num_events++;
+
+	while (!need_more_events(emitter)) {
+		event = next_event(emitter);
+
+		if (analyze_event(emitter, event))
+			return;
+
+		if (emitter_state_machine(emitter, event))
+			return;
+
+		emitter->num_events--;
+		list_del(&event->link);
+		yaml_event_delete(event);
+	}
+}
+EXPORT_SYMBOL(yaml_emitter_emit);
+
+int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem)
+{
+	int err;
+
+	if (!emitter) {
+		if (problem)
+			*problem = "allocation error";
+		return -ENOMEM;
+	}
+
+	if (emitter->errno)
+		err = emitter->errno;
+	else
+		err = flush(emitter);
+	if (!err)
+		return 0;
+
+	if (problem)
+		*problem = emitter->problem;
+	return err;
+}
+EXPORT_SYMBOL(yaml_emitter_flush);
+
+static bool yaml_stack_extend(void **start, void **top, void **end)
+{
+	void *new_start;
+
+	if (*end - *start >= INT_MAX / 2)
+		return false;
+
+	new_start = krealloc(*start, (*end - *start) * 2, GFP_KERNEL);
+	if (!new_start)
+		return false;
+
+	*top = new_start + (*top - *start);
+	*end = new_start + (*end - *start) * 2;
+	*start = new_start;
+
+	return true;
+}
+
+#define STACK_INIT(context, stack)                                     \
+	(((stack).start = kmalloc(INITIAL_STACK_SIZE * sizeof(*(stack). start),\
+				  gfp)) ?			\
+	((stack).top = (stack).start,					\
+	(stack).end = (stack).start + INITIAL_STACK_SIZE,		\
+	1) : 0)
+#define STACK_DEL(context, stack) kfree((stack).start)
+
+struct yaml_emitter *yaml_emitter_create(gfp_t gfp)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = kzalloc(sizeof(*emitter), gfp);
+	if (!emitter)
+		return NULL;
+
+	emitter->gfp = gfp;
+	INIT_LIST_HEAD(&emitter->events);
+
+	emitter->buf.start = kmalloc(PAGE_SIZE, gfp);
+	if (!emitter->buf.start)
+		goto error;
+
+	emitter->buf.pos = emitter->buf.start;
+	emitter->buf.last = emitter->buf.pos;
+	emitter->buf.end = emitter->buf.start + PAGE_SIZE;
+
+	if (!STACK_INIT(emitter, emitter->states))
+		goto error;
+
+	if (!STACK_INIT(emitter, emitter->indents))
+		goto error;
+
+	if (!STACK_INIT(emitter, emitter->tags))
+		goto error;
+
+	return emitter;
+
+error:
+	yaml_emitter_destroy(emitter);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_emitter_create);
+
+gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp)
+{
+	gfp_t old = 0;
+
+	if (emitter) {
+		old = emitter->gfp;
+		emitter->gfp = gfp;
+	}
+
+	return old;
+}
+EXPORT_SYMBOL(yaml_emitter_set_gfp);
+
+static void delete_events(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event, *next;
+
+	list_for_each_entry_safe(event, next, &emitter->events, link)
+		yaml_event_delete(event);
+}
+
+void yaml_emitter_destroy(struct yaml_emitter *emitter)
+{
+	if (!emitter)
+		return;
+
+	delete_events(emitter);
+
+	kfree(emitter->buf.start);
+	STACK_DEL(emitter, emitter->states);
+	STACK_DEL(emitter, emitter->indents);
+	while (!EMPTY(empty, emitter->tags)) {
+		struct yaml_tag_directive tag =
+			POP(emitter, emitter->tags);
+
+		kfree(tag.handle);
+		kfree(tag.prefix);
+	}
+	STACK_DEL(emitter, emitter->tags);
+	kfree(emitter->anchors);
+
+	kfree(emitter);
+}
+EXPORT_SYMBOL(yaml_emitter_destroy);
+
+static int string_write_handler(void *data, const u8 *buffer, size_t size)
+{
+	struct yaml_emitter *emitter = data;
+	typeof(emitter->output.string) *out = &emitter->output.string;
+
+	if (out->size - *out->size_written < size)
+		size = out->size - *out->size_written;
+
+	memcpy(out->buffer + *out->size_written, buffer, size);
+	*out->size_written += size;
+	return 0;
+}
+
+void yaml_emitter_set_string(struct yaml_emitter *emitter,
+			     u8 *output,
+			     size_t size,
+			     size_t *size_written)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = string_write_handler;
+	emitter->write_data = emitter;
+
+	emitter->output.string.buffer = output;
+	emitter->output.string.size = size;
+	emitter->output.string.size_written = size_written;
+	*size_written = 0;
+}
+EXPORT_SYMBOL(yaml_emitter_set_string);
+
+static int seq_file_write_handler(void *data, const u8 *buffer, size_t size)
+{
+	/* Ignore the overflow error; seq will restart with a larger buffer */
+	seq_write(data, buffer, size);
+	return 0;
+}
+
+void yaml_emitter_set_seq_file(struct yaml_emitter *emitter,
+			       struct seq_file *file)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = seq_file_write_handler;
+	emitter->write_data = file;
+}
+EXPORT_SYMBOL(yaml_emitter_set_seq_file);
+
+void yaml_emitter_set_width(struct yaml_emitter *emitter, int width)
+{
+	if (!emitter)
+		return;
+
+	emitter->best_width = (width >= 0) ? width : -1;
+}
+EXPORT_SYMBOL(yaml_emitter_set_width);
+
+void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode)
+{
+	if (!emitter)
+		return;
+
+	emitter->unicode = unicode;
+}
+EXPORT_SYMBOL(yaml_emitter_set_unicode);
+
+void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent)
+{
+	if (!emitter)
+		return;
+
+	emitter->best_indent = (1 < indent && indent < 10) ? indent : 2;
+}
+EXPORT_SYMBOL(yaml_emitter_set_indent);
+
+void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical)
+{
+	if (!emitter)
+		return;
+
+	emitter->canonical = canonical;
+}
+EXPORT_SYMBOL(yaml_emitter_set_canonical);
+
+void yaml_emitter_set_output(struct yaml_emitter *emitter,
+			     yaml_write_t handler,
+			     void *data)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = handler;
+	emitter->write_data = data;
+}
+EXPORT_SYMBOL(yaml_emitter_set_output);
diff --git a/lib/yaml/yaml-emitter.h b/lib/yaml/yaml-emitter.h
new file mode 100644
index 000000000000..c4ed590f73cf
--- /dev/null
+++ b/lib/yaml/yaml-emitter.h
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef YAML_EMITTER_H
+#define YAML_EMITTER_H
+
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/yaml.h>
+
+enum yaml_emitter_state {
+	EMIT_STREAM_START_STATE,
+	EMIT_FIRST_DOCUMENT_START_STATE,
+	EMIT_DOCUMENT_START_STATE,
+	EMIT_DOCUMENT_CONTENT_STATE,
+	EMIT_DOCUMENT_END_STATE,
+	EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE,
+	EMIT_FLOW_SEQUENCE_ITEM_STATE,
+	EMIT_FLOW_MAPPING_FIRST_KEY_STATE,
+	EMIT_FLOW_MAPPING_KEY_STATE,
+	EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE,
+	EMIT_FLOW_MAPPING_VALUE_STATE,
+	EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE,
+	EMIT_BLOCK_SEQUENCE_ITEM_STATE,
+	EMIT_BLOCK_MAPPING_FIRST_KEY_STATE,
+	EMIT_BLOCK_MAPPING_KEY_STATE,
+	EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE,
+	EMIT_BLOCK_MAPPING_VALUE_STATE,
+	EMIT_END_STATE
+};
+
+struct yaml_anchors {
+	int references;
+	int anchor;
+	int serialized;
+};
+
+struct yaml_emitter {
+	int errno;
+	const char *problem;
+	gfp_t gfp;
+
+	yaml_write_t write_handler;
+	void *write_data;
+
+	union {
+		struct {
+			u8 *buffer;
+			size_t size;
+			size_t *size_written;
+		} string;
+	} output;
+
+	struct {
+		char *start;
+		char *end;
+		char *pos;
+		char *last;
+	} buf;
+
+	int canonical;
+	int best_indent;
+	int best_width;
+	int unicode;
+
+	struct {
+		enum yaml_emitter_state *start;
+		enum yaml_emitter_state *end;
+		enum yaml_emitter_state *top;
+	} states;
+
+	enum yaml_emitter_state state;
+
+	struct list_head events;
+	int num_events;
+
+	struct {
+		int *start;
+		int *end;
+		int *top;
+	} indents;
+
+	struct {
+		struct yaml_tag_directive *start;
+		struct yaml_tag_directive *end;
+		struct yaml_tag_directive *top;
+	} tags;
+
+	int indent;
+	int flow_level;
+
+	int root_context;
+	int sequence_context;
+	int mapping_context;
+	int simple_key_context;
+
+	int column;
+	int whitespace;
+	int indention;
+	int open_ended;
+
+	/** Anchor analysis. */
+	struct {
+		const char *anchor;
+		size_t anchor_length;
+		int alias;
+	} anchor_data;
+
+	/** Tag analysis. */
+	struct {
+		const char *handle;
+		size_t handle_length;
+		const char *suffix;
+		size_t suffix_length;
+	} tag_data;
+
+	/** Scalar analysis. */
+	struct {
+		const char *value;
+		size_t length;
+		int multiline;
+		int flow_plain_allowed;
+		int block_plain_allowed;
+		int single_quoted_allowed;
+		int block_allowed;
+		enum yaml_scalar_style style;
+	} scalar_data;
+
+	int opened;
+	int closed;
+
+	struct yaml_anchors *anchors;
+	int last_anchor_id;
+};
+
+#endif /* YAML_EMITTER_H */
diff --git a/lib/yaml/yaml-events.c b/lib/yaml/yaml-events.c
new file mode 100644
index 000000000000..adc7ed887d6a
--- /dev/null
+++ b/lib/yaml/yaml-events.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+static bool check_utf8(const char *pos, size_t length)
+{
+	const char *end = pos + length;
+
+	while (pos < end) {
+		u8 octet = pos[0];
+		unsigned int width;
+		unsigned int value;
+		size_t k;
+
+		width = (octet & 0x80) == 0x00 ? 1 :
+			(octet & 0xE0) == 0xC0 ? 2 :
+			(octet & 0xF0) == 0xE0 ? 3 :
+			(octet & 0xF8) == 0xF0 ? 4 : 0;
+		value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+			(octet & 0xE0) == 0xC0 ? octet & 0x1F :
+			(octet & 0xF0) == 0xE0 ? octet & 0x0F :
+			(octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+		if (!width)
+			return false;
+
+		if (pos + width > end)
+			return false;
+
+		for (k = 1; k < width; k++) {
+			octet = pos[k];
+			if ((octet & 0xC0) != 0x80)
+				return false;
+
+			value = (value << 6) + (octet & 0x3F);
+		}
+
+		if (!(width == 1 ||
+		      (width == 2 && value >= 0x80) ||
+		      (width == 3 && value >= 0x800) ||
+		      (width == 4 && value >= 0x10000)))
+			return false;
+
+		pos += width;
+	}
+
+	return true;
+}
+
+static struct yaml_event *yaml_event_create(int type, gfp_t gfp)
+{
+	struct yaml_event *ev;
+
+	ev = kzalloc(sizeof(*ev), gfp);
+	if (ev)
+		ev->type = type;
+
+	return ev;
+}
+
+static void yaml_event_free(struct yaml_event *event)
+{
+	kfree(event);
+}
+
+struct yaml_event *yaml_stream_start_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_STREAM_START_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_stream_start_event_create);
+
+struct yaml_event *yaml_stream_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_STREAM_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_stream_end_event_create);
+
+struct yaml_event *
+yaml_document_start_event_create(struct yaml_version_directive *version,
+				 struct yaml_tag_directive *start,
+				 struct yaml_tag_directive *end,
+				 int implicit,
+				 gfp_t gfp)
+{
+	struct yaml_version_directive *version_copy = NULL;
+	struct {
+		struct yaml_tag_directive *start;
+		struct yaml_tag_directive *end;
+		struct yaml_tag_directive *top;
+	} copy = {};
+	struct yaml_tag_directive value = {};
+	struct yaml_event *event;
+
+	if (!((start && end) || start == end))
+		return NULL;
+
+	event = yaml_event_create(YAML_DOCUMENT_START_EVENT, gfp);
+	if (!event)
+		return NULL;
+
+	if (version) {
+		version_copy = kmemdup(version, sizeof(*version), gfp);
+		if (!version_copy)
+			goto error;
+	}
+
+#if 0
+	if (start != end) {
+		struct yaml_tag_directive *tag;
+		struct {
+			enum yaml_error error;
+		} context;
+
+		if (!STACK_INIT(&context, copy, struct yaml_tag_directive*))
+			goto error;
+
+		for (tag = start; tag != end; tag++) {
+			if (!check_utf8(tag->handle, strlen(tag->handle)))
+				goto error;
+
+			if (!check_utf8(tag->prefix, strlen(tag->prefix)))
+				goto error;
+
+			value.handle = kstrdup(tag->handle, gfp);
+			value.prefix = kstrdup(tag->prefix, gfp);
+			if (!value.handle || !value.prefix)
+				goto error;
+
+			if (!PUSH(&context, copy, value))
+				goto error;
+
+			value.handle = NULL;
+			value.prefix = NULL;
+		}
+	}
+#endif
+
+	event->type = YAML_DOCUMENT_START_EVENT;
+	event->document_start.version = version_copy;
+	event->document_start.tags.start = copy.start;
+	event->document_start.tags.end = copy.top;
+	event->document_start.implicit = implicit;
+
+	return event;
+
+error:
+	kfree(version_copy);
+#if 0
+	while (!STACK_EMPTY(context, copy)) {
+		struct yaml_tag_directive value = POP(context, copy);
+
+		kfree(value.handle);
+		kfree(value.prefix);
+	}
+	STACK_DEL(context, copy);
+#endif
+	kfree(value.handle);
+	kfree(value.prefix);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_document_start_event_create);
+
+struct yaml_event *
+yaml_document_end_event_create(int implicit, gfp_t gfp)
+{
+	struct yaml_event *event;
+
+	event = yaml_event_create(YAML_DOCUMENT_END_EVENT, gfp);
+	if (event)
+		event->document_end.implicit = implicit;
+
+	return event;
+}
+EXPORT_SYMBOL(yaml_document_end_event_create);
+
+struct yaml_event *
+yaml_alias_event_create(const char *anchor, gfp_t gfp)
+{
+	struct yaml_event *event;
+
+	if (!anchor || !check_utf8(anchor, strlen(anchor)))
+		return NULL;
+
+	event = yaml_event_create(YAML_ALIAS_EVENT, gfp);
+	if (!event)
+		return NULL;
+
+	event->alias.anchor = kstrdup(anchor, gfp);
+	if (!event->alias.anchor) {
+		yaml_event_free(event);
+		return NULL;
+	}
+
+	return event;
+}
+EXPORT_SYMBOL(yaml_alias_event_create);
+
+struct yaml_event *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+			 const char *value, ssize_t length, bool steal,
+			 int plain_implicit, int quoted_implicit,
+			 enum yaml_scalar_style style,
+			 gfp_t gfp)
+{
+	struct yaml_event *event;
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+
+	if (!value)
+		return NULL;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	if (length < 0)
+		length = strlen(value);
+
+	if (!check_utf8(value, length))
+		goto error;
+
+	event = yaml_event_create(YAML_SCALAR_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	if (steal) {
+		event->scalar.value = (char *)value;
+	} else {
+		event->scalar.value = kmemdup(value, length, gfp);
+		if (!event->scalar.value) {
+			yaml_event_free(event);
+			goto error;
+		}
+	}
+	event->scalar.length = length;
+
+	event->scalar.anchor = anchor_copy;
+	event->scalar.tag = tag_copy;
+	event->scalar.plain_implicit = plain_implicit;
+	event->scalar.quoted_implicit = quoted_implicit;
+	event->scalar.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_scalar_event_create);
+
+struct yaml_event *
+yaml_sequence_start_event_create(const char *anchor,
+				 const char *tag,
+				 int implicit,
+				 enum yaml_sequence_style style,
+				 gfp_t gfp)
+{
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+	struct yaml_event *event;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	event = yaml_event_create(YAML_SEQUENCE_START_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	event->sequence_start.anchor = anchor_copy;
+	event->sequence_start.tag = tag_copy;
+	event->sequence_start.implicit = implicit;
+	event->sequence_start.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_sequence_start_event_create);
+
+struct yaml_event *yaml_sequence_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_SEQUENCE_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_sequence_end_event_create);
+
+struct yaml_event *
+yaml_mapping_start_event_create(const char *anchor,
+				const char *tag,
+				int implicit,
+				enum yaml_mapping_style style,
+				gfp_t gfp)
+{
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+	struct yaml_event *event;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	event = yaml_event_create(YAML_MAPPING_START_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	event->mapping_start.anchor = anchor_copy;
+	event->mapping_start.tag = tag_copy;
+	event->mapping_start.implicit = implicit;
+	event->mapping_start.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_mapping_start_event_create);
+
+struct yaml_event *yaml_mapping_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_MAPPING_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_mapping_end_event_create);
+
+void yaml_event_delete(struct yaml_event *event)
+{
+	struct yaml_tag_directive *tag;
+
+	if (!event)
+		return;
+
+	switch (event->type) {
+	case YAML_DOCUMENT_START_EVENT:
+		kfree(event->document_start.version);
+		for (tag = event->document_start.tags.start;
+		     tag != event->document_start.tags.end;
+		     tag++) {
+			kfree(tag->handle);
+			kfree(tag->prefix);
+		}
+		kfree(event->document_start.tags.start);
+		break;
+
+	case YAML_ALIAS_EVENT:
+		kfree(event->alias.anchor);
+		break;
+
+	case YAML_SCALAR_EVENT:
+		kfree(event->scalar.anchor);
+		kfree(event->scalar.tag);
+		kfree(event->scalar.value);
+		break;
+
+	case YAML_SEQUENCE_START_EVENT:
+		kfree(event->sequence_start.anchor);
+		kfree(event->sequence_start.tag);
+		break;
+
+	case YAML_MAPPING_START_EVENT:
+		kfree(event->mapping_start.anchor);
+		kfree(event->mapping_start.tag);
+		break;
+
+	default:
+		break;
+	}
+
+	yaml_event_free(event);
+}
+EXPORT_SYMBOL(yaml_event_delete);
+
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/lib/yaml/yaml-simple.c b/lib/yaml/yaml-simple.c
new file mode 100644
index 000000000000..ed8ea7637b59
--- /dev/null
+++ b/lib/yaml/yaml-simple.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#include <linux/ascii85.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+#include "yaml-emitter.h"
+
+struct yaml_emitter *__yaml_open(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event;
+
+	event = yaml_stream_start_event_create(emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_start_event_create(NULL, NULL, NULL, true,
+						 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	return emitter;
+}
+
+struct yaml_emitter *yaml_open(yaml_write_t handler, void *data)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_output(emitter, handler, data);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open);
+
+struct yaml_emitter *yaml_open_file(struct seq_file *m)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_seq_file(emitter, m);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open_file);
+
+struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *out)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_string(emitter, buf, len, out);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open_string);
+
+void __yaml_mapping_start(struct yaml_emitter *emitter,
+			  enum yaml_mapping_style style)
+{
+	struct yaml_event *event;
+
+	event = yaml_mapping_start_event_create(NULL, NULL, true, style,
+						emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(__yaml_mapping_start);
+
+void yaml_mapping_end(struct yaml_emitter *emitter)
+{
+	yaml_emitter_emit(emitter, yaml_mapping_end_event_create(emitter->gfp));
+}
+EXPORT_SYMBOL(yaml_mapping_end);
+
+void __yaml_sequence_start(struct yaml_emitter *emitter,
+			   enum yaml_sequence_style style)
+{
+	struct yaml_event *event;
+
+	event = yaml_sequence_start_event_create(NULL, NULL, true, style,
+						 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(__yaml_sequence_start);
+
+void yaml_sequence_end(struct yaml_emitter *emitter)
+{
+	yaml_emitter_emit(emitter,
+			  yaml_sequence_end_event_create(emitter->gfp));
+}
+EXPORT_SYMBOL(yaml_sequence_end);
+
+void yaml_alias_printf(struct yaml_emitter *emitter,
+		       const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	yaml_emitter_emit(emitter, yaml_alias_event_create(str, emitter->gfp));
+
+	kfree(str);
+}
+EXPORT_SYMBOL(yaml_alias_printf);
+
+void yaml_scalar_printf(struct yaml_emitter *emitter,
+			const char *fmt, ...)
+{
+	struct yaml_event *event;
+	va_list ap;
+	char *str;
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	event = yaml_scalar_event_create(NULL, NULL, str, -1, true,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_scalar_printf);
+
+void yaml_pair_printf(struct yaml_emitter *emitter,
+		      const char *name, const char *fmt, ...)
+{
+	struct yaml_event *event;
+	va_list ap;
+	char *str;
+
+	event = yaml_scalar_event_create(NULL, NULL, name, -1, false,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	event = yaml_scalar_event_create(NULL, NULL, str, -1, true,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_pair_printf);
+
+void yaml_ascii85_encode(struct yaml_emitter *emitter,
+			 const void *data, size_t len, bool compressed)
+{
+	struct yaml_event *event;
+	const u32 *src = data;
+	char *buf, *dst;
+	int i;
+
+	if (!data || !len)
+		return;
+
+	buf = kmalloc(ascii85_encode_len(len) * 5, emitter->gfp);
+	if (!buf)
+		return;
+
+	dst = buf;
+	while (len > 4) {
+		u32 in = *src++;
+
+		if (in == 0) {
+			*dst++ = 'z';
+		} else {
+			for (i = 5; i--; ) {
+				dst[i] = '!' + in % 85;
+				in /= 85;
+			}
+			dst += 5;
+		}
+		len -= 4;
+	}
+
+	if (len) {
+		u32 in = 0;
+
+		memcpy(&in, src, len);
+
+		if (in == 0) {
+			*dst++ = 'z';
+		} else {
+			for (i = 5; i--; ) {
+				dst[i] = '!' + in % 85;
+				in /= 85;
+			}
+			dst += 5;
+		}
+	}
+
+	event = yaml_scalar_event_create(NULL,
+					 compressed ? "ascii85.gz" : "ascii85",
+					 buf, dst - buf, true,
+					 false, false,
+					 YAML_FOLDED_SCALAR_STYLE,
+					 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_ascii85_encode);
+
+int yaml_close(struct yaml_emitter *emitter, const char **problem)
+{
+	int err;
+
+	yaml_emitter_emit(emitter,
+			  yaml_document_end_event_create(true, GFP_KERNEL));
+	yaml_emitter_emit(emitter,
+			  yaml_stream_end_event_create(GFP_KERNEL));
+
+	err = yaml_emitter_flush(emitter, problem);
+	yaml_emitter_destroy(emitter);
+
+	return err;
+}
+EXPORT_SYMBOL(yaml_close);
-- 
2.25.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 7+ messages in thread
* [Intel-gfx] [PATCH] lib: Add a YAML emitter
@ 2021-02-11 11:38 Chris Wilson
  0 siblings, 0 replies; 7+ messages in thread
From: Chris Wilson @ 2021-02-11 11:38 UTC (permalink / raw)
  To: intel-gfx; +Cc: Jani Nikula, Chris Wilson

Provide a library to generate correct YAML for use in structured debugfs
or similar information dumps. This will be useful to pull our large
information dumps into a forwards compatible, parse-able file-format by
forcing some structure upon ourselves!

Originally from https://github.com/yaml/libyaml/blob/master/src/emitter.c
under a permissive MIT licence.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>

---
Converting to kerneldoc is about the last major task. It uses an
opencoded stack struct which might be nice to convert to a library,
maybe just use XArray?

It has been dogfooded to convert i915_engine_info (and friends)
https://patchwork.freedesktop.org/patch/353209/?series=73403&rev=1
---
 include/linux/yaml-events.h |  259 ++++
 include/linux/yaml.h        |  190 +++
 lib/Kconfig                 |   11 +
 lib/Makefile                |    2 +
 lib/yaml/Makefile           |    8 +
 lib/yaml/test-yaml.c        |  123 ++
 lib/yaml/yaml-emitter.c     | 2539 +++++++++++++++++++++++++++++++++++
 lib/yaml/yaml-emitter.h     |  140 ++
 lib/yaml/yaml-events.c      |  424 ++++++
 lib/yaml/yaml-simple.c      |  227 ++++
 10 files changed, 3923 insertions(+)
 create mode 100644 include/linux/yaml-events.h
 create mode 100644 include/linux/yaml.h
 create mode 100644 lib/yaml/Makefile
 create mode 100644 lib/yaml/test-yaml.c
 create mode 100644 lib/yaml/yaml-emitter.c
 create mode 100644 lib/yaml/yaml-emitter.h
 create mode 100644 lib/yaml/yaml-events.c
 create mode 100644 lib/yaml/yaml-simple.c

diff --git a/include/linux/yaml-events.h b/include/linux/yaml-events.h
new file mode 100644
index 000000000000..33d1bb227e65
--- /dev/null
+++ b/include/linux/yaml-events.h
@@ -0,0 +1,259 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef __LINUX_YAML_EVENTS_H__
+#define __LINUX_YAML_EVENTS_H__
+
+#include <linux/gfp.h>
+#include <linux/types.h>
+#include <linux/list.h>
+
+/** The version directive data. */
+struct yaml_version_directive {
+	int major;
+	int minor;
+};
+
+/** The tag directive data. */
+struct yaml_tag_directive {
+	char *handle;
+	char *prefix;
+};
+
+/** The pointer position. */
+struct yaml_mark {
+	size_t index;
+	size_t line;
+	size_t column;
+};
+
+enum yaml_event_type {
+	YAML_NO_EVENT = 0,
+
+	YAML_STREAM_START_EVENT,
+	YAML_STREAM_END_EVENT,
+
+	YAML_DOCUMENT_START_EVENT,
+	YAML_DOCUMENT_END_EVENT,
+
+	YAML_ALIAS_EVENT,
+	YAML_SCALAR_EVENT,
+
+	YAML_SEQUENCE_START_EVENT,
+	YAML_SEQUENCE_END_EVENT,
+
+	YAML_MAPPING_START_EVENT,
+	YAML_MAPPING_END_EVENT
+};
+
+struct yaml_event {
+	enum yaml_event_type type;
+	struct list_head link;
+
+	union {
+		/** The stream parameters (for YAML_STREAM_START_EVENT). */
+		struct {
+		} stream_start;
+
+		/** The document parameters (for YAML_DOCUMENT_START_EVENT). */
+		struct {
+			struct yaml_version_directive *version;
+
+			struct {
+				struct yaml_tag_directive *start;
+				struct yaml_tag_directive *end;
+			} tags;
+
+			int implicit;
+		} document_start;
+
+		/** The document end parameters (for YAML_DOCUMENT_END_EVENT). */
+		struct {
+			int implicit;
+		} document_end;
+
+		/** The alias parameters (for YAML_ALIAS_EVENT). */
+		struct {
+			char *anchor;
+		} alias;
+
+		/** The scalar parameters (for YAML_SCALAR_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			char *value;
+			size_t length;
+			int plain_implicit;
+			int quoted_implicit;
+			enum yaml_scalar_style style;
+		} scalar;
+
+		/** The sequence parameters (for YAML_SEQUENCE_START_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			int implicit;
+			enum yaml_sequence_style style;
+		} sequence_start;
+
+		/** The mapping parameters (for YAML_MAPPING_START_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			int implicit;
+			enum yaml_mapping_style style;
+		} mapping_start;
+	};
+
+	struct yaml_mark start_mark;
+	struct yaml_mark end_mark;
+};
+
+struct yaml_event *yaml_stream_start_event_create(gfp_t gfp);
+struct yaml_event *yaml_stream_end_event_create(gfp_t gfp);
+
+/**
+ * Create the DOCUMENT-START event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[in]       version_directive       The %YAML directive value or
+ *                                          @c NULL.
+ * @param[in]       tag_directives_start    The beginning of the %TAG
+ *                                          directives list.
+ * @param[in]       tag_directives_end      The end of the %TAG directives
+ *                                          list.
+ * @param[in]       implicit                If the document start indicator is
+ *                                          implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_document_start_event_create(struct yaml_version_directive *version,
+				 struct yaml_tag_directive *start,
+				 struct yaml_tag_directive *end,
+				 int implicit,
+				 gfp_t gfp);
+
+/**
+ * Create the DOCUMENT-END event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[in]       implicit    If the document end indicator is implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_document_end_event_create(int implicit, gfp_t gfp);
+
+/**
+ * Create an ALIAS event.
+ *
+ * @param[in]       anchor      The anchor value.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_alias_event_create(const char *anchor, gfp_t gfp);
+
+/**
+ * Create a SCALAR event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or one of the @a plain_implicit and
+ * @a quoted_implicit flags must be set.
+ *
+ * @param[in]       anchor          The scalar anchor or @c NULL.
+ * @param[in]       tag             The scalar tag or @c NULL.
+ * @param[in]       value           The scalar value.
+ * @param[in]       length          The length of the scalar value.
+ * @param[in]       plain_implicit  If the tag may be omitted for the plain
+ *                                  style.
+ * @param[in]       quoted_implicit If the tag may be omitted for any
+ *                                  non-plain style.
+ * @param[in]       style           The scalar style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+			 const char *value, ssize_t length, bool steal,
+			 int plain_implicit, int quoted_implicit,
+			 enum yaml_scalar_style style,
+			 gfp_t gfp);
+
+/**
+ * Create a SEQUENCE-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[in]       anchor      The sequence anchor or @c NULL.
+ * @param[in]       tag         The sequence tag or @c NULL.
+ * @param[in]       implicit    If the tag may be omitted.
+ * @param[in]       style       The sequence style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_sequence_start_event_create(const char *anchor,
+				 const char *tag,
+				 int implicit,
+				 enum yaml_sequence_style style,
+				 gfp_t gfp);
+
+/**
+ * Create a SEQUENCE-END event.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_sequence_end_event_create(gfp_t gfp);
+
+/**
+ * Create a MAPPING-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[in]       anchor      The mapping anchor or @c NULL.
+ * @param[in]       tag         The mapping tag or @c NULL.
+ * @param[in]       implicit    If the tag may be omitted.
+ * @param[in]       style       The mapping style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_mapping_start_event_create(const char *anchor,
+				const char *tag,
+				int implicit,
+				enum yaml_mapping_style style,
+				gfp_t gfp);
+
+/**
+ * Create a MAPPING-END event.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_mapping_end_event_create(gfp_t gfp);
+
+/**
+ * Free any memory allocated for an event object.
+ *
+ * @param[in,out]   event   An event object.
+ */
+
+void yaml_event_delete(struct yaml_event *event);
+
+#endif /* __LINUX_YAML_EVENTS_H__ */
diff --git a/include/linux/yaml.h b/include/linux/yaml.h
new file mode 100644
index 000000000000..541095a1669e
--- /dev/null
+++ b/include/linux/yaml.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef __LINUX_YAML_H__
+#define __LINUX_YAML_H__
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct seq_file;
+
+struct yaml_emitter;
+struct yaml_event;
+
+/** Scalar styles. */
+enum yaml_scalar_style {
+	YAML_ANY_SCALAR_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_PLAIN_SCALAR_STYLE,
+
+	YAML_SINGLE_QUOTED_SCALAR_STYLE,
+	YAML_DOUBLE_QUOTED_SCALAR_STYLE,
+
+	YAML_LITERAL_SCALAR_STYLE,
+	YAML_FOLDED_SCALAR_STYLE,
+};
+
+/** Sequence styles. */
+enum yaml_sequence_style {
+	YAML_ANY_SEQUENCE_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_BLOCK_SEQUENCE_STYLE,
+	YAML_FLOW_SEQUENCE_STYLE,
+};
+
+/** Mapping styles. */
+enum yaml_mapping_style {
+	YAML_ANY_MAPPING_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_BLOCK_MAPPING_STYLE,
+	YAML_FLOW_MAPPING_STYLE
+};
+
+typedef int (*yaml_write_t)(void *data, const u8 *buffer, size_t size);
+
+/**
+ * Initialize an emitter.
+ *
+ * This function creates a new emitter object.  An application is responsible
+ * for destroying the object using the yaml_emitter_delete() function.
+ *
+ * @param[out]      emitter     An empty parser object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_emitter *yaml_emitter_create(gfp_t gfp);
+
+/**
+ * Destroy an emitter.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ */
+void yaml_emitter_destroy(struct yaml_emitter *emitter);
+
+/**
+ * Set a string output.
+ *
+ * The emitter will write the output characters to the @a output buffer of the
+ * size @a size.  The emitter will set @a size_written to the number of written
+ * bytes.  If the buffer is smaller than required, the emitter produces the
+ * YAML_WRITE_ERROR error.
+ *
+ * @param[in,out]   emitter         An emitter object.
+ * @param[in]       output          An output buffer.
+ * @param[in]       size            The buffer size.
+ * @param[in]       size_written    The pointer to save the number of written
+ *                                  bytes.
+ */
+void yaml_emitter_set_string(struct yaml_emitter *emitter,
+			     u8 *output, size_t size,
+			     size_t *size_written);
+
+void yaml_emitter_set_seq_file(struct yaml_emitter *emitter,
+			       struct seq_file *file);
+
+void yaml_emitter_set_output(struct yaml_emitter *emitter,
+			     yaml_write_t handler,
+			     void *data);
+
+/**
+ * Set if the output should be in the "canonical" format as in the YAML
+ * specification.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       canonical   If the output is canonical.
+ */
+void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical);
+
+/**
+ * Set the indentation increment.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       indent      The indentation increment (1 < . < 10).
+ */
+void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent);
+
+/**
+ * Set the preferred line width. @c -1 means unlimited.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       width       The preferred line width.
+ */
+void yaml_emitter_set_width(struct yaml_emitter *emitter, int width);
+
+/**
+ * Set if unescaped non-ASCII characters are allowed.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       unicode     If unescaped Unicode characters are allowed.
+ */
+void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode);
+
+/**
+ * Emit an event.
+ *
+ * The event object may be generated using the yaml_parser_parse() function.
+ * The emitter takes the responsibility for the event object and destroys its
+ * content after it is emitted. The event object is destroyed even if the
+ * function fails.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in,out]   event       An event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event);
+
+/**
+ * Flush the accumulated characters to the output.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem);
+
+gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp);
+
+struct yaml_emitter *yaml_open(yaml_write_t handler, void *data);
+struct yaml_emitter *yaml_open_file(struct seq_file *m);
+struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *written);
+
+void __yaml_mapping_start(struct yaml_emitter *emitter,
+			  enum yaml_mapping_style style);
+static inline void yaml_mapping_start(struct yaml_emitter *emitter)
+{
+	__yaml_mapping_start(emitter, YAML_ANY_MAPPING_STYLE);
+}
+
+void yaml_mapping_end(struct yaml_emitter *emitter);
+
+void __yaml_sequence_start(struct yaml_emitter *emitter,
+			   enum yaml_sequence_style style);
+static inline void yaml_sequence_start(struct yaml_emitter *emitter)
+{
+	__yaml_sequence_start(emitter, YAML_ANY_SEQUENCE_STYLE);
+}
+
+void yaml_sequence_end(struct yaml_emitter *emitter);
+
+__printf(2, 3)
+void yaml_alias_printf(struct yaml_emitter *emitter, const char *fmt, ...);
+
+__printf(2, 3)
+void yaml_scalar_printf(struct yaml_emitter *emitter, const char *fmt, ...);
+
+__printf(3, 4)
+void yaml_pair_printf(struct yaml_emitter *emitter,
+		      const char *name, const char *fmt, ...);
+
+void yaml_ascii85_encode(struct yaml_emitter *emitter,
+			 const void *data, size_t len, bool compressed);
+
+int yaml_close(struct yaml_emitter *emitter, const char **problem);
+
+#endif /* __LINUX_YAML_H__ */
diff --git a/lib/Kconfig b/lib/Kconfig
index 46806332a8cc..4829719e8b9b 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -663,6 +663,17 @@ config OBJAGG
 config STRING_SELFTEST
 	tristate "Test string functions"
 
+config YAML
+	tristate
+
+config YAML_EMITTER
+	tristate
+	select YAML
+
+config TEST_YAML
+	tristate "Small test module for libyaml"
+	depends on YAML
+
 endmenu
 
 config GENERIC_IOREMAP
diff --git a/lib/Makefile b/lib/Makefile
index afeff05fa8c5..0ace23ef51bc 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -285,6 +285,8 @@ obj-$(CONFIG_ASN1) += asn1_decoder.o
 
 obj-$(CONFIG_FONT_SUPPORT) += fonts/
 
+obj-$(CONFIG_YAML) += yaml/
+
 hostprogs	:= gen_crc32table
 hostprogs	+= gen_crc64table
 clean-files	:= crc32table.h
diff --git a/lib/yaml/Makefile b/lib/yaml/Makefile
new file mode 100644
index 000000000000..f5e8c1795914
--- /dev/null
+++ b/lib/yaml/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+yaml-objs = yaml-events.o
+yaml-objs-$(CONFIG_YAML_EMITTER) += yaml-emitter.o yaml-simple.o
+yaml-objs += $(yaml-objs-m)
+
+obj-$(CONFIG_YAML) += yaml.o
+obj-$(CONFIG_TEST_YAML) += test-yaml.o
diff --git a/lib/yaml/test-yaml.c b/lib/yaml/test-yaml.c
new file mode 100644
index 000000000000..5c3c7a928859
--- /dev/null
+++ b/lib/yaml/test-yaml.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL
+/*
+ * Copyright (c) 2020 Intel Corporation
+ */
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+struct dentry *root;
+
+#if IS_ENABLED(CONFIG_YAML_EMITTER)
+static int y_events_show(struct seq_file *m, void *data)
+{
+	struct yaml_emitter *emitter;
+	struct yaml_event *event;
+	const char *problem;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return -ENOMEM;
+
+	yaml_emitter_set_seq_file(emitter, m);
+
+	event = yaml_stream_start_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_start_event_create(NULL, NULL, NULL, true,
+						 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_mapping_start_event_create(NULL, NULL, true,
+						YAML_FLOW_MAPPING_STYLE,
+						GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_scalar_event_create(NULL, NULL,
+					 "General", -1, false,
+					 true, true, YAML_PLAIN_SCALAR_STYLE,
+					 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_scalar_event_create(NULL, NULL,
+					 "Kenobi", -1, false,
+					 true, true, YAML_PLAIN_SCALAR_STYLE,
+					 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_mapping_end_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_end_event_create(true, GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_stream_end_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	if (yaml_emitter_flush(emitter, &problem))
+		pr_err("test-yaml events failed: %s\n", problem);
+
+	yaml_emitter_destroy(emitter);
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(y_events);
+
+static int y_simple_show(struct seq_file *m, void *data)
+{
+	struct yaml_emitter *yaml;
+	const char *problem;
+
+	yaml = yaml_open_file(m);
+	if (!yaml)
+		return -ENOMEM;
+
+	yaml_mapping_start(yaml); {
+		yaml_scalar_printf(yaml, "map");
+		yaml_mapping_start(yaml); {
+			yaml_scalar_printf(yaml, "Hello");
+			yaml_mapping_start(yaml); {
+				yaml_pair_printf(yaml,
+						 "General", "%s", "Kenobi");
+			} yaml_mapping_end(yaml);
+		} yaml_mapping_end(yaml);
+
+		yaml_scalar_printf(yaml, "sequence");
+		yaml_sequence_start(yaml); {
+			yaml_scalar_printf(yaml, "1");
+			yaml_scalar_printf(yaml, "2");
+			yaml_scalar_printf(yaml, "3");
+			yaml_ascii85_encode(yaml, "4", 2, false);
+		} yaml_sequence_end(yaml);
+	} yaml_mapping_end(yaml);
+
+	if (yaml_close(yaml, &problem))
+		pr_err("test-yaml simple failed: %s\n", problem);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(y_simple);
+#endif
+
+static int __init test_yaml_init(void)
+{
+	root = debugfs_create_dir("libyaml", NULL);
+	if (!root)
+		return -ENOMEM;
+
+#if IS_ENABLED(CONFIG_YAML_EMITTER)
+	debugfs_create_file("emit-events", 0444, root, NULL, &y_events_fops);
+	debugfs_create_file("emit-simple", 0444, root, NULL, &y_simple_fops);
+#endif
+
+	return 0;
+}
+
+static void __exit test_yaml_exit(void)
+{
+	debugfs_remove(root);
+}
+
+module_init(test_yaml_init);
+module_exit(test_yaml_exit);
+MODULE_LICENSE("GPL");
diff --git a/lib/yaml/yaml-emitter.c b/lib/yaml/yaml-emitter.c
new file mode 100644
index 000000000000..ab45070fc933
--- /dev/null
+++ b/lib/yaml/yaml-emitter.c
@@ -0,0 +1,2539 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+#include "yaml-emitter.h"
+
+#define INITIAL_STACK_SIZE  16
+
+struct yaml_string {
+	const char *start;
+	const char *end;
+	const char *pos;
+};
+
+#define STRING_INIT(str, len) { (str), (str) + (len), (str) }
+
+#define YAML_STRING(name, str) \
+	struct yaml_string name = STRING_INIT(str, strlen(str))
+
+#define CHECK_AT(s, octet, offset)					\
+	((s).pos[offset] == (char)(octet))
+#define CHECK(s, octet) (CHECK_AT((s), (octet), 0))
+
+#define IS_ALPHA_AT(s, offset)						\
+	(isalnum((s).pos[offset]) ||					\
+	 (s).pos[offset] == '_' ||					\
+	 (s).pos[offset] == '-')
+#define IS_ALPHA(s)	IS_ALPHA_AT((s), 0)
+
+#define IS_ASCII_AT(s, offset) ((s).pos[offset] <= '\x7F')
+#define IS_ASCII(s)    IS_ASCII_AT((s), 0)
+
+#define IS_PRINTABLE_AT(s, offset) isprint((s).pos[offset])
+#define IS_PRINTABLE(s)    IS_PRINTABLE_AT((s), 0)
+
+#define IS_Z_AT(s, offset)    CHECK_AT((s), '\0', (offset))
+#define IS_Z(s)    IS_Z_AT((s), 0)
+
+#define IS_BOM_AT(s, offset)						\
+	(CHECK_AT((s), '\xEF', (offset) + 0) &&				\
+	 CHECK_AT((s), '\xBB', (offset) + 1) &&				\
+	 CHECK_AT((s), '\xBF', (offset) + 2))  /* BOM (#xFEFF) */
+#define IS_BOM(s)  IS_BOM_AT(s, 0)
+
+#define IS_SPACE_AT(s, offset)  CHECK_AT((s), ' ', (offset))
+#define IS_SPACE(s)	IS_SPACE_AT((s), 0)
+
+#define IS_TAB_AT(s, offset)    CHECK_AT((s), '\t', (offset))
+#define IS_TAB(s)	IS_TAB_AT((s), 0)
+
+#define IS_BLANK_AT(s, offset)						\
+	(IS_SPACE_AT((s), (offset)) || IS_TAB_AT((s), (offset)))
+#define IS_BLANK(s)	IS_BLANK_AT((s), 0)
+
+#define IS_BREAK_AT(s, offset)						\
+	(CHECK_AT((s), '\r', (offset)) || /* CR (#xD)*/			\
+	 CHECK_AT((s), '\n', (offset)) || /* LF (#xA) */		\
+	 (CHECK_AT((s), '\xC2', (offset)) &&				\
+	  CHECK_AT((s), '\x85', (offset) + 1)) || /* NEL (#x85) */	\
+	 (CHECK_AT((s), '\xE2', (offset)) &&				\
+	  CHECK_AT((s), '\x80', (offset) + 1) &&			\
+	  CHECK_AT((s), '\xA8', (offset) + 2)) ||   /* LS (#x2028) */	\
+	 (CHECK_AT((s), '\xE2', (offset)) &&				\
+	  CHECK_AT((s), '\x80', (offset) + 1) &&			\
+	    CHECK_AT((s), '\xA9', (offset) + 2)))  /* PS (#x2029) */
+#define IS_BREAK(s)	IS_BREAK_AT((s), 0)
+
+#define IS_CRLF_AT(s, offset)						\
+	(CHECK_AT((s), '\r', (offset)) && CHECK_AT((s), '\n', (offset) + 1))
+#define IS_CRLF(s)	IS_CRLF_AT((s), 0)
+
+#define IS_BREAKZ_AT(s, offset)						\
+	(IS_BREAK_AT((s), (offset)) || IS_Z_AT((s), (offset)))
+#define IS_BREAKZ(s)	IS_BREAKZ_AT((s), 0)
+
+#define IS_SPACEZ_AT(s, offset)						\
+	(IS_SPACE_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset)))
+#define IS_SPACEZ(s)	IS_SPACEZ_AT((s), 0)
+
+#define IS_BLANKZ_AT(s, offset)						\
+	(IS_BLANK_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset)))
+#define IS_BLANKZ(s)	IS_BLANKZ_AT((s), 0)
+
+static inline int __utf8_width(const struct yaml_string *s, int idx)
+{
+	return ((s->pos[idx] & 0x80) == 0x00 ? 1 :
+		(s->pos[idx] & 0xE0) == 0xC0 ? 2 :
+		(s->pos[idx] & 0xF0) == 0xE0 ? 3 :
+		(s->pos[idx] & 0xF8) == 0xF0 ? 4 :
+		0);
+}
+
+static inline int utf8_width(const struct yaml_string *s)
+{
+	return __utf8_width(s, 0);
+}
+
+#define ADVANCE(s)	((s).pos += utf8_width((&s)))
+
+static bool yaml_stack_extend(void **start, void **top, void **end);
+
+#define EMPTY(context, stack) ((stack).start == (stack).top)
+
+#define PUSH(context, stack, value)					\
+	(((stack).top != (stack).end ||					\
+	  !yaml_stack_extend((void **)&(stack).start,			\
+			     (void **)&(stack).top, (void **)&(stack).end)) ? \
+	 (*((stack).top++) = value, 0) :				\
+	 ((context)->errno = -ENOMEM))
+
+#define POP(context, stack) (*(--(stack).top))
+
+static int
+__set_error(struct yaml_emitter *emitter, const char *problem, int err)
+{
+	WARN_ON(emitter->errno);
+
+	emitter->errno = err;
+	emitter->problem = problem;
+
+	return err;
+}
+
+static int set_error(struct yaml_emitter *emitter, const char *problem)
+{
+	return __set_error(emitter, problem, EINVAL);
+}
+
+static int flush(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (unlikely(emitter->errno))
+		return emitter->errno;
+
+	emitter->buf.last = emitter->buf.pos;
+	emitter->buf.pos = emitter->buf.start;
+
+	if (emitter->buf.start == emitter->buf.last)
+		return 0;
+
+	err = emitter->write_handler(emitter->write_data,
+				     emitter->buf.start,
+				     emitter->buf.last - emitter->buf.start);
+	if (unlikely(err < 0))
+		return __set_error(emitter, "write error", err);
+
+	emitter->buf.last = emitter->buf.start;
+	emitter->buf.pos = emitter->buf.start;
+	return 0;
+}
+
+static int maybe_flush(struct yaml_emitter *emitter, int len)
+{
+	if (emitter->buf.pos + len < emitter->buf.end)
+		return 0;
+
+	return flush(emitter);
+}
+
+static int put(struct yaml_emitter *emitter, const char value)
+{
+	int err;
+
+	err = maybe_flush(emitter, 5);
+	if (err)
+		return err;
+
+	*emitter->buf.pos++ = value;
+	emitter->column++;
+	return 0;
+}
+
+static int put_break(struct yaml_emitter *emitter)
+{
+	int err;
+
+	err =  put(emitter, '\n');
+	if (err)
+		return err;
+
+	emitter->column = 0;
+	return 0;
+}
+
+static int put_char(struct yaml_emitter *emitter, struct yaml_string *str)
+{
+	const int width = utf8_width(str);
+	int err;
+
+	err = maybe_flush(emitter, width);
+	if (err)
+		return err;
+
+	memcpy(emitter->buf.pos, str->pos, width);
+	emitter->buf.pos += width;
+	str->pos += width;
+
+	emitter->column++;
+	return 0;
+}
+
+static int put_char_break(struct yaml_emitter *emitter, struct yaml_string *str)
+{
+	const int width = utf8_width(str);
+	int err;
+
+	err = maybe_flush(emitter, width);
+	if (err)
+		return err;
+
+	memcpy(emitter->buf.pos, str->pos, width);
+	emitter->buf.pos += width;
+	str->pos += width;
+
+	emitter->column = 0;
+	return 0;
+}
+
+static struct yaml_event *next_event(const struct yaml_emitter *emitter)
+{
+	return list_first_entry(&emitter->events, struct yaml_event, link);
+}
+
+static bool need_more_events(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event;
+	int acc;
+
+	if (list_empty(&emitter->events))
+		return true;
+
+	acc = 0;
+	switch (next_event(emitter)->type) {
+	case YAML_DOCUMENT_START_EVENT:
+		acc = 1;
+		break;
+	case YAML_SEQUENCE_START_EVENT:
+		acc = 2;
+		break;
+	case YAML_MAPPING_START_EVENT:
+		acc = 3;
+		break;
+
+	default:
+		return false;
+	}
+
+	if (emitter->num_events > acc)
+		return false;
+
+	acc = 0;
+	list_for_each_entry(event, &emitter->events, link) {
+		switch (event->type) {
+		case YAML_STREAM_START_EVENT:
+		case YAML_DOCUMENT_START_EVENT:
+		case YAML_SEQUENCE_START_EVENT:
+		case YAML_MAPPING_START_EVENT:
+			acc++;
+			break;
+
+		case YAML_STREAM_END_EVENT:
+		case YAML_DOCUMENT_END_EVENT:
+		case YAML_SEQUENCE_END_EVENT:
+		case YAML_MAPPING_END_EVENT:
+			acc--;
+			break;
+
+		default:
+			break;
+		}
+
+		if (!acc)
+			return false;
+	}
+
+	return true;
+}
+
+static int
+analyze_version_directive(struct yaml_emitter *emitter,
+			  struct yaml_version_directive version)
+{
+	if (version.major != 1 || version.minor != 1)
+		return set_error(emitter, "incompatible %YAML directive");
+
+	return 0;
+}
+
+static int
+analyze_tag_directive(struct yaml_emitter *emitter,
+		      struct yaml_tag_directive tag)
+{
+	YAML_STRING(handle, tag.handle);
+	YAML_STRING(prefix, tag.prefix);
+
+	if (handle.start == handle.end)
+		return set_error(emitter, "tag handle must not be empty");
+
+	if (handle.start[0] != '!')
+		return set_error(emitter, "tag handle must start with '!'");
+
+	if (handle.end[-1] != '!')
+		return set_error(emitter, "tag handle must end with '!'");
+
+	handle.pos++;
+
+	while (handle.pos < handle.end - 1) {
+		if (!IS_ALPHA(handle))
+			return set_error(emitter,
+					 "tag handle must contain alphanumerical characters only");
+
+		ADVANCE(handle);
+	}
+
+	if (prefix.start == prefix.end)
+		return set_error(emitter, "tag prefix must not be empty");
+
+	return 0;
+}
+
+static int
+analyze_anchor(struct yaml_emitter *emitter, const char *anchor, int alias)
+{
+	YAML_STRING(string, anchor);
+
+	if (string.start == string.end) {
+		return set_error(emitter, alias ?
+				 "alias value must not be empty" :
+				 "anchor value must not be empty");
+	}
+
+	while (string.pos != string.end) {
+		if (!IS_ALPHA(string)) {
+			return set_error(emitter, alias ?
+					 "alias value must contain alphanumerical characters only" :
+					 "anchor value must contain alphanumerical characters only");
+		}
+		ADVANCE(string);
+	}
+
+	emitter->anchor_data.anchor = string.start;
+	emitter->anchor_data.anchor_length = string.end - string.start;
+	emitter->anchor_data.alias = alias;
+	return 0;
+}
+
+static int analyze_tag(struct yaml_emitter *emitter, const char *str)
+{
+	YAML_STRING(string, str);
+	struct yaml_tag_directive *tag;
+
+	if (string.start == string.end)
+		return set_error(emitter, "tag value must not be empty");
+
+	for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) {
+		size_t prefix_length = strlen(tag->prefix);
+
+		if (prefix_length < string.end - string.start &&
+		    strncmp(tag->prefix, string.start, prefix_length) == 0) {
+			emitter->tag_data.handle = tag->handle;
+			emitter->tag_data.handle_length = strlen(tag->handle);
+			emitter->tag_data.suffix = string.start + prefix_length;
+			emitter->tag_data.suffix_length =
+				(string.end - string.start) - prefix_length;
+			return 0;
+		}
+	}
+
+	emitter->tag_data.suffix = string.start;
+	emitter->tag_data.suffix_length = string.end - string.start;
+	return 0;
+}
+
+static int
+analyze_scalar(struct yaml_emitter *emitter, const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+
+	int block_indicators = 0;
+	int flow_indicators = 0;
+	int line_breaks = 0;
+	int special_characters = 0;
+
+	int leading_space = 0;
+	int leading_break = 0;
+	int trailing_space = 0;
+	int trailing_break = 0;
+	int break_space = 0;
+	int space_break = 0;
+
+	int preceded_by_whitespace = 0;
+	int followed_by_whitespace = 0;
+	int previous_space = 0;
+	int previous_break = 0;
+
+	emitter->scalar_data.value = value;
+	emitter->scalar_data.length = length;
+
+	if (string.start == string.end) {
+		emitter->scalar_data.multiline = 0;
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 1;
+		emitter->scalar_data.single_quoted_allowed = 1;
+		emitter->scalar_data.block_allowed = 0;
+		return 0;
+	}
+
+	if ((CHECK_AT(string, '-', 0) &&
+	     CHECK_AT(string, '-', 1) &&
+	     CHECK_AT(string, '-', 2)) ||
+	    (CHECK_AT(string, '.', 0) &&
+	     CHECK_AT(string, '.', 1) &&
+	     CHECK_AT(string, '.', 2))) {
+		block_indicators = 1;
+		flow_indicators = 1;
+	}
+
+	preceded_by_whitespace = 1;
+	followed_by_whitespace = IS_BLANKZ_AT(string, utf8_width(&string));
+
+	while (string.pos != string.end) {
+		if (string.start == string.pos) {
+			if (CHECK(string, '#') || CHECK(string, ',') ||
+			    CHECK(string, '[') || CHECK(string, ']') ||
+			    CHECK(string, '{') || CHECK(string, '}') ||
+			    CHECK(string, '&') || CHECK(string, '*') ||
+			    CHECK(string, '!') || CHECK(string, '|') ||
+			    CHECK(string, '>') || CHECK(string, '\'') ||
+			    CHECK(string, '"') || CHECK(string, '%') ||
+			    CHECK(string, '@') || CHECK(string, '`')) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+
+			if (CHECK(string, '?') || CHECK(string, ':')) {
+				flow_indicators = 1;
+				if (followed_by_whitespace)
+					block_indicators = 1;
+			}
+
+			if (CHECK(string, '-') && followed_by_whitespace) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+		} else {
+			if (CHECK(string, ',') || CHECK(string, '?') ||
+			    CHECK(string, '[') || CHECK(string, ']') ||
+			    CHECK(string, '{') || CHECK(string, '}'))
+				flow_indicators = 1;
+
+			if (CHECK(string, ':')) {
+				flow_indicators = 1;
+				if (followed_by_whitespace)
+					block_indicators = 1;
+			}
+
+			if (CHECK(string, '#') && preceded_by_whitespace) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+		}
+
+		if (!IS_PRINTABLE(string) ||
+		    (!IS_ASCII(string) && !emitter->unicode)) {
+			special_characters = 1;
+		}
+
+		if (IS_BREAK(string))
+			line_breaks = 1;
+
+		if (IS_SPACE(string)) {
+			if (string.start == string.pos)
+				leading_space = 1;
+
+			if (string.pos + utf8_width(&string) == string.end)
+				trailing_space = 1;
+
+			if (previous_break)
+				break_space = 1;
+
+			previous_space = 1;
+			previous_break = 0;
+		} else if (IS_BREAK(string)) {
+			if (string.start == string.pos)
+				leading_break = 1;
+
+			if (string.pos + utf8_width(&string) == string.end)
+				trailing_break = 1;
+
+			if (previous_space)
+				space_break = 1;
+
+			previous_space = 0;
+			previous_break = 1;
+		} else {
+			previous_space = 0;
+			previous_break = 0;
+		}
+
+		preceded_by_whitespace = IS_BLANKZ(string);
+		ADVANCE(string);
+		if (string.pos != string.end)
+			followed_by_whitespace =
+				IS_BLANKZ_AT(string, utf8_width(&string));
+	}
+
+	emitter->scalar_data.multiline = line_breaks;
+
+	emitter->scalar_data.flow_plain_allowed = 1;
+	emitter->scalar_data.block_plain_allowed = 1;
+	emitter->scalar_data.single_quoted_allowed = 1;
+	emitter->scalar_data.block_allowed = 1;
+
+	if (leading_space ||
+	    leading_break ||
+	    trailing_space ||
+	    trailing_break) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+	}
+
+	if (trailing_space)
+		emitter->scalar_data.block_allowed = 0;
+
+	if (break_space) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+		emitter->scalar_data.single_quoted_allowed = 0;
+	}
+
+	if (space_break || special_characters) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+		emitter->scalar_data.single_quoted_allowed = 0;
+		emitter->scalar_data.block_allowed = 0;
+	}
+
+	if (line_breaks) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+	}
+
+	if (flow_indicators)
+		emitter->scalar_data.flow_plain_allowed = 0;
+
+	if (block_indicators)
+		emitter->scalar_data.block_plain_allowed = 0;
+
+	return 0;
+}
+
+static int
+analyze_event(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	emitter->anchor_data.anchor = NULL;
+	emitter->anchor_data.anchor_length = 0;
+	emitter->tag_data.handle = NULL;
+	emitter->tag_data.handle_length = 0;
+	emitter->tag_data.suffix = NULL;
+	emitter->tag_data.suffix_length = 0;
+	emitter->scalar_data.value = NULL;
+	emitter->scalar_data.length = 0;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		return analyze_anchor(emitter, event->alias.anchor, 1);
+
+	case YAML_SCALAR_EVENT:
+		if (event->scalar.anchor) {
+			err = analyze_anchor(emitter, event->scalar.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->scalar.tag &&
+		    (emitter->canonical ||
+		     (!event->scalar.plain_implicit &&
+		      !event->scalar.quoted_implicit))) {
+			err = analyze_tag(emitter, event->scalar.tag);
+			if (err)
+				return err;
+		}
+
+		return analyze_scalar(emitter,
+				      event->scalar.value,
+				      event->scalar.length);
+
+	case YAML_SEQUENCE_START_EVENT:
+		if (event->sequence_start.anchor) {
+			err = analyze_anchor(emitter,
+					     event->sequence_start.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->sequence_start.tag &&
+		    (emitter->canonical || !event->sequence_start.implicit)) {
+			err = analyze_tag(emitter,
+					  event->sequence_start.tag);
+			if (err)
+				return err;
+		}
+
+		return 0;
+
+	case YAML_MAPPING_START_EVENT:
+		if (event->mapping_start.anchor) {
+			err = analyze_anchor(emitter,
+					     event->mapping_start.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->mapping_start.tag &&
+		    (emitter->canonical || !event->mapping_start.implicit)) {
+			err = analyze_tag(emitter, event->mapping_start.tag);
+			if (err)
+				return err;
+		}
+
+		return 0;
+
+	default:
+		return 0;
+	}
+}
+
+static bool check_empty_document(const struct yaml_emitter *emitter)
+{
+	return false;
+}
+
+static bool check_empty_sequence(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event;
+
+	if (emitter->num_events < 2)
+		return false;
+
+	event = next_event(emitter);
+	return (event->type == YAML_SEQUENCE_START_EVENT &&
+		list_next_entry(event, link)->type == YAML_SEQUENCE_END_EVENT);
+}
+
+static bool check_empty_mapping(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event;
+
+	if (emitter->num_events < 2)
+		return false;
+
+	event = next_event(emitter);
+	return (event->type == YAML_MAPPING_START_EVENT &&
+		list_next_entry(event, link)->type == YAML_MAPPING_END_EVENT);
+}
+
+static bool check_simple_key(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event = next_event(emitter);
+	size_t length = 0;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		length += emitter->anchor_data.anchor_length;
+		break;
+
+	case YAML_SCALAR_EVENT:
+		if (emitter->scalar_data.multiline)
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length +
+			emitter->scalar_data.length;
+		break;
+
+	case YAML_SEQUENCE_START_EVENT:
+		if (!check_empty_sequence(emitter))
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length;
+		break;
+
+	case YAML_MAPPING_START_EVENT:
+		if (!check_empty_mapping(emitter))
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length;
+		break;
+
+	default:
+		return false;
+	}
+
+	return length <= 128;
+}
+
+static int increase_indent(struct yaml_emitter *emitter,
+			   bool flow, bool indentless)
+{
+	int err;
+
+	err = PUSH(emitter, emitter->indents, emitter->indent);
+	if (err)
+		return err;
+
+	if (emitter->indent < 0)
+		emitter->indent = flow ? emitter->best_indent : 0;
+	else if (!indentless)
+		emitter->indent += emitter->best_indent;
+
+	return 0;
+}
+
+static int write_indent(struct yaml_emitter *emitter)
+{
+	int indent = (emitter->indent >= 0) ? emitter->indent : 0;
+	int err;
+
+	if (!emitter->indention ||
+	    emitter->column > indent ||
+	    (emitter->column == indent && !emitter->whitespace)) {
+		err = put_break(emitter);
+		if (err)
+			return err;
+	}
+
+	while (emitter->column < indent) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 1;
+	emitter->indention = 1;
+	return 0;
+}
+
+static int write_indicator(struct yaml_emitter *emitter,
+			   const char *indicator, int need_whitespace,
+			   int is_whitespace, int is_indention)
+{
+	YAML_STRING(string, indicator);
+	int err;
+
+	if (need_whitespace && !emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = is_whitespace;
+	emitter->indention = (emitter->indention && is_indention);
+	emitter->open_ended = 0;
+	return 0;
+}
+
+static int write_anchor(struct yaml_emitter *emitter,
+			const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag_handle(struct yaml_emitter *emitter,
+			    const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	if (!emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag_content(struct yaml_emitter *emitter,
+			     const char *value, size_t length,
+			     int need_whitespace)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	if (need_whitespace && !emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		if (IS_ALPHA(string) ||
+		    CHECK(string, ';') ||
+		    CHECK(string, '/') ||
+		    CHECK(string, '?') ||
+		    CHECK(string, ':') ||
+		    CHECK(string, '@') ||
+		    CHECK(string, '&') ||
+		    CHECK(string, '=') ||
+		    CHECK(string, '+') ||
+		    CHECK(string, '$') ||
+		    CHECK(string, ',') ||
+		    CHECK(string, '_') ||
+		    CHECK(string, '.') ||
+		    CHECK(string, '~') ||
+		    CHECK(string, '*') ||
+		    CHECK(string, '\'') ||
+		    CHECK(string, '(') ||
+		    CHECK(string, ')') ||
+		    CHECK(string, '[') ||
+		    CHECK(string, ']')) {
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+		} else {
+			int width = utf8_width(&string);
+
+			while (width--) {
+				unsigned int value = *string.pos++;
+				int c;
+
+				err = put(emitter, '%');
+				if (err)
+					return err;
+
+				c = value >> 4;
+				c += c < 10 ? '0' : 'A' - 10;
+				err = put(emitter, c);
+				if (err)
+					return err;
+
+				c = value & 0x0f;
+				c += c < 10 ? '0' : 'A' - 10;
+				err = put(emitter, c);
+				if (err)
+					return err;
+			}
+		}
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (!emitter->tag_data.handle && !emitter->tag_data.suffix)
+		return 0;
+
+	if (emitter->tag_data.handle) {
+		err = write_tag_handle(emitter,
+				       emitter->tag_data.handle,
+				       emitter->tag_data.handle_length);
+		if (err)
+			return err;
+
+		if (emitter->tag_data.suffix) {
+			err = write_tag_content(emitter,
+						emitter->tag_data.suffix,
+						emitter->tag_data.suffix_length,
+						0);
+			if (err)
+				return err;
+		}
+	} else {
+		err = write_indicator(emitter, "!<", 1, 0, 0);
+		if (err)
+			return err;
+
+		err = write_tag_content(emitter,
+					emitter->tag_data.suffix,
+					emitter->tag_data.suffix_length,
+					0);
+		if (err)
+			return err;
+
+		err = write_indicator(emitter, ">", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int write_plain_scalar(struct yaml_emitter *emitter,
+			      const char *value,
+			      size_t length,
+			      int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int breaks = 0;
+	int err;
+
+	if (!emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		if (IS_SPACE(string)) {
+			if (allow_breaks && !spaces &&
+			    emitter->column > emitter->best_width &&
+			    !IS_SPACE_AT(string, 1)) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else if (IS_BREAK(string)) {
+			if (!breaks && CHECK(string, '\n')) {
+				err = put_break(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			spaces = 0;
+			breaks = 0;
+		}
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	if (emitter->root_context)
+		emitter->open_ended = 1;
+	return 0;
+}
+
+static int
+write_single_quoted_scalar(struct yaml_emitter *emitter,
+			   const char *value,
+			   size_t length,
+			   int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int breaks = 0;
+	int err;
+
+	err = write_indicator(emitter, "'", 1, 0, 0);
+	if (err)
+		return err;
+
+	while (string.pos != string.end) {
+		if (IS_SPACE(string)) {
+			if (allow_breaks && !spaces &&
+			    emitter->column > emitter->best_width &&
+			    string.pos != string.start &&
+			    string.pos != string.end - 1 &&
+			    !IS_SPACE_AT(string, 1)) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else if (IS_BREAK(string)) {
+			if (!breaks && CHECK(string, '\n')) {
+				err = put_break(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			if (CHECK(string, '\'')) {
+				err = put(emitter, '\'');
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			spaces = 0;
+			breaks = 0;
+		}
+	}
+
+	if (breaks) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	err = write_indicator(emitter, "'", 0, 0, 0);
+	if (err)
+		return err;
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int
+write_double_quoted_scalar(struct yaml_emitter *emitter,
+			   const char *value, size_t length, int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int err;
+
+	err = write_indicator(emitter, "\"", 1, 0, 0);
+	if (err)
+		return err;
+
+	while (string.pos != string.end) {
+		if (!IS_PRINTABLE(string) ||
+		    (!emitter->unicode && !IS_ASCII(string)) ||
+		    IS_BOM(string) ||
+		    IS_BREAK(string) ||
+		    CHECK(string, '"') ||
+		    CHECK(string, '\\')) {
+			u8 octet = string.pos[0];
+			unsigned int width;
+			unsigned int value;
+			int k;
+
+			width = (octet & 0x80) == 0x00 ? 1 :
+				(octet & 0xE0) == 0xC0 ? 2 :
+				(octet & 0xF0) == 0xE0 ? 3 :
+				(octet & 0xF8) == 0xF0 ? 4 : 0;
+			value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+				(octet & 0xE0) == 0xC0 ? octet & 0x1F :
+				(octet & 0xF0) == 0xE0 ? octet & 0x0F :
+				(octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+			for (k = 1; k < (int)width; k++) {
+				octet = string.pos[k];
+				value = (value << 6) + (octet & 0x3F);
+			}
+			string.pos += width;
+
+			err = put(emitter, '\\');
+			if (err)
+				return err;
+
+			switch (value) {
+			case 0x00:
+				err = put(emitter, '0');
+				if (err)
+					return err;
+				break;
+
+			case 0x07:
+				err = put(emitter, 'a');
+				if (err)
+					return err;
+				break;
+
+			case 0x08:
+				err = put(emitter, 'b');
+				if (err)
+					return err;
+				break;
+
+			case 0x09:
+				err = put(emitter, 't');
+				if (err)
+					return err;
+				break;
+
+			case 0x0A:
+				err = put(emitter, 'n');
+				if (err)
+					return err;
+				break;
+
+			case 0x0B:
+				err = put(emitter, 'v');
+				if (err)
+					return err;
+				break;
+
+			case 0x0C:
+				err = put(emitter, 'f');
+				if (err)
+					return err;
+				break;
+
+			case 0x0D:
+				err = put(emitter, 'r');
+				if (err)
+					return err;
+				break;
+
+			case 0x1B:
+				err = put(emitter, 'e');
+				if (err)
+					return err;
+				break;
+
+			case 0x22:
+				err = put(emitter, '\"');
+				if (err)
+					return err;
+				break;
+
+			case 0x5C:
+				err = put(emitter, '\\');
+				if (err)
+					return err;
+				break;
+
+			case 0x85:
+				err = put(emitter, 'N');
+				if (err)
+					return err;
+				break;
+
+			case 0xA0:
+				err = put(emitter, '_');
+				if (err)
+					return err;
+				break;
+
+			case 0x2028:
+				err = put(emitter, 'L');
+				if (err)
+					return err;
+				break;
+
+			case 0x2029:
+				err = put(emitter, 'P');
+				if (err)
+					return err;
+				break;
+
+			default:
+				if (value <= 0xFF) {
+					err = put(emitter, 'x');
+					if (err)
+						return err;
+					width = 2;
+				} else if (value <= 0xFFFF) {
+					err = put(emitter, 'u');
+					if (err)
+						return err;
+					width = 4;
+				} else {
+					err = put(emitter, 'U');
+					if (err)
+						return err;
+					width = 8;
+				}
+
+				for (k = (width - 1) * 4; k >= 0; k -= 4) {
+					int digit;
+
+					digit = (value >> k) & 0x0F;
+					digit += digit < 10 ? '0' : 'A' - 10;
+					err = put(emitter, digit);
+					if (err)
+						return err;
+				}
+			}
+			spaces = 0;
+		} else if (IS_SPACE(string)) {
+			if (allow_breaks &&
+			    !spaces &&
+			    emitter->column > emitter->best_width &&
+			    string.pos != string.start &&
+			    string.pos != string.end - 1) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				if (IS_SPACE_AT(string, 1)) {
+					err = put(emitter, '\\');
+					if (err)
+						return err;
+				}
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else {
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+			spaces = 0;
+		}
+	}
+
+	err = write_indicator(emitter, "\"", 0, 0, 0);
+	if (err)
+		return err;
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int
+write_block_scalar_hints(struct yaml_emitter *emitter,
+			 struct yaml_string string)
+{
+	char indent_hint[2];
+	const char *chomp_hint = NULL;
+	int err;
+
+	if (IS_SPACE(string) || IS_BREAK(string)) {
+		indent_hint[0] = '0' + (char)emitter->best_indent;
+		indent_hint[1] = '\0';
+		err = write_indicator(emitter, indent_hint, 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	emitter->open_ended = 0;
+
+	string.pos = string.end;
+	if (string.start == string.pos) {
+		chomp_hint = "-";
+	} else {
+		do {
+			string.pos--;
+		} while ((*string.pos & 0xC0) == 0x80);
+
+		if (!IS_BREAK(string)) {
+			chomp_hint = "-";
+		} else if (string.start == string.pos) {
+			chomp_hint = "+";
+			emitter->open_ended = 1;
+		} else {
+			do {
+				string.pos--;
+			} while ((*string.pos & 0xC0) == 0x80);
+
+			if (IS_BREAK(string)) {
+				chomp_hint = "+";
+				emitter->open_ended = 1;
+			}
+		}
+	}
+
+	if (chomp_hint) {
+		err = write_indicator(emitter, chomp_hint, 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int
+write_literal_scalar(struct yaml_emitter *emitter,
+		     const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int breaks = 1;
+	int err;
+
+	err = write_indicator(emitter, "|", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = write_block_scalar_hints(emitter, string);
+	if (err)
+		return err;
+
+	err = put_break(emitter);
+	if (err)
+		return err;
+
+	emitter->indention = 1;
+	emitter->whitespace = 1;
+
+	while (string.pos != string.end) {
+		if (IS_BREAK(string)) {
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			breaks = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int
+write_folded_scalar(struct yaml_emitter *emitter,
+		    const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int breaks = 1;
+	int leading_spaces = 1;
+	int err;
+
+	err = write_indicator(emitter, ">", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = write_block_scalar_hints(emitter, string);
+	if (err)
+		return err;
+
+	err = put_break(emitter);
+	if (err)
+		return err;
+
+	emitter->indention = 1;
+	emitter->whitespace = 1;
+
+	while (string.pos != string.end) {
+		if (IS_BREAK(string)) {
+			if (!breaks && !leading_spaces && CHECK(string, '\n')) {
+				int k;
+
+				k = 0;
+				while (IS_BREAK_AT(string, k))
+					k += __utf8_width(&string, k);
+
+				if (!IS_BLANKZ_AT(string, k)) {
+					err = put_break(emitter);
+					if (err)
+						return err;
+				}
+			}
+
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				leading_spaces = IS_BLANK(string);
+			}
+
+			if (!breaks &&
+			    IS_SPACE(string) &&
+			    !IS_SPACE_AT(string, 1) &&
+			    emitter->column > emitter->best_width) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			emitter->indention = 0;
+			breaks = 0;
+		}
+	}
+
+	return err;
+}
+
+static int
+write_scalar(struct yaml_emitter *emitter)
+{
+	switch (emitter->scalar_data.style) {
+	case YAML_PLAIN_SCALAR_STYLE:
+		return write_plain_scalar(emitter,
+					  emitter->scalar_data.value,
+					  emitter->scalar_data.length,
+					  !emitter->simple_key_context);
+
+	case YAML_SINGLE_QUOTED_SCALAR_STYLE:
+		return write_single_quoted_scalar(emitter,
+						  emitter->scalar_data.value,
+						  emitter->scalar_data.length,
+						  !emitter->simple_key_context);
+
+	case YAML_DOUBLE_QUOTED_SCALAR_STYLE:
+		return write_double_quoted_scalar(emitter,
+						  emitter->scalar_data.value,
+						  emitter->scalar_data.length,
+						  !emitter->simple_key_context);
+
+	case YAML_LITERAL_SCALAR_STYLE:
+		return write_literal_scalar(emitter,
+					    emitter->scalar_data.value,
+					    emitter->scalar_data.length);
+
+	case YAML_FOLDED_SCALAR_STYLE:
+		return write_folded_scalar(emitter,
+					   emitter->scalar_data.value,
+					   emitter->scalar_data.length);
+
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+static int emit_anchor(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (!emitter->anchor_data.anchor)
+		return 0;
+
+	err = write_indicator(emitter,
+			      emitter->anchor_data.alias ? "*" : "&",
+			      1, 0, 0);
+	if (err)
+		return err;
+
+	return write_anchor(emitter,
+			    emitter->anchor_data.anchor,
+			    emitter->anchor_data.anchor_length);
+}
+
+static int emit_stream_start(struct yaml_emitter *emitter,
+			     const struct yaml_event *event)
+{
+	if (event->type == YAML_STREAM_START_EVENT) {
+		if (emitter->best_indent < 2 || emitter->best_indent > 9)
+			emitter->best_indent  = 2;
+
+		if (emitter->best_width >= 0 &&
+		    emitter->best_width <= emitter->best_indent * 2)
+			emitter->best_width = 80;
+
+		if (emitter->best_width < 0)
+			emitter->best_width = INT_MAX;
+
+		emitter->indent = -1;
+
+		emitter->column = 0;
+		emitter->whitespace = 1;
+		emitter->indention = 1;
+
+		emitter->state = EMIT_FIRST_DOCUMENT_START_STATE;
+		return 0;
+	}
+
+	return set_error(emitter, "expected STREAM-START");
+}
+
+static int
+append_tag_directive(struct yaml_emitter *emitter,
+		     struct yaml_tag_directive value,
+		     int allow_duplicates)
+{
+	struct yaml_tag_directive *tag;
+	struct yaml_tag_directive copy = {};
+	int err;
+
+	for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) {
+		if (strcmp(value.handle, tag->handle) == 0) {
+			if (allow_duplicates)
+				return 0;
+
+			return set_error(emitter, "duplicate %TAG directive");
+		}
+	}
+
+	copy.handle = kstrdup(value.handle, GFP_KERNEL);
+	copy.prefix = kstrdup(value.prefix, GFP_KERNEL);
+	if (!copy.handle || !copy.prefix) {
+		emitter->errno = -ENOMEM;
+		err = -ENOMEM;
+		goto error;
+	}
+
+	err = PUSH(emitter, emitter->tags, copy);
+	if (err)
+		goto error;
+
+	return 0;
+
+error:
+	kfree(copy.handle);
+	kfree(copy.prefix);
+	return err;
+}
+
+static int
+emit_document_start(struct yaml_emitter *emitter,
+		    const struct yaml_event *event,
+		    int first)
+{
+	int err;
+
+	if (event->type == YAML_DOCUMENT_START_EVENT) {
+		struct yaml_tag_directive default_tag_directives[] = {
+			{ "!", "!" },
+			{ "!!", "tag:yaml.org,2002:" },
+			{ NULL, NULL }
+		};
+		struct yaml_tag_directive *tag;
+		int implicit;
+
+		if (event->document_start.version) {
+			err = analyze_version_directive(emitter,
+							*event->document_start.version);
+			if (err)
+				return err;
+		}
+
+		for (tag = event->document_start.tags.start;
+		     tag != event->document_start.tags.end;
+		     tag++) {
+			err = analyze_tag_directive(emitter, *tag);
+			if (err)
+				return err;
+
+			err = append_tag_directive(emitter, *tag, 0);
+			if (err)
+				return err;
+		}
+
+		for (tag = default_tag_directives;
+		     tag->handle; tag++) {
+			err = append_tag_directive(emitter, *tag, 1);
+			if (err)
+				return err;
+		}
+
+		implicit = event->document_start.implicit;
+		if (!first || emitter->canonical)
+			implicit = 0;
+
+		if ((event->document_start.version ||
+		     event->document_start.tags.start
+		     != event->document_start.tags.end) &&
+		    emitter->open_ended) {
+			err = write_indicator(emitter, "...", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		if (event->document_start.version) {
+			implicit = 0;
+			err = write_indicator(emitter, "%YAML", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indicator(emitter, "1.1", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		if (event->document_start.tags.start !=
+		    event->document_start.tags.end) {
+			implicit = 0;
+			for (tag = event->document_start.tags.start;
+			     tag != event->document_start.tags.end;
+			     tag++) {
+				err = write_indicator(emitter, "%TAG", 1, 0, 0);
+				if (err)
+					return err;
+
+				err = write_tag_handle(emitter,
+						       tag->handle,
+						       strlen(tag->handle));
+				if (err)
+					return err;
+
+				err = write_tag_content(emitter,
+							tag->prefix,
+							strlen(tag->prefix),
+							1);
+				if (err)
+					return err;
+
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+		}
+
+		if (check_empty_document(emitter))
+			implicit = 0;
+
+		if (!implicit) {
+			err = write_indent(emitter);
+			if (err)
+				return err;
+
+			err = write_indicator(emitter, "---", 1, 0, 0);
+			if (err)
+				return err;
+
+			if (emitter->canonical) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+		}
+
+		emitter->state = EMIT_DOCUMENT_CONTENT_STATE;
+		return 0;
+	}
+
+	if (event->type == YAML_STREAM_END_EVENT) {
+		err = flush(emitter);
+		if (err)
+			return err;
+
+		emitter->state = EMIT_END_STATE;
+		return 0;
+	}
+
+	return set_error(emitter, "expected DOCUMENT-START or STREAM-END");
+}
+
+static int
+emit_document_end(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	if (event->type == YAML_DOCUMENT_END_EVENT) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+
+		if (!event->document_end.implicit) {
+			err = write_indicator(emitter, "...", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = flush(emitter);
+		if (err)
+			return err;
+
+		emitter->state = EMIT_DOCUMENT_START_STATE;
+
+		while (!EMPTY(emitter, emitter->tags)) {
+			struct yaml_tag_directive tag =
+				POP(emitter, emitter->tags);
+
+			kfree(tag.handle);
+			kfree(tag.prefix);
+		}
+
+		return 0;
+	}
+
+	return set_error(emitter, "expected DOCUMENT-END");
+}
+
+static int
+emit_alias(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	emitter->state = POP(emitter, emitter->states);
+	return 0;
+}
+
+static int
+select_scalar_style(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	enum yaml_scalar_style style = event->scalar.style;
+	bool no_tag = !emitter->tag_data.handle && !emitter->tag_data.suffix;
+
+	if (no_tag &&
+	    !event->scalar.plain_implicit &&
+	    !event->scalar.quoted_implicit)
+		return set_error(emitter,
+				 "neither tag nor implicit flags are specified");
+
+	if (style == YAML_ANY_SCALAR_STYLE)
+		style = YAML_PLAIN_SCALAR_STYLE;
+
+	if (emitter->canonical)
+		style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+	if (emitter->simple_key_context && emitter->scalar_data.multiline)
+		style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+	if (style == YAML_PLAIN_SCALAR_STYLE) {
+		if ((emitter->flow_level && !emitter->scalar_data.flow_plain_allowed) ||
+		    (!emitter->flow_level && !emitter->scalar_data.block_plain_allowed))
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+
+		if (!emitter->scalar_data.length &&
+		    (emitter->flow_level || emitter->simple_key_context))
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+
+		if (no_tag && !event->scalar.plain_implicit)
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (style == YAML_SINGLE_QUOTED_SCALAR_STYLE) {
+		if (!emitter->scalar_data.single_quoted_allowed)
+			style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (style == YAML_LITERAL_SCALAR_STYLE ||
+	    style == YAML_FOLDED_SCALAR_STYLE) {
+		if (!emitter->scalar_data.block_allowed ||
+		    emitter->flow_level ||
+		    emitter->simple_key_context)
+			style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (no_tag && !event->scalar.quoted_implicit &&
+	    style != YAML_PLAIN_SCALAR_STYLE) {
+		emitter->tag_data.handle = "!";
+		emitter->tag_data.handle_length = 1;
+	}
+
+	emitter->scalar_data.style = style;
+	return 0;
+}
+
+static int
+emit_scalar(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = select_scalar_style(emitter, event);
+	if (err)
+		return err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	err = increase_indent(emitter, 1, 0);
+	if (err)
+		return err;
+
+	err = write_scalar(emitter);
+	if (err)
+		return err;
+
+	emitter->indent = POP(emitter, emitter->indents);
+	emitter->state = POP(emitter, emitter->states);
+	return 0;
+}
+
+static int
+emit_sequence_start(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	if (emitter->flow_level || emitter->canonical ||
+	    event->sequence_start.style == YAML_FLOW_SEQUENCE_STYLE ||
+	    check_empty_sequence(emitter)) {
+		emitter->state = EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE;
+	} else {
+		emitter->state = EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE;
+	}
+
+	return 0;
+}
+
+static int
+emit_mapping_start(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	if (emitter->flow_level ||
+	    emitter->canonical ||
+	    event->mapping_start.style == YAML_FLOW_MAPPING_STYLE ||
+	    check_empty_mapping(emitter)) {
+		emitter->state = EMIT_FLOW_MAPPING_FIRST_KEY_STATE;
+	} else {
+		emitter->state = EMIT_BLOCK_MAPPING_FIRST_KEY_STATE;
+	}
+
+	return 0;
+}
+
+static int
+emit_node(struct yaml_emitter *emitter, const struct yaml_event *event,
+	  int root, int sequence, int mapping, int simple_key)
+{
+	emitter->root_context = root;
+	emitter->sequence_context = sequence;
+	emitter->mapping_context = mapping;
+	emitter->simple_key_context = simple_key;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		return emit_alias(emitter, event);
+
+	case YAML_SCALAR_EVENT:
+		return emit_scalar(emitter, event);
+
+	case YAML_SEQUENCE_START_EVENT:
+		return emit_sequence_start(emitter, event);
+
+	case YAML_MAPPING_START_EVENT:
+		return emit_mapping_start(emitter, event);
+
+	default:
+		return set_error(emitter,
+				 "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS");
+	}
+
+	return 0;
+}
+
+static int
+emit_document_content(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = PUSH(emitter, emitter->states, EMIT_DOCUMENT_END_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 1, 0, 0, 0);
+}
+
+static int
+emit_flow_sequence_item(struct yaml_emitter *emitter,
+			const struct yaml_event *event,
+			bool first)
+{
+	int err;
+
+	if (first) {
+		err = write_indicator(emitter, "[", 1, 1, 0);
+		if (err)
+			return err;
+
+		err = increase_indent(emitter, 1, 0);
+		if (err)
+			return err;
+
+		emitter->flow_level++;
+	}
+
+	if (event->type == YAML_SEQUENCE_END_EVENT) {
+		emitter->flow_level--;
+		emitter->indent = POP(emitter, emitter->indents);
+
+		if (emitter->canonical && !first) {
+			err = write_indicator(emitter, ",", 0, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, "]", 0, 0, 0);
+		if (err)
+			return err;
+
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	if (!first) {
+		err = write_indicator(emitter, ",", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (emitter->canonical || emitter->column > emitter->best_width) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_SEQUENCE_ITEM_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_flow_mapping_key(struct yaml_emitter *emitter,
+		      const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = write_indicator(emitter, "{", 1, 1, 0);
+		if (err)
+			return err;
+
+		err = increase_indent(emitter, 1, 0);
+		if (err)
+			return err;
+
+		emitter->flow_level++;
+	}
+
+	if (event->type == YAML_MAPPING_END_EVENT) {
+		emitter->flow_level--;
+		emitter->indent = POP(emitter, emitter->indents);
+
+		if (emitter->canonical && !first) {
+			err = write_indicator(emitter, ",", 0, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, "}", 0, 0, 0);
+		if (err)
+			return err;
+
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	if (!first) {
+		err = write_indicator(emitter, ",", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (emitter->canonical || emitter->column > emitter->best_width) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	if (!emitter->canonical && check_simple_key(emitter)) {
+		err = PUSH(emitter, emitter->states,
+			   EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 1);
+	}
+
+	err = write_indicator(emitter, "?", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_VALUE_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emit_flow_mapping_value(struct yaml_emitter *emitter,
+			const struct yaml_event *event, int simple)
+{
+	int err;
+
+	if (simple) {
+		err = write_indicator(emitter, ":", 0, 0, 0);
+		if (err)
+			return err;
+	} else {
+		if (emitter->canonical ||
+		    emitter->column > emitter->best_width) {
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, ":", 1, 0, 0);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_KEY_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emit_block_sequence_item(struct yaml_emitter *emitter,
+			 const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = increase_indent(emitter, 0,
+				      emitter->mapping_context &&
+				      !emitter->indention);
+		if (err)
+			return err;
+	}
+
+	if (event->type == YAML_SEQUENCE_END_EVENT) {
+		emitter->indent = POP(emitter, emitter->indents);
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	err = write_indent(emitter);
+	if (err)
+		return err;
+
+	err = write_indicator(emitter, "-", 1, 0, 1);
+	if (err)
+		return err;
+
+	err = PUSH(emitter, emitter->states, EMIT_BLOCK_SEQUENCE_ITEM_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_block_mapping_key(struct yaml_emitter *emitter,
+		       const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = increase_indent(emitter, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (event->type == YAML_MAPPING_END_EVENT) {
+		emitter->indent = POP(emitter, emitter->indents);
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	err = write_indent(emitter);
+	if (err)
+		return err;
+
+	if (check_simple_key(emitter)) {
+		err = PUSH(emitter, emitter->states,
+			   EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 1);
+	} else {
+		err = write_indicator(emitter, "?", 1, 0, 1);
+		if (err)
+			return err;
+
+		err = PUSH(emitter, emitter->states,
+			   EMIT_BLOCK_MAPPING_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 0);
+	}
+}
+
+static int
+emit_block_mapping_value(struct yaml_emitter *emitter,
+			 const struct yaml_event *event, int simple)
+{
+	int err;
+
+	if (simple) {
+		err = write_indicator(emitter, ":", 0, 0, 0);
+		if (err)
+			return err;
+	} else {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+
+		err = write_indicator(emitter, ":", 1, 0, 1);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_BLOCK_MAPPING_KEY_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emitter_state_machine(struct yaml_emitter *emitter,
+		      const struct yaml_event *event)
+{
+	switch (emitter->state) {
+	case EMIT_STREAM_START_STATE:
+		return emit_stream_start(emitter, event);
+
+	case EMIT_FIRST_DOCUMENT_START_STATE:
+		return emit_document_start(emitter, event, true);
+
+	case EMIT_DOCUMENT_START_STATE:
+		return emit_document_start(emitter, event, false);
+
+	case EMIT_DOCUMENT_CONTENT_STATE:
+		return emit_document_content(emitter, event);
+
+	case EMIT_DOCUMENT_END_STATE:
+		return emit_document_end(emitter, event);
+
+	case EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
+		return emit_flow_sequence_item(emitter, event, true);
+
+	case EMIT_FLOW_SEQUENCE_ITEM_STATE:
+		return emit_flow_sequence_item(emitter, event, false);
+
+	case EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
+		return emit_flow_mapping_key(emitter, event, true);
+
+	case EMIT_FLOW_MAPPING_KEY_STATE:
+		return emit_flow_mapping_key(emitter, event, false);
+
+	case EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
+		return emit_flow_mapping_value(emitter, event, true);
+
+	case EMIT_FLOW_MAPPING_VALUE_STATE:
+		return emit_flow_mapping_value(emitter, event, false);
+
+	case EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
+		return emit_block_sequence_item(emitter, event, true);
+
+	case EMIT_BLOCK_SEQUENCE_ITEM_STATE:
+		return emit_block_sequence_item(emitter, event, false);
+
+	case EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
+		return emit_block_mapping_key(emitter, event, true);
+
+	case EMIT_BLOCK_MAPPING_KEY_STATE:
+		return emit_block_mapping_key(emitter, event, false);
+
+	case EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
+		return emit_block_mapping_value(emitter, event, true);
+
+	case EMIT_BLOCK_MAPPING_VALUE_STATE:
+		return emit_block_mapping_value(emitter, event, false);
+
+	case EMIT_END_STATE:
+		return set_error(emitter, "expected nothing after STREAM-END");
+
+	default:
+		BUG();
+	}
+
+	return 0;
+}
+
+void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event)
+{
+	if (!event || !emitter || emitter->errno) {
+		yaml_event_delete(event);
+		return;
+	}
+
+	list_add_tail(&event->link, &emitter->events);
+	emitter->num_events++;
+
+	while (!need_more_events(emitter)) {
+		event = next_event(emitter);
+
+		if (analyze_event(emitter, event))
+			return;
+
+		if (emitter_state_machine(emitter, event))
+			return;
+
+		emitter->num_events--;
+		list_del(&event->link);
+		yaml_event_delete(event);
+	}
+}
+EXPORT_SYMBOL(yaml_emitter_emit);
+
+int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem)
+{
+	int err;
+
+	if (!emitter) {
+		if (problem)
+			*problem = "allocation error";
+		return -ENOMEM;
+	}
+
+	if (emitter->errno)
+		err = emitter->errno;
+	else
+		err = flush(emitter);
+	if (!err)
+		return 0;
+
+	if (problem)
+		*problem = emitter->problem;
+	return err;
+}
+EXPORT_SYMBOL(yaml_emitter_flush);
+
+static bool yaml_stack_extend(void **start, void **top, void **end)
+{
+	void *new_start;
+
+	if (*end - *start >= INT_MAX / 2)
+		return false;
+
+	new_start = krealloc(*start, (*end - *start) * 2, GFP_KERNEL);
+	if (!new_start)
+		return false;
+
+	*top = new_start + (*top - *start);
+	*end = new_start + (*end - *start) * 2;
+	*start = new_start;
+
+	return true;
+}
+
+#define STACK_INIT(context, stack)                                     \
+	(((stack).start = kmalloc(INITIAL_STACK_SIZE * sizeof(*(stack). start),\
+				  gfp)) ?			\
+	((stack).top = (stack).start,					\
+	(stack).end = (stack).start + INITIAL_STACK_SIZE,		\
+	1) : 0)
+#define STACK_DEL(context, stack) kfree((stack).start)
+
+struct yaml_emitter *yaml_emitter_create(gfp_t gfp)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = kzalloc(sizeof(*emitter), gfp);
+	if (!emitter)
+		return NULL;
+
+	emitter->gfp = gfp;
+	INIT_LIST_HEAD(&emitter->events);
+
+	emitter->buf.start = kmalloc(PAGE_SIZE, gfp);
+	if (!emitter->buf.start)
+		goto error;
+
+	emitter->buf.pos = emitter->buf.start;
+	emitter->buf.last = emitter->buf.pos;
+	emitter->buf.end = emitter->buf.start + PAGE_SIZE;
+
+	if (!STACK_INIT(emitter, emitter->states))
+		goto error;
+
+	if (!STACK_INIT(emitter, emitter->indents))
+		goto error;
+
+	if (!STACK_INIT(emitter, emitter->tags))
+		goto error;
+
+	return emitter;
+
+error:
+	yaml_emitter_destroy(emitter);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_emitter_create);
+
+gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp)
+{
+	gfp_t old = 0;
+
+	if (emitter) {
+		old = emitter->gfp;
+		emitter->gfp = gfp;
+	}
+
+	return old;
+}
+EXPORT_SYMBOL(yaml_emitter_set_gfp);
+
+static void delete_events(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event, *next;
+
+	list_for_each_entry_safe(event, next, &emitter->events, link)
+		yaml_event_delete(event);
+}
+
+void yaml_emitter_destroy(struct yaml_emitter *emitter)
+{
+	if (!emitter)
+		return;
+
+	delete_events(emitter);
+
+	kfree(emitter->buf.start);
+	STACK_DEL(emitter, emitter->states);
+	STACK_DEL(emitter, emitter->indents);
+	while (!EMPTY(empty, emitter->tags)) {
+		struct yaml_tag_directive tag =
+			POP(emitter, emitter->tags);
+
+		kfree(tag.handle);
+		kfree(tag.prefix);
+	}
+	STACK_DEL(emitter, emitter->tags);
+	kfree(emitter->anchors);
+
+	kfree(emitter);
+}
+EXPORT_SYMBOL(yaml_emitter_destroy);
+
+static int string_write_handler(void *data, const u8 *buffer, size_t size)
+{
+	struct yaml_emitter *emitter = data;
+	typeof(emitter->output.string) *out = &emitter->output.string;
+
+	if (out->size - *out->size_written < size)
+		size = out->size - *out->size_written;
+
+	memcpy(out->buffer + *out->size_written, buffer, size);
+	*out->size_written += size;
+	return 0;
+}
+
+void yaml_emitter_set_string(struct yaml_emitter *emitter,
+			     u8 *output,
+			     size_t size,
+			     size_t *size_written)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = string_write_handler;
+	emitter->write_data = emitter;
+
+	emitter->output.string.buffer = output;
+	emitter->output.string.size = size;
+	emitter->output.string.size_written = size_written;
+	*size_written = 0;
+}
+EXPORT_SYMBOL(yaml_emitter_set_string);
+
+static int seq_file_write_handler(void *data, const u8 *buffer, size_t size)
+{
+	/* Ignore the overflow error; seq will restart with a larger buffer */
+	seq_write(data, buffer, size);
+	return 0;
+}
+
+void yaml_emitter_set_seq_file(struct yaml_emitter *emitter,
+			       struct seq_file *file)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = seq_file_write_handler;
+	emitter->write_data = file;
+}
+EXPORT_SYMBOL(yaml_emitter_set_seq_file);
+
+void yaml_emitter_set_width(struct yaml_emitter *emitter, int width)
+{
+	if (!emitter)
+		return;
+
+	emitter->best_width = (width >= 0) ? width : -1;
+}
+EXPORT_SYMBOL(yaml_emitter_set_width);
+
+void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode)
+{
+	if (!emitter)
+		return;
+
+	emitter->unicode = unicode;
+}
+EXPORT_SYMBOL(yaml_emitter_set_unicode);
+
+void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent)
+{
+	if (!emitter)
+		return;
+
+	emitter->best_indent = (1 < indent && indent < 10) ? indent : 2;
+}
+EXPORT_SYMBOL(yaml_emitter_set_indent);
+
+void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical)
+{
+	if (!emitter)
+		return;
+
+	emitter->canonical = canonical;
+}
+EXPORT_SYMBOL(yaml_emitter_set_canonical);
+
+void yaml_emitter_set_output(struct yaml_emitter *emitter,
+			     yaml_write_t handler,
+			     void *data)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = handler;
+	emitter->write_data = data;
+}
+EXPORT_SYMBOL(yaml_emitter_set_output);
diff --git a/lib/yaml/yaml-emitter.h b/lib/yaml/yaml-emitter.h
new file mode 100644
index 000000000000..a8708bc63f29
--- /dev/null
+++ b/lib/yaml/yaml-emitter.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef YAML_EMITTER_H
+#define YAML_EMITTER_H
+
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/yaml.h>
+
+enum yaml_emitter_state {
+	EMIT_STREAM_START_STATE,
+	EMIT_FIRST_DOCUMENT_START_STATE,
+	EMIT_DOCUMENT_START_STATE,
+	EMIT_DOCUMENT_CONTENT_STATE,
+	EMIT_DOCUMENT_END_STATE,
+	EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE,
+	EMIT_FLOW_SEQUENCE_ITEM_STATE,
+	EMIT_FLOW_MAPPING_FIRST_KEY_STATE,
+	EMIT_FLOW_MAPPING_KEY_STATE,
+	EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE,
+	EMIT_FLOW_MAPPING_VALUE_STATE,
+	EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE,
+	EMIT_BLOCK_SEQUENCE_ITEM_STATE,
+	EMIT_BLOCK_MAPPING_FIRST_KEY_STATE,
+	EMIT_BLOCK_MAPPING_KEY_STATE,
+	EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE,
+	EMIT_BLOCK_MAPPING_VALUE_STATE,
+	EMIT_END_STATE
+};
+
+struct yaml_anchors {
+	int references;
+	int anchor;
+	int serialized;
+};
+
+struct yaml_emitter {
+	int errno;
+	const char *problem;
+	gfp_t gfp;
+
+	yaml_write_t write_handler;
+	void *write_data;
+
+	union {
+		struct {
+			u8 *buffer;
+			size_t size;
+			size_t *size_written;
+		} string;
+	} output;
+
+	struct {
+		char *start;
+		char *end;
+		char *pos;
+		char *last;
+	} buf;
+
+	int canonical;
+	int best_indent;
+	int best_width;
+	int unicode;
+
+	struct {
+		enum yaml_emitter_state *start;
+		enum yaml_emitter_state *end;
+		enum yaml_emitter_state *top;
+	} states;
+
+	enum yaml_emitter_state state;
+
+	struct list_head events;
+	int num_events;
+
+	struct {
+		int *start;
+		int *end;
+		int *top;
+	} indents;
+
+	struct {
+		struct yaml_tag_directive *start;
+		struct yaml_tag_directive *end;
+		struct yaml_tag_directive *top;
+	} tags;
+
+	int indent;
+	int flow_level;
+
+	int root_context;
+	int sequence_context;
+	int mapping_context;
+	int simple_key_context;
+
+	int column;
+	int whitespace;
+	int indention;
+	int open_ended;
+
+	/** Anchor analysis. */
+	struct {
+		const char *anchor;
+		size_t anchor_length;
+		int alias;
+	} anchor_data;
+
+	/** Tag analysis. */
+	struct {
+		const char *handle;
+		size_t handle_length;
+		const char *suffix;
+		size_t suffix_length;
+	} tag_data;
+
+	/** Scalar analysis. */
+	struct {
+		const char *value;
+		size_t length;
+		int multiline;
+		int flow_plain_allowed;
+		int block_plain_allowed;
+		int single_quoted_allowed;
+		int block_allowed;
+		enum yaml_scalar_style style;
+	} scalar_data;
+
+	int opened;
+	int closed;
+
+	struct yaml_anchors *anchors;
+	int last_anchor_id;
+};
+
+#endif /* YAML_EMITTER_H */
diff --git a/lib/yaml/yaml-events.c b/lib/yaml/yaml-events.c
new file mode 100644
index 000000000000..adc7ed887d6a
--- /dev/null
+++ b/lib/yaml/yaml-events.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+static bool check_utf8(const char *pos, size_t length)
+{
+	const char *end = pos + length;
+
+	while (pos < end) {
+		u8 octet = pos[0];
+		unsigned int width;
+		unsigned int value;
+		size_t k;
+
+		width = (octet & 0x80) == 0x00 ? 1 :
+			(octet & 0xE0) == 0xC0 ? 2 :
+			(octet & 0xF0) == 0xE0 ? 3 :
+			(octet & 0xF8) == 0xF0 ? 4 : 0;
+		value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+			(octet & 0xE0) == 0xC0 ? octet & 0x1F :
+			(octet & 0xF0) == 0xE0 ? octet & 0x0F :
+			(octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+		if (!width)
+			return false;
+
+		if (pos + width > end)
+			return false;
+
+		for (k = 1; k < width; k++) {
+			octet = pos[k];
+			if ((octet & 0xC0) != 0x80)
+				return false;
+
+			value = (value << 6) + (octet & 0x3F);
+		}
+
+		if (!(width == 1 ||
+		      (width == 2 && value >= 0x80) ||
+		      (width == 3 && value >= 0x800) ||
+		      (width == 4 && value >= 0x10000)))
+			return false;
+
+		pos += width;
+	}
+
+	return true;
+}
+
+static struct yaml_event *yaml_event_create(int type, gfp_t gfp)
+{
+	struct yaml_event *ev;
+
+	ev = kzalloc(sizeof(*ev), gfp);
+	if (ev)
+		ev->type = type;
+
+	return ev;
+}
+
+static void yaml_event_free(struct yaml_event *event)
+{
+	kfree(event);
+}
+
+struct yaml_event *yaml_stream_start_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_STREAM_START_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_stream_start_event_create);
+
+struct yaml_event *yaml_stream_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_STREAM_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_stream_end_event_create);
+
+struct yaml_event *
+yaml_document_start_event_create(struct yaml_version_directive *version,
+				 struct yaml_tag_directive *start,
+				 struct yaml_tag_directive *end,
+				 int implicit,
+				 gfp_t gfp)
+{
+	struct yaml_version_directive *version_copy = NULL;
+	struct {
+		struct yaml_tag_directive *start;
+		struct yaml_tag_directive *end;
+		struct yaml_tag_directive *top;
+	} copy = {};
+	struct yaml_tag_directive value = {};
+	struct yaml_event *event;
+
+	if (!((start && end) || start == end))
+		return NULL;
+
+	event = yaml_event_create(YAML_DOCUMENT_START_EVENT, gfp);
+	if (!event)
+		return NULL;
+
+	if (version) {
+		version_copy = kmemdup(version, sizeof(*version), gfp);
+		if (!version_copy)
+			goto error;
+	}
+
+#if 0
+	if (start != end) {
+		struct yaml_tag_directive *tag;
+		struct {
+			enum yaml_error error;
+		} context;
+
+		if (!STACK_INIT(&context, copy, struct yaml_tag_directive*))
+			goto error;
+
+		for (tag = start; tag != end; tag++) {
+			if (!check_utf8(tag->handle, strlen(tag->handle)))
+				goto error;
+
+			if (!check_utf8(tag->prefix, strlen(tag->prefix)))
+				goto error;
+
+			value.handle = kstrdup(tag->handle, gfp);
+			value.prefix = kstrdup(tag->prefix, gfp);
+			if (!value.handle || !value.prefix)
+				goto error;
+
+			if (!PUSH(&context, copy, value))
+				goto error;
+
+			value.handle = NULL;
+			value.prefix = NULL;
+		}
+	}
+#endif
+
+	event->type = YAML_DOCUMENT_START_EVENT;
+	event->document_start.version = version_copy;
+	event->document_start.tags.start = copy.start;
+	event->document_start.tags.end = copy.top;
+	event->document_start.implicit = implicit;
+
+	return event;
+
+error:
+	kfree(version_copy);
+#if 0
+	while (!STACK_EMPTY(context, copy)) {
+		struct yaml_tag_directive value = POP(context, copy);
+
+		kfree(value.handle);
+		kfree(value.prefix);
+	}
+	STACK_DEL(context, copy);
+#endif
+	kfree(value.handle);
+	kfree(value.prefix);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_document_start_event_create);
+
+struct yaml_event *
+yaml_document_end_event_create(int implicit, gfp_t gfp)
+{
+	struct yaml_event *event;
+
+	event = yaml_event_create(YAML_DOCUMENT_END_EVENT, gfp);
+	if (event)
+		event->document_end.implicit = implicit;
+
+	return event;
+}
+EXPORT_SYMBOL(yaml_document_end_event_create);
+
+struct yaml_event *
+yaml_alias_event_create(const char *anchor, gfp_t gfp)
+{
+	struct yaml_event *event;
+
+	if (!anchor || !check_utf8(anchor, strlen(anchor)))
+		return NULL;
+
+	event = yaml_event_create(YAML_ALIAS_EVENT, gfp);
+	if (!event)
+		return NULL;
+
+	event->alias.anchor = kstrdup(anchor, gfp);
+	if (!event->alias.anchor) {
+		yaml_event_free(event);
+		return NULL;
+	}
+
+	return event;
+}
+EXPORT_SYMBOL(yaml_alias_event_create);
+
+struct yaml_event *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+			 const char *value, ssize_t length, bool steal,
+			 int plain_implicit, int quoted_implicit,
+			 enum yaml_scalar_style style,
+			 gfp_t gfp)
+{
+	struct yaml_event *event;
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+
+	if (!value)
+		return NULL;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	if (length < 0)
+		length = strlen(value);
+
+	if (!check_utf8(value, length))
+		goto error;
+
+	event = yaml_event_create(YAML_SCALAR_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	if (steal) {
+		event->scalar.value = (char *)value;
+	} else {
+		event->scalar.value = kmemdup(value, length, gfp);
+		if (!event->scalar.value) {
+			yaml_event_free(event);
+			goto error;
+		}
+	}
+	event->scalar.length = length;
+
+	event->scalar.anchor = anchor_copy;
+	event->scalar.tag = tag_copy;
+	event->scalar.plain_implicit = plain_implicit;
+	event->scalar.quoted_implicit = quoted_implicit;
+	event->scalar.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_scalar_event_create);
+
+struct yaml_event *
+yaml_sequence_start_event_create(const char *anchor,
+				 const char *tag,
+				 int implicit,
+				 enum yaml_sequence_style style,
+				 gfp_t gfp)
+{
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+	struct yaml_event *event;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	event = yaml_event_create(YAML_SEQUENCE_START_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	event->sequence_start.anchor = anchor_copy;
+	event->sequence_start.tag = tag_copy;
+	event->sequence_start.implicit = implicit;
+	event->sequence_start.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_sequence_start_event_create);
+
+struct yaml_event *yaml_sequence_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_SEQUENCE_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_sequence_end_event_create);
+
+struct yaml_event *
+yaml_mapping_start_event_create(const char *anchor,
+				const char *tag,
+				int implicit,
+				enum yaml_mapping_style style,
+				gfp_t gfp)
+{
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+	struct yaml_event *event;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	event = yaml_event_create(YAML_MAPPING_START_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	event->mapping_start.anchor = anchor_copy;
+	event->mapping_start.tag = tag_copy;
+	event->mapping_start.implicit = implicit;
+	event->mapping_start.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_mapping_start_event_create);
+
+struct yaml_event *yaml_mapping_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_MAPPING_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_mapping_end_event_create);
+
+void yaml_event_delete(struct yaml_event *event)
+{
+	struct yaml_tag_directive *tag;
+
+	if (!event)
+		return;
+
+	switch (event->type) {
+	case YAML_DOCUMENT_START_EVENT:
+		kfree(event->document_start.version);
+		for (tag = event->document_start.tags.start;
+		     tag != event->document_start.tags.end;
+		     tag++) {
+			kfree(tag->handle);
+			kfree(tag->prefix);
+		}
+		kfree(event->document_start.tags.start);
+		break;
+
+	case YAML_ALIAS_EVENT:
+		kfree(event->alias.anchor);
+		break;
+
+	case YAML_SCALAR_EVENT:
+		kfree(event->scalar.anchor);
+		kfree(event->scalar.tag);
+		kfree(event->scalar.value);
+		break;
+
+	case YAML_SEQUENCE_START_EVENT:
+		kfree(event->sequence_start.anchor);
+		kfree(event->sequence_start.tag);
+		break;
+
+	case YAML_MAPPING_START_EVENT:
+		kfree(event->mapping_start.anchor);
+		kfree(event->mapping_start.tag);
+		break;
+
+	default:
+		break;
+	}
+
+	yaml_event_free(event);
+}
+EXPORT_SYMBOL(yaml_event_delete);
+
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/lib/yaml/yaml-simple.c b/lib/yaml/yaml-simple.c
new file mode 100644
index 000000000000..cfde261f09e8
--- /dev/null
+++ b/lib/yaml/yaml-simple.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#include <linux/ascii85.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+#include "yaml-emitter.h"
+
+static struct yaml_emitter *__yaml_open(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event;
+
+	event = yaml_stream_start_event_create(emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_start_event_create(NULL, NULL, NULL, true,
+						 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	return emitter;
+}
+
+struct yaml_emitter *yaml_open(yaml_write_t handler, void *data)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_output(emitter, handler, data);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open);
+
+struct yaml_emitter *yaml_open_file(struct seq_file *m)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_seq_file(emitter, m);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open_file);
+
+struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *out)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_string(emitter, buf, len, out);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open_string);
+
+void __yaml_mapping_start(struct yaml_emitter *emitter,
+			  enum yaml_mapping_style style)
+{
+	struct yaml_event *event;
+
+	event = yaml_mapping_start_event_create(NULL, NULL, true, style,
+						emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(__yaml_mapping_start);
+
+void yaml_mapping_end(struct yaml_emitter *emitter)
+{
+	yaml_emitter_emit(emitter, yaml_mapping_end_event_create(emitter->gfp));
+}
+EXPORT_SYMBOL(yaml_mapping_end);
+
+void __yaml_sequence_start(struct yaml_emitter *emitter,
+			   enum yaml_sequence_style style)
+{
+	struct yaml_event *event;
+
+	event = yaml_sequence_start_event_create(NULL, NULL, true, style,
+						 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(__yaml_sequence_start);
+
+void yaml_sequence_end(struct yaml_emitter *emitter)
+{
+	yaml_emitter_emit(emitter,
+			  yaml_sequence_end_event_create(emitter->gfp));
+}
+EXPORT_SYMBOL(yaml_sequence_end);
+
+void yaml_alias_printf(struct yaml_emitter *emitter,
+		       const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	yaml_emitter_emit(emitter, yaml_alias_event_create(str, emitter->gfp));
+
+	kfree(str);
+}
+EXPORT_SYMBOL(yaml_alias_printf);
+
+void yaml_scalar_printf(struct yaml_emitter *emitter,
+			const char *fmt, ...)
+{
+	struct yaml_event *event;
+	va_list ap;
+	char *str;
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	event = yaml_scalar_event_create(NULL, NULL, str, -1, true,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_scalar_printf);
+
+void yaml_pair_printf(struct yaml_emitter *emitter,
+		      const char *name, const char *fmt, ...)
+{
+	struct yaml_event *event;
+	va_list ap;
+	char *str;
+
+	event = yaml_scalar_event_create(NULL, NULL, name, -1, false,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	event = yaml_scalar_event_create(NULL, NULL, str, -1, true,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_pair_printf);
+
+void yaml_ascii85_encode(struct yaml_emitter *emitter,
+			 const void *data, size_t len, bool compressed)
+{
+	struct yaml_event *event;
+	const u32 *src = data;
+	char *buf, *dst;
+	int i;
+
+	if (!data || !len)
+		return;
+
+	buf = kmalloc(ascii85_encode_len(len) * 5, emitter->gfp);
+	if (!buf)
+		return;
+
+	dst = buf;
+	while (len > 4) {
+		u32 in = *src++;
+
+		if (in == 0) {
+			*dst++ = 'z';
+		} else {
+			for (i = 5; i--; ) {
+				dst[i] = '!' + in % 85;
+				in /= 85;
+			}
+			dst += 5;
+		}
+		len -= 4;
+	}
+
+	if (len) {
+		u32 in = 0;
+
+		memcpy(&in, src, len);
+
+		if (in == 0) {
+			*dst++ = 'z';
+		} else {
+			for (i = 5; i--; ) {
+				dst[i] = '!' + in % 85;
+				in /= 85;
+			}
+			dst += 5;
+		}
+	}
+
+	event = yaml_scalar_event_create(NULL,
+					 compressed ? "ascii85.gz" : "ascii85",
+					 buf, dst - buf, true,
+					 false, false,
+					 YAML_FOLDED_SCALAR_STYLE,
+					 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_ascii85_encode);
+
+int yaml_close(struct yaml_emitter *emitter, const char **problem)
+{
+	int err;
+
+	yaml_emitter_emit(emitter,
+			  yaml_document_end_event_create(true, GFP_KERNEL));
+	yaml_emitter_emit(emitter,
+			  yaml_stream_end_event_create(GFP_KERNEL));
+
+	err = yaml_emitter_flush(emitter, problem);
+	yaml_emitter_destroy(emitter);
+
+	return err;
+}
+EXPORT_SYMBOL(yaml_close);
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

end of thread, other threads:[~2021-02-11 11:39 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-13 23:23 [Intel-gfx] [PATCH] lib: Add a YAML emitter Chris Wilson
2020-02-14  0:13 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for " Patchwork
2020-02-14  1:01 ` [Intel-gfx] ✓ Fi.CI.BAT: success " Patchwork
2020-02-14  7:57 ` [Intel-gfx] [PATCH] " Jani Nikula
2020-02-14 12:48   ` Chris Wilson
2020-02-17 15:48 ` [Intel-gfx] ✓ Fi.CI.IGT: success for " Patchwork
2021-02-11 11:38 [Intel-gfx] [PATCH] " Chris Wilson

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).