@@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
+#include <linux/packing_types.h>
#define QUIRK_MSB_ON_THE_RIGHT BIT(0)
#define QUIRK_LITTLE_ENDIAN BIT(1)
@@ -26,4 +27,46 @@ int pack(void *pbuf, u64 uval, size_t startbit, size_t endbit, size_t pbuflen,
int unpack(const void *pbuf, u64 *uval, size_t startbit, size_t endbit,
size_t pbuflen, u8 quirks);
+void pack_fields_s(void *pbuf, size_t pbuflen, const void *ustruct,
+ const struct packed_field_s *fields, size_t num_fields,
+ u8 quirks);
+
+void pack_fields_m(void *pbuf, size_t pbuflen, const void *ustruct,
+ const struct packed_field_m *fields, size_t num_fields,
+ u8 quirks);
+
+void unpack_fields_s(const void *pbuf, size_t pbuflen, void *ustruct,
+ const struct packed_field_s *fields, size_t num_fields,
+ u8 quirks);
+
+void unpack_fields_m(const void *pbuf, size_t pbuflen, void *ustruct,
+ const struct packed_field_m *fields, size_t num_fields,
+ u8 quirks);
+
+#define pack_fields(pbuf, ustruct, fields, quirks) \
+ ({ \
+ typeof(fields[0]) *__f = fields; \
+ size_t pbuflen = sizeof(*pbuf); \
+ size_t num_fields = ARRAY_SIZE(fields); \
+ BUILD_BUG_ON(__f[0].startbit >= BITS_PER_BYTE * pbuflen); \
+ BUILD_BUG_ON(__f[num_fields - 1].startbit >= BITS_PER_BYTE * pbuflen); \
+ _Generic((fields), \
+ const struct packed_field_s * : pack_fields_s, \
+ const struct packed_field_m * : pack_fields_m \
+ )(pbuf, pbuflen, ustruct, __f, num_fields, quirks); \
+ })
+
+#define unpack_fields(pbuf, ustruct, fields, quirks) \
+ ({ \
+ typeof(fields[0]) *__f = fields; \
+ size_t pbuflen = sizeof(*pbuf); \
+ size_t num_fields = ARRAY_SIZE(fields); \
+ BUILD_BUG_ON(__f[0].startbit >= BITS_PER_BYTE * pbuflen); \
+ BUILD_BUG_ON(__f[num_fields - 1].startbit >= BITS_PER_BYTE * pbuflen); \
+ _Generic((fields), \
+ const struct packed_field_s * : unpack_fields_s, \
+ const struct packed_field_m * : unpack_fields_m \
+ )(pbuf, pbuflen, ustruct, __f, num_fields, quirks); \
+ })
+
#endif
new file mode 100644
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024, Intel Corporation
+ * Copyright (c) 2024, Vladimir Oltean <olteanv@gmail.com>
+ */
+#ifndef _LINUX_PACKING_TYPES_H
+#define _LINUX_PACKING_TYPES_H
+
+#include <linux/types.h>
+
+/* If you add another packed field type, please update
+ * scripts/mod/packed_fields.c to enable compile time sanity checks.
+ */
+
+#define GEN_PACKED_FIELD_MEMBERS(__type) \
+ __type startbit; \
+ __type endbit; \
+ __type offset; \
+ __type size;
+
+/* Small packed field. Use with bit offsets < 256, buffers < 32B and
+ * unpacked structures < 256B.
+ */
+struct packed_field_s {
+ GEN_PACKED_FIELD_MEMBERS(u8);
+};
+
+#define DECLARE_PACKED_FIELDS_S(name) \
+ const struct packed_field_s name[] __section(".rodata.packed_fields_s")
+
+/* Medium packed field. Use with bit offsets < 65536, buffers < 8KB and
+ * unpacked structures < 64KB.
+ */
+struct packed_field_m {
+ GEN_PACKED_FIELD_MEMBERS(u16);
+};
+
+#define DECLARE_PACKED_FIELDS_M(name) \
+ const struct packed_field_m name[] __section(".rodata.packed_fields_m")
+
+#define PACKED_FIELD(start, end, struct_name, struct_field) \
+{ \
+ (start), \
+ (end), \
+ offsetof(struct_name, struct_field), \
+ sizeof_field(struct_name, struct_field), \
+}
+
+#endif
@@ -175,12 +175,17 @@ void add_moddevtable(struct buffer *buf, struct module *mod);
/* sumversion.c */
void get_src_version(const char *modname, char sum[], unsigned sumlen);
+/* packed_fields.c */
+void handle_packed_field_symbol(struct module *mod, struct elf_info *info,
+ Elf_Sym *sym, const char *symname);
+
/* from modpost.c */
extern bool target_is_big_endian;
extern bool host_is_big_endian;
char *read_text_file(const char *filename);
char *get_line(char **stringp);
void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
+const char *sec_name(const struct elf_info *info, unsigned int secindex);
void __attribute__((format(printf, 2, 3)))
modpost_log(bool is_error, const char *fmt, ...);
@@ -5,10 +5,37 @@
#include <linux/packing.h>
#include <linux/module.h>
#include <linux/bitops.h>
+#include <linux/bits.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/bitrev.h>
+#define __pack_fields(pbuf, pbuflen, ustruct, fields, num_fields, quirks) \
+ ({ \
+ for (size_t i = 0; i < (num_fields); i++) { \
+ typeof(&(fields)[0]) field = &(fields)[i]; \
+ u64 uval; \
+ \
+ uval = ustruct_field_to_u64(ustruct, field->offset, field->size); \
+ \
+ __pack(pbuf, uval, field->startbit, field->endbit, \
+ pbuflen, quirks); \
+ } \
+ })
+
+#define __unpack_fields(pbuf, pbuflen, ustruct, fields, num_fields, quirks) \
+ ({ \
+ for (size_t i = 0; i < (num_fields); i++) { \
+ typeof(&(fields)[0]) field = &fields[i]; \
+ u64 uval; \
+ \
+ __unpack(pbuf, &uval, field->startbit, field->endbit, \
+ pbuflen, quirks); \
+ \
+ u64_to_ustruct_field(ustruct, field->offset, field->size, uval); \
+ } \
+ })
+
/**
* calculate_box_addr - Determine physical location of byte in buffer
* @box: Index of byte within buffer seen as a logical big-endian big number
@@ -322,4 +349,122 @@ int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
}
EXPORT_SYMBOL(packing);
+static u64 ustruct_field_to_u64(const void *ustruct, size_t field_offset,
+ size_t field_size)
+{
+ switch (field_size) {
+ case 1:
+ return *((u8 *)(ustruct + field_offset));
+ case 2:
+ return *((u16 *)(ustruct + field_offset));
+ case 4:
+ return *((u32 *)(ustruct + field_offset));
+ default:
+ return *((u64 *)(ustruct + field_offset));
+ }
+}
+
+static void u64_to_ustruct_field(void *ustruct, size_t field_offset,
+ size_t field_size, u64 uval)
+{
+ switch (field_size) {
+ case 1:
+ *((u8 *)(ustruct + field_offset)) = uval;
+ break;
+ case 2:
+ *((u16 *)(ustruct + field_offset)) = uval;
+ break;
+ case 4:
+ *((u32 *)(ustruct + field_offset)) = uval;
+ break;
+ default:
+ *((u64 *)(ustruct + field_offset)) = uval;
+ break;
+ }
+}
+
+/**
+ * pack_fields_s - Pack array of small fields
+ *
+ * @pbuf: Pointer to a buffer holding the packed value.
+ * @pbuflen: The length in bytes of the packed buffer pointed to by @pbuf.
+ * @ustruct: Pointer to CPU-readable structure holding the unpacked value.
+ * It is expected (but not checked) that this has the same data type
+ * as all struct packed_field_s definitions.
+ * @fields: Array of small packed fields definition. They must not overlap.
+ * @num_fields: Length of @fields array.
+ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
+ * QUIRK_MSB_ON_THE_RIGHT.
+ */
+void pack_fields_s(void *pbuf, size_t pbuflen, const void *ustruct,
+ const struct packed_field_s *fields, size_t num_fields,
+ u8 quirks)
+{
+ __pack_fields(pbuf, pbuflen, ustruct, fields, num_fields, quirks);
+}
+EXPORT_SYMBOL(pack_fields_s);
+
+/**
+ * pack_fields_m - Pack array of medium fields
+ *
+ * @pbuf: Pointer to a buffer holding the packed value.
+ * @pbuflen: The length in bytes of the packed buffer pointed to by @pbuf.
+ * @ustruct: Pointer to CPU-readable structure holding the unpacked value.
+ * It is expected (but not checked) that this has the same data type
+ * as all struct packed_field_s definitions.
+ * @fields: Array of medium packed fields definition. They must not overlap.
+ * @num_fields: Length of @fields array.
+ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
+ * QUIRK_MSB_ON_THE_RIGHT.
+ */
+void pack_fields_m(void *pbuf, size_t pbuflen, const void *ustruct,
+ const struct packed_field_m *fields, size_t num_fields,
+ u8 quirks)
+{
+ __pack_fields(pbuf, pbuflen, ustruct, fields, num_fields, quirks);
+}
+EXPORT_SYMBOL(pack_fields_m);
+
+/**
+ * unpack_fields_s - Unpack array of small fields
+ *
+ * @pbuf: Pointer to a buffer holding the packed value.
+ * @pbuflen: The length in bytes of the packed buffer pointed to by @pbuf.
+ * @ustruct: Pointer to CPU-readable structure holding the unpacked value.
+ * It is expected (but not checked) that this has the same data type
+ * as all struct packed_field_s definitions.
+ * @fields: Array of small packed fields definition. They must not overlap.
+ * @num_fields: Length of @fields array.
+ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
+ * QUIRK_MSB_ON_THE_RIGHT.
+ */
+void unpack_fields_s(const void *pbuf, size_t pbuflen, void *ustruct,
+ const struct packed_field_s *fields, size_t num_fields,
+ u8 quirks)
+{
+ __unpack_fields(pbuf, pbuflen, ustruct, fields, num_fields, quirks);
+}
+EXPORT_SYMBOL(unpack_fields_s);
+
+/**
+ * unpack_fields_m - Unpack array of medium fields
+ *
+ * @pbuf: Pointer to a buffer holding the packed value.
+ * @pbuflen: The length in bytes of the packed buffer pointed to by @pbuf.
+ * @ustruct: Pointer to CPU-readable structure holding the unpacked value.
+ * It is expected (but not checked) that this has the same data type
+ * as all struct packed_field_s definitions.
+ * @fields: Array of medium packed fields definition. They must not overlap.
+ * @num_fields: Length of @fields array.
+ * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
+ * QUIRK_MSB_ON_THE_RIGHT.
+ */
+void unpack_fields_m(const void *pbuf, size_t pbuflen, void *ustruct,
+ const struct packed_field_m *fields, size_t num_fields,
+ u8 quirks)
+{
+ __unpack_fields(pbuf, pbuflen, ustruct, fields, num_fields, quirks);
+}
+EXPORT_SYMBOL(unpack_fields_m);
+
MODULE_DESCRIPTION("Generic bitfield packing and unpacking");
@@ -396,9 +396,70 @@ static void packing_test_unpack(struct kunit *test)
KUNIT_EXPECT_EQ(test, uval, params->uval);
}
+#define PACKED_BUF_SIZE 8
+
+typedef struct __packed { u8 buf[PACKED_BUF_SIZE]; } packed_buf_t;
+
+struct test_data {
+ u32 field3;
+ u16 field2;
+ u16 field4;
+ u16 field6;
+ u8 field1;
+ u8 field5;
+};
+
+static DECLARE_PACKED_FIELDS_S(test_fields) = {
+ PACKED_FIELD(63, 61, struct test_data, field1),
+ PACKED_FIELD(60, 52, struct test_data, field2),
+ PACKED_FIELD(51, 28, struct test_data, field3),
+ PACKED_FIELD(27, 14, struct test_data, field4),
+ PACKED_FIELD(13, 9, struct test_data, field5),
+ PACKED_FIELD(8, 0, struct test_data, field6),
+};
+
+static void packing_test_pack_fields(struct kunit *test)
+{
+ const struct test_data data = {
+ .field1 = 0x2,
+ .field2 = 0x100,
+ .field3 = 0xF00050,
+ .field4 = 0x7D3,
+ .field5 = 0x9,
+ .field6 = 0x10B,
+ };
+ packed_buf_t expect = {
+ .buf = { 0x50, 0x0F, 0x00, 0x05, 0x01, 0xF4, 0xD3, 0x0B },
+ };
+ packed_buf_t buf = {};
+
+ pack_fields(&buf, &data, test_fields, 0);
+
+ KUNIT_EXPECT_MEMEQ(test, &expect, &buf, sizeof(buf));
+}
+
+static void packing_test_unpack_fields(struct kunit *test)
+{
+ const packed_buf_t buf = {
+ .buf = { 0x17, 0x28, 0x10, 0x19, 0x3D, 0xA9, 0x07, 0x9C },
+ };
+ struct test_data data = {};
+
+ unpack_fields(&buf, &data, test_fields, 0);
+
+ KUNIT_EXPECT_EQ(test, 0, data.field1);
+ KUNIT_EXPECT_EQ(test, 0x172, data.field2);
+ KUNIT_EXPECT_EQ(test, 0x810193, data.field3);
+ KUNIT_EXPECT_EQ(test, 0x36A4, data.field4);
+ KUNIT_EXPECT_EQ(test, 0x3, data.field5);
+ KUNIT_EXPECT_EQ(test, 0x19C, data.field6);
+}
+
static struct kunit_case packing_test_cases[] = {
KUNIT_CASE_PARAM(packing_test_pack, packing_gen_params),
KUNIT_CASE_PARAM(packing_test_unpack, packing_gen_params),
+ KUNIT_CASE(packing_test_pack_fields),
+ KUNIT_CASE(packing_test_unpack_fields),
{},
};
@@ -327,7 +327,7 @@ static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr)
sechdr->sh_name);
}
-static const char *sec_name(const struct elf_info *info, unsigned int secindex)
+const char *sec_name(const struct elf_info *info, unsigned int secindex)
{
/*
* If sym->st_shndx is a special section index, there is no
@@ -1602,6 +1602,7 @@ static void read_symbols(const char *modname)
handle_symbol(mod, &info, sym, symname);
handle_moddevtable(mod, &info, sym, symname);
+ handle_packed_field_symbol(mod, &info, sym, symname);
}
check_sec_ref(mod, &info);
new file mode 100644
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024, Intel Corporation. */
+
+/* Code to validate struct packed_field_[sm] data, and perform sanity checks
+ * to ensure the packed field data is laid out correctly and fits into the
+ * relevant buffer size.
+ */
+
+#include <fnmatch.h>
+#include <hashtable.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <xalloc.h>
+
+#include "modpost.h"
+
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+#define BITS_PER_BYTE 8
+
+/* Big exception to the "don't include kernel headers into userspace", which
+ * even potentially has different endianness and word sizes, since we handle
+ * those differences explicitly below
+ */
+#include "../../include/linux/packing_types.h"
+
+#define max(a, b) ({\
+ typeof(a) _a = a;\
+ typeof(b) _b = b;\
+ _a > _b ? _a : _b; })
+
+#define min(a, b) ({\
+ typeof(a) _a = a;\
+ typeof(b) _b = b;\
+ _a < _b ? _a : _b; })
+
+struct packed_field_elem {
+ uint64_t startbit;
+ uint64_t endbit;
+ uint64_t offset;
+ uint64_t size;
+};
+
+enum field_type {
+ UNKNOWN_SECTION,
+ PACKED_FIELD_S,
+ PACKED_FIELD_M,
+};
+
+enum element_order {
+ FIRST_ELEMENT,
+ SECOND_ELEMENT,
+ ASCENDING_ORDER,
+ DESCENDING_ORDER,
+};
+
+static size_t field_type_to_size(enum field_type type)
+{
+ switch (type) {
+ case PACKED_FIELD_S:
+ return sizeof(struct packed_field_s);
+ case PACKED_FIELD_M:
+ return sizeof(struct packed_field_m);
+ default:
+ error("attempted to get field size for unknown packed field type %u\n",
+ type);
+ return 0;
+ }
+}
+
+static void get_field_contents(const void *data, enum field_type type,
+ struct packed_field_elem *elem)
+{
+ switch (type) {
+ case PACKED_FIELD_S: {
+ const struct packed_field_s *data_field = data;
+
+ elem->startbit = TO_NATIVE(data_field->startbit);
+ elem->endbit = TO_NATIVE(data_field->endbit);
+ elem->offset = TO_NATIVE(data_field->offset);
+ elem->size = TO_NATIVE(data_field->size);
+ return;
+ }
+ case PACKED_FIELD_M: {
+ const struct packed_field_m *data_field = data;
+
+ elem->startbit = TO_NATIVE(data_field->startbit);
+ elem->endbit = TO_NATIVE(data_field->endbit);
+ elem->offset = TO_NATIVE(data_field->offset);
+ elem->size = TO_NATIVE(data_field->size);
+ return;
+ }
+ default:
+ error("attempted to get field contents for unknown packed field type %u\n",
+ type);
+ }
+}
+
+void handle_packed_field_symbol(struct module *mod, struct elf_info *info,
+ Elf_Sym *sym, const char *symname)
+{
+ unsigned int secindex = get_secindex(info, sym);
+ struct packed_field_elem elem = {}, prev = {};
+ enum element_order order = FIRST_ELEMENT;
+ enum field_type type = UNKNOWN_SECTION;
+ size_t field_size, count;
+ const void *data, *ptr;
+ const char *section;
+
+ /* Skip symbols without a name */
+ if (*symname == '\0')
+ return;
+
+ /* Skip symbols with invalid sections */
+ if (secindex >= info->num_sections)
+ return;
+
+ section = sec_name(info, secindex);
+
+ if (strcmp(section, ".rodata.packed_fields_s") == 0)
+ type = PACKED_FIELD_S;
+ else if (strcmp(section, ".rodata.packed_fields_m") == 0)
+ type = PACKED_FIELD_M;
+
+ /* Other sections don't relate to packed fields */
+ if (type == UNKNOWN_SECTION)
+ return;
+
+ field_size = field_type_to_size(type);
+
+ /* check that the data is a multiple of the size */
+ if (sym->st_size % field_size != 0) {
+ error("[%s.ko] \"%s\" has size %zu which is not a multiple of the field size (%zu)\n",
+ mod->name, symname, sym->st_size, field_size);
+ return;
+ }
+
+ data = sym_get_data(info, sym);
+
+ for (ptr = data, count = 0;
+ ptr < data + sym->st_size;
+ ptr += field_size, count++, prev = elem) {
+ get_field_contents(ptr, type, &elem);
+
+ if (elem.size != 1 && elem.size != 2 &&
+ elem.size != 4 && elem.size != 8)
+ error("[%s.ko] \"%s\" field %zu unpacked size (%" PRIu64 ") must be 1, 2, 4, or 8\n",
+ mod->name, symname, count, elem.size);
+
+ if (elem.startbit < elem.endbit)
+ error("[%s.ko] \"%s\" field %zu (%" PRIu64 "-%" PRIu64 "): start bit must be >= end bit\n",
+ mod->name, symname, count,
+ elem.startbit, elem.endbit);
+
+ if (elem.startbit - elem.endbit + 1 > BITS_PER_BYTE * elem.size)
+ error("[%s.ko] \"%s\" field %zu (%" PRIu64 "-%" PRIu64 ") has a width of %" PRIu64 " bits which does not fit into the unpacked structure field (%" PRIu64 " bytes)\n",
+ mod->name, symname, count,
+ elem.startbit, elem.endbit,
+ elem.startbit - elem.endbit + 1,
+ elem.size);
+
+ if (order != FIRST_ELEMENT &&
+ max(elem.endbit, prev.endbit) <=
+ min(elem.startbit, prev.startbit))
+ error("[%s.ko] \"%s\" field %zu (%" PRIu64 "-%" PRIu64 ") overlaps with previous field (%" PRIu64 "-%" PRIu64 ")\n",
+ mod->name, symname, count,
+ elem.startbit, elem.endbit,
+ prev.startbit, prev.endbit);
+
+ switch (order) {
+ case FIRST_ELEMENT:
+ order = SECOND_ELEMENT;
+ break;
+ case SECOND_ELEMENT:
+ order = prev.startbit < elem.startbit ?
+ ASCENDING_ORDER : DESCENDING_ORDER;
+ break;
+ case ASCENDING_ORDER:
+ if (prev.startbit >= elem.startbit ||
+ prev.endbit >= elem.endbit)
+ error("[%s.ko] \"%s\" field %zu (%" PRIu64 "-%" PRIu64") not in ascending order with previous field (%" PRIu64 "-%" PRIu64 ")\n",
+ mod->name, symname, count,
+ elem.startbit, elem.endbit,
+ prev.startbit, prev.endbit);
+ break;
+ case DESCENDING_ORDER:
+ if (prev.startbit <= elem.startbit ||
+ prev.endbit <= elem.endbit)
+ error("[%s.ko] \"%s\" field %zu (%" PRIu64 "-%" PRIu64") not in descending order with previous field (%" PRIu64 "-%" PRIu64 ")\n",
+ mod->name, symname, count,
+ elem.startbit, elem.endbit,
+ prev.startbit, prev.endbit);
+ break;
+ default:
+ break;
+ }
+ }
+}
@@ -235,3 +235,62 @@ programmer against incorrect API use. The errors are not expected to occur
during runtime, therefore it is reasonable for xxx_packing() to return void
and simply swallow those errors. Optionally it can dump stack or print the
error description.
+
+The pack_fields() and unpack_fields() macros automatically select the
+appropriate function at compile time based on the type of the fields array
+passed in.
+
+Packed Fields
+-------------
+
+Drivers are encouraged to use the ``pack_fields()`` and ``unpack_fields()``
+APIs over using ``pack()``, ``unpack()``, or ``packing()``.
+
+These APIs use field definitions in arrays of ``struct packed_field_s`` or
+``struct packed_field_m`` stored as ``.rodata``. This significantly reduces
+the code footprint required to pack or unpack many fields. In addition,
+sanity checks on the field definitions are handled at compile time via
+``modpost`` warnings, rather than only when the offending code is executed.
+
+The ``pack_fields()`` and ``unpack_fields()`` macros determine the size of
+the packed buffer by its type. Thus, you must ensure the buffer is a pointer
+to a type with the desired size. This is typically achieved by creating a
+typedef with a packed structure.
+
+Here is an example of how to use the fields APIs:
+
+.. code-block:: c
+
+ struct data {
+ u64 field3;
+ u32 field4;
+ u16 field1;
+ u8 field2;
+ };
+
+ #define SIZE 13
+
+ typdef struct __packed { u8 buf[SIZE]; } packed_buf_t;
+
+ DECLARE_PACKED_FIELDS_S(fields, SIZE) = {
+ PACKED_FIELD(100, 90, struct data, field1),
+ PACKED_FIELD(90, 87, struct data, field2),
+ PACKED_FIELD(86, 30, struct data, field3),
+ PACKED_FIELD(29, 0, struct data, field4),
+ };
+
+ void unpack_your_data(const packed_buf_t *buf, struct data *unpacked)
+ {
+ BUILD_BUG_ON(sizeof(*buf) != SIZE;
+
+ unpack_fields(buf, sizeof(*buf), unpacked, fields,
+ QUIRK_LITTLE_ENDIAN);
+ }
+
+ void pack_your_data(const struct data *unpacked, packed_buf_t *buf)
+ {
+ BUILD_BUG_ON(sizeof(*buf) != SIZE;
+
+ pack_fields(buf, sizeof(*buf), unpacked, fields,
+ QUIRK_LITTLE_ENDIAN);
+ }
@@ -17391,8 +17391,10 @@ L: netdev@vger.kernel.org
S: Supported
F: Documentation/core-api/packing.rst
F: include/linux/packing.h
+F: include/linux/packing_types.h
F: lib/packing.c
F: lib/packing_test.c
+F: scripts/mod/packed_fields.c
PADATA PARALLEL EXECUTION MECHANISM
M: Steffen Klassert <steffen.klassert@secunet.com>
@@ -4,7 +4,7 @@ CFLAGS_REMOVE_empty.o += $(CC_FLAGS_LTO)
hostprogs-always-y += modpost mk_elfconfig
always-y += empty.o
-modpost-objs := modpost.o file2alias.o sumversion.o symsearch.o
+modpost-objs := modpost.o file2alias.o sumversion.o symsearch.o packed_fields.o
devicetable-offsets-file := devicetable-offsets.h
@@ -15,7 +15,7 @@ targets += $(devicetable-offsets-file) devicetable-offsets.s
# dependencies on generated files need to be listed explicitly
-$(obj)/modpost.o $(obj)/file2alias.o $(obj)/sumversion.o $(obj)/symsearch.o: $(obj)/elfconfig.h
+$(obj)/modpost.o $(obj)/file2alias.o $(obj)/sumversion.o $(obj)/symsearch.o $(obj)/packed_fields.o: $(obj)/elfconfig.h
$(obj)/file2alias.o: $(obj)/$(devicetable-offsets-file)
quiet_cmd_elfconfig = MKELF $@