All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/4] IR decoding using BPF
@ 2018-05-14 21:10 Sean Young
  2018-05-14 21:10 ` [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER Sean Young
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Sean Young @ 2018-05-14 21:10 UTC (permalink / raw)
  To: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

The kernel IR decoders support the most widely used IR protocols, but there
are many protocols which are not supported[1]. For example, the
lirc-remotes[2] repo has over 2700 remotes, many of which are not supported
by rc-core. There is a "long tail" of unsupported IR protocols.

IR encoding is done in such a way that some simple circuit can decode it;
therefore, bpf is ideal.

In order to support all these protocols, here we have bpf based IR decoding.
The idea is that user-space can define a decoder in bpf, attach it to
the rc device through the lirc chardev.

Separate work is underway to extend ir-keytable to have an extensive library
of bpf-based decoders, and a much expanded library of rc keymaps. 

Another future application would be to compile IRP[3] to a IR BPF program, and
so support virtually every remote without having to write a decoder for each.

Thanks,

Sean Young

[1] http://www.hifi-remote.com/wiki/index.php?title=DecodeIR
[2] https://sourceforge.net/p/lirc-remotes/code/ci/master/tree/remotes/
[3] http://www.hifi-remote.com/wiki/index.php?title=IRP_Notation

Sean Young (4):
  media: rc: introduce BPF_PROG_IR_DECODER
  media: bpf: allow raw IR decoder bpf programs to be used
  media: rc bpf: move ir_raw_event to uapi
  samples/bpf: an example of a raw IR decoder

 drivers/media/rc/Kconfig                  |   8 +
 drivers/media/rc/Makefile                 |   1 +
 drivers/media/rc/ir-bpf-decoder.c         | 284 ++++++++++++++++++++++
 drivers/media/rc/lirc_dev.c               |  30 +++
 drivers/media/rc/rc-core-priv.h           |  15 ++
 drivers/media/rc/rc-ir-raw.c              |   5 +
 include/linux/bpf_types.h                 |   3 +
 include/media/rc-core.h                   |  19 +-
 include/uapi/linux/bpf.h                  |  17 +-
 include/uapi/linux/bpf_rcdev.h            |  24 ++
 kernel/bpf/syscall.c                      |   7 +
 samples/bpf/Makefile                      |   4 +
 samples/bpf/bpf_load.c                    |   9 +-
 samples/bpf/grundig_decoder_kern.c        | 112 +++++++++
 samples/bpf/grundig_decoder_user.c        |  54 ++++
 tools/bpf/bpftool/prog.c                  |   1 +
 tools/include/uapi/linux/bpf.h            |  17 +-
 tools/testing/selftests/bpf/bpf_helpers.h |   6 +
 18 files changed, 594 insertions(+), 22 deletions(-)
 create mode 100644 drivers/media/rc/ir-bpf-decoder.c
 create mode 100644 include/uapi/linux/bpf_rcdev.h
 create mode 100644 samples/bpf/grundig_decoder_kern.c
 create mode 100644 samples/bpf/grundig_decoder_user.c

-- 
2.17.0

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

* [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER
  2018-05-14 21:10 [PATCH v1 0/4] IR decoding using BPF Sean Young
@ 2018-05-14 21:10 ` Sean Young
  2018-05-14 23:27   ` Randy Dunlap
  2018-05-15  4:48   ` Y Song
  2018-05-14 21:10 ` [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used Sean Young
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 15+ messages in thread
From: Sean Young @ 2018-05-14 21:10 UTC (permalink / raw)
  To: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
that the last key should be repeated.

Signed-off-by: Sean Young <sean@mess.org>
---
 drivers/media/rc/Kconfig          |  8 +++
 drivers/media/rc/Makefile         |  1 +
 drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
 include/linux/bpf_types.h         |  3 +
 include/uapi/linux/bpf.h          | 16 +++++-
 5 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/rc/ir-bpf-decoder.c

diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index eb2c3b6eca7f..10ad6167d87c 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -120,6 +120,14 @@ config IR_IMON_DECODER
 	   remote control and you would like to use it with a raw IR
 	   receiver, or if you wish to use an encoder to transmit this IR.
 
+config IR_BPF_DECODER
+	bool "Enable IR raw decoder using BPF"
+	depends on BPF_SYSCALL
+	depends on RC_CORE=y
+	help
+	   Enable this option to make it possible to load custom IR
+	   decoders written in BPF.
+
 endif #RC_DECODERS
 
 menuconfig RC_DEVICES
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 2e1c87066f6c..12e1118430d0 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -5,6 +5,7 @@ obj-y += keymaps/
 obj-$(CONFIG_RC_CORE) += rc-core.o
 rc-core-y := rc-main.o rc-ir-raw.o
 rc-core-$(CONFIG_LIRC) += lirc_dev.o
+rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
 obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
 obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
 obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
new file mode 100644
index 000000000000..aaa5e208b1a5
--- /dev/null
+++ b/drivers/media/rc/ir-bpf-decoder.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+// ir-bpf-decoder.c - handles bpf decoders
+//
+// Copyright (C) 2018 Sean Young <sean@mess.org>
+
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include "rc-core-priv.h"
+
+/*
+ * BPF interface for raw IR decoder
+ */
+const struct bpf_prog_ops ir_decoder_prog_ops = {
+};
+
+BPF_CALL_1(bpf_rc_repeat, struct ir_raw_event*, event)
+{
+	struct ir_raw_event_ctrl *ctrl;
+
+	ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
+
+	rc_repeat(ctrl->dev);
+	return 0;
+}
+
+static const struct bpf_func_proto rc_repeat_proto = {
+	.func	   = bpf_rc_repeat,
+	.gpl_only  = true, // rc_repeat is EXPORT_SYMBOL_GPL
+	.ret_type  = RET_VOID,
+	.arg1_type = ARG_PTR_TO_CTX,
+};
+
+BPF_CALL_4(bpf_rc_keydown, struct ir_raw_event*, event, u32, protocol,
+	   u32, scancode, u32, toggle)
+{
+	struct ir_raw_event_ctrl *ctrl;
+
+	ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
+	rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
+	return 0;
+}
+
+static const struct bpf_func_proto rc_keydown_proto = {
+	.func	   = bpf_rc_keydown,
+	.gpl_only  = true, // rc_keydown is EXPORT_SYMBOL_GPL
+	.ret_type  = RET_VOID,
+	.arg1_type = ARG_PTR_TO_CTX,
+	.arg2_type = ARG_ANYTHING,
+	.arg3_type = ARG_ANYTHING,
+	.arg4_type = ARG_ANYTHING,
+};
+
+static const struct bpf_func_proto *ir_decoder_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+	switch (func_id) {
+	case BPF_FUNC_rc_repeat:
+		return &rc_repeat_proto;
+	case BPF_FUNC_rc_keydown:
+		return &rc_keydown_proto;
+	case BPF_FUNC_map_lookup_elem:
+		return &bpf_map_lookup_elem_proto;
+	case BPF_FUNC_map_update_elem:
+		return &bpf_map_update_elem_proto;
+	case BPF_FUNC_map_delete_elem:
+		return &bpf_map_delete_elem_proto;
+	case BPF_FUNC_ktime_get_ns:
+		return &bpf_ktime_get_ns_proto;
+	case BPF_FUNC_tail_call:
+		return &bpf_tail_call_proto;
+	case BPF_FUNC_get_prandom_u32:
+		return &bpf_get_prandom_u32_proto;
+	default:
+		return NULL;
+	}
+}
+
+static bool ir_decoder_is_valid_access(int off, int size,
+				       enum bpf_access_type type,
+				       const struct bpf_prog *prog,
+				       struct bpf_insn_access_aux *info)
+{
+	if (type == BPF_WRITE)
+		return false;
+	if (off < 0 || off + size > sizeof(struct ir_raw_event))
+		return false;
+
+	return true;
+}
+
+const struct bpf_verifier_ops ir_decoder_verifier_ops = {
+	.get_func_proto  = ir_decoder_func_proto,
+	.is_valid_access = ir_decoder_is_valid_access
+};
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 2b28fcf6f6ae..ee5355715ee0 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -25,6 +25,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
 #ifdef CONFIG_CGROUP_BPF
 BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
 #endif
+#ifdef CONFIG_IR_BPF_DECODER
+BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_DECODER, ir_decoder)
+#endif
 
 BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
 BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index c5ec89732a8d..6ad053e831c0 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -137,6 +137,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_SK_MSG,
 	BPF_PROG_TYPE_RAW_TRACEPOINT,
 	BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+	BPF_PROG_TYPE_RAWIR_DECODER,
 };
 
 enum bpf_attach_type {
@@ -755,6 +756,17 @@ union bpf_attr {
  *     @addr: pointer to struct sockaddr to bind socket to
  *     @addr_len: length of sockaddr structure
  *     Return: 0 on success or negative error code
+ *
+ * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
+ *	Report decoded scancode with toggle value
+ *	@ctx: pointer to ctx
+ *	@protocol: decoded protocol
+ *	@scancode: decoded scancode
+ *	@toggle: set to 1 if button was toggled, else 0
+ *
+ * int bpf_rc_repeat(ctx)
+ *	Repeat the last decoded scancode
+ *	@ctx: pointer to ctx
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -821,7 +833,9 @@ union bpf_attr {
 	FN(msg_apply_bytes),		\
 	FN(msg_cork_bytes),		\
 	FN(msg_pull_data),		\
-	FN(bind),
+	FN(bind),			\
+	FN(rc_repeat),			\
+	FN(rc_keydown),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
-- 
2.17.0

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

* [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used
  2018-05-14 21:10 [PATCH v1 0/4] IR decoding using BPF Sean Young
  2018-05-14 21:10 ` [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER Sean Young
@ 2018-05-14 21:10 ` Sean Young
  2018-05-15  2:34   ` kbuild test robot
  2018-05-15  5:22   ` Y Song
  2018-05-14 21:11 ` [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi Sean Young
  2018-05-14 21:11 ` [PATCH v1 4/4] samples/bpf: an example of a raw IR decoder Sean Young
  3 siblings, 2 replies; 15+ messages in thread
From: Sean Young @ 2018-05-14 21:10 UTC (permalink / raw)
  To: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

This implements attaching, detaching, querying and execution. The target
fd has to be the /dev/lircN device.

Signed-off-by: Sean Young <sean@mess.org>
---
 drivers/media/rc/ir-bpf-decoder.c | 191 ++++++++++++++++++++++++++++++
 drivers/media/rc/lirc_dev.c       |  30 +++++
 drivers/media/rc/rc-core-priv.h   |  15 +++
 drivers/media/rc/rc-ir-raw.c      |   5 +
 include/uapi/linux/bpf.h          |   1 +
 kernel/bpf/syscall.c              |   7 ++
 6 files changed, 249 insertions(+)

diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
index aaa5e208b1a5..651590a14772 100644
--- a/drivers/media/rc/ir-bpf-decoder.c
+++ b/drivers/media/rc/ir-bpf-decoder.c
@@ -91,3 +91,194 @@ const struct bpf_verifier_ops ir_decoder_verifier_ops = {
 	.get_func_proto  = ir_decoder_func_proto,
 	.is_valid_access = ir_decoder_is_valid_access
 };
+
+#define BPF_MAX_PROGS 64
+
+int rc_dev_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)
+{
+	struct ir_raw_event_ctrl *raw;
+	struct bpf_prog_array __rcu *old_array;
+	struct bpf_prog_array *new_array;
+	int ret;
+
+	if (rcdev->driver_type != RC_DRIVER_IR_RAW)
+		return -EINVAL;
+
+	ret = mutex_lock_interruptible(&rcdev->lock);
+	if (ret)
+		return ret;
+
+	raw = rcdev->raw;
+
+	if (raw->progs && bpf_prog_array_length(raw->progs) >= BPF_MAX_PROGS) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	old_array = raw->progs;
+	ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array);
+	if (ret < 0)
+		goto out;
+
+	rcu_assign_pointer(raw->progs, new_array);
+	bpf_prog_array_free(old_array);
+out:
+	mutex_unlock(&rcdev->lock);
+	return ret;
+}
+
+int rc_dev_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)
+{
+	struct ir_raw_event_ctrl *raw;
+	struct bpf_prog_array __rcu *old_array;
+	struct bpf_prog_array *new_array;
+	int ret;
+
+	if (rcdev->driver_type != RC_DRIVER_IR_RAW)
+		return -EINVAL;
+
+	ret = mutex_lock_interruptible(&rcdev->lock);
+	if (ret)
+		return ret;
+
+	raw = rcdev->raw;
+
+	old_array = raw->progs;
+	ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array);
+	if (ret < 0) {
+		bpf_prog_array_delete_safe(old_array, prog);
+	} else {
+		rcu_assign_pointer(raw->progs, new_array);
+		bpf_prog_array_free(old_array);
+	}
+
+	bpf_prog_put(prog);
+	mutex_unlock(&rcdev->lock);
+	return 0;
+}
+
+void rc_dev_bpf_run(struct rc_dev *rcdev)
+{
+	struct ir_raw_event_ctrl *raw = rcdev->raw;
+
+	if (raw->progs)
+		BPF_PROG_RUN_ARRAY(raw->progs, &raw->prev_ev, BPF_PROG_RUN);
+}
+
+void rc_dev_bpf_put(struct rc_dev *rcdev)
+{
+	struct bpf_prog_array *progs = rcdev->raw->progs;
+	int i, size;
+
+	if (!progs)
+		return;
+
+	size = bpf_prog_array_length(progs);
+	for (i = 0; i < size; i++)
+		bpf_prog_put(progs->progs[i]);
+
+	bpf_prog_array_free(rcdev->raw->progs);
+}
+
+int rc_dev_prog_attach(const union bpf_attr *attr)
+{
+	struct bpf_prog *prog;
+	struct rc_dev *rcdev;
+	int ret;
+
+	if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
+		return -EINVAL;
+
+	prog = bpf_prog_get_type(attr->attach_bpf_fd,
+				 BPF_PROG_TYPE_RAWIR_DECODER);
+	if (IS_ERR(prog))
+		return PTR_ERR(prog);
+
+	rcdev = rc_dev_get_from_fd(attr->target_fd);
+	if (IS_ERR(rcdev)) {
+		bpf_prog_put(prog);
+		return PTR_ERR(rcdev);
+	}
+
+	ret = rc_dev_bpf_attach(rcdev, prog, attr->attach_flags);
+	if (ret)
+		bpf_prog_put(prog);
+
+	put_device(&rcdev->dev);
+
+	return ret;
+}
+
+int rc_dev_prog_detach(const union bpf_attr *attr)
+{
+	struct bpf_prog *prog;
+	struct rc_dev *rcdev;
+	int ret;
+
+	if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
+		return -EINVAL;
+
+	prog = bpf_prog_get_type(attr->attach_bpf_fd,
+				 BPF_PROG_TYPE_RAWIR_DECODER);
+	if (IS_ERR(prog))
+		return PTR_ERR(prog);
+
+	rcdev = rc_dev_get_from_fd(attr->target_fd);
+	if (IS_ERR(rcdev)) {
+		bpf_prog_put(prog);
+		return PTR_ERR(rcdev);
+	}
+
+	ret = rc_dev_bpf_detach(rcdev, prog, attr->attach_flags);
+
+	bpf_prog_put(prog);
+	put_device(&rcdev->dev);
+
+	return ret;
+}
+
+int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
+{
+	__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
+	struct bpf_prog_array *progs;
+	struct rc_dev *rcdev;
+	u32 cnt, flags = 0;
+	int ret;
+
+	if (attr->query.query_flags)
+		return -EINVAL;
+
+	rcdev = rc_dev_get_from_fd(attr->query.target_fd);
+	if (IS_ERR(rcdev))
+		return PTR_ERR(rcdev);
+
+	if (rcdev->driver_type != RC_DRIVER_IR_RAW) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = mutex_lock_interruptible(&rcdev->lock);
+	if (ret)
+		goto out;
+
+	progs = rcdev->raw->progs;
+	cnt = progs ? bpf_prog_array_length(progs) : 0;
+
+	if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) {
+		ret = -EFAULT;
+		goto out;
+	}
+	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	if (attr->query.prog_cnt != 0 && prog_ids && cnt)
+		ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt);
+
+out:
+	mutex_unlock(&rcdev->lock);
+	put_device(&rcdev->dev);
+
+	return ret;
+}
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 24e9fbb80e81..65319f2ccc13 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/device.h>
+#include <linux/file.h>
 #include <linux/idr.h>
 #include <linux/poll.h>
 #include <linux/sched.h>
@@ -28,6 +29,8 @@
 #include "rc-core-priv.h"
 #include <uapi/linux/lirc.h>
 
+#include <linux/bpf-rcdev.h>
+
 #define LIRCBUF_SIZE	256
 
 static dev_t lirc_base_dev;
@@ -816,4 +819,31 @@ void __exit lirc_dev_exit(void)
 	unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
 }
 
+struct rc_dev *rc_dev_get_from_fd(int fd)
+{
+	struct rc_dev *dev;
+	struct file *f;
+
+	f = fget_raw(fd);
+	if (!f)
+		return ERR_PTR(-EBADF);
+
+	if (!S_ISCHR(f->f_inode->i_mode) ||
+	    imajor(f->f_inode) != MAJOR(lirc_base_dev)) {
+		fput(f);
+		return ERR_PTR(-EBADF);
+	}
+
+	dev = container_of(f->f_inode->i_cdev, struct rc_dev, lirc_cdev);
+	if (!dev->registered) {
+		fput(f);
+		return ERR_PTR(-ENODEV);
+	}
+
+	get_device(&dev->dev);
+	fput(f);
+
+	return dev;
+}
+
 MODULE_ALIAS("lirc_dev");
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index e0e6a17460f6..b6f24f369657 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -57,6 +57,9 @@ struct ir_raw_event_ctrl {
 	/* raw decoder state follows */
 	struct ir_raw_event prev_ev;
 	struct ir_raw_event this_ev;
+#ifdef CONFIG_IR_BPF_DECODER
+	struct bpf_prog_array *progs;
+#endif
 	struct nec_dec {
 		int state;
 		unsigned count;
@@ -288,6 +291,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
 void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
 int ir_lirc_register(struct rc_dev *dev);
 void ir_lirc_unregister(struct rc_dev *dev);
+struct rc_dev *rc_dev_get_from_fd(int fd);
 #else
 static inline int lirc_dev_init(void) { return 0; }
 static inline void lirc_dev_exit(void) {}
@@ -299,4 +303,15 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
 static inline void ir_lirc_unregister(struct rc_dev *dev) { }
 #endif
 
+/*
+ * bpf interface
+ */
+#ifdef CONFIG_IR_BPF_DECODER
+void rc_dev_bpf_put(struct rc_dev *dev);
+void rc_dev_bpf_run(struct rc_dev *dev);
+#else
+void rc_dev_bpf_put(struct rc_dev *dev) {}
+void rc_dev_bpf_run(struct rc_dev *dev) {}
+#endif
+
 #endif /* _RC_CORE_PRIV */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index 374f83105a23..efddd9c44466 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -8,6 +8,8 @@
 #include <linux/mutex.h>
 #include <linux/kmod.h>
 #include <linux/sched.h>
+#include <linux/filter.h>
+#include <linux/bpf.h>
 #include "rc-core-priv.h"
 
 /* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
@@ -33,6 +35,7 @@ static int ir_raw_event_thread(void *data)
 					handler->decode(raw->dev, ev);
 			ir_lirc_raw_event(raw->dev, ev);
 			raw->prev_ev = ev;
+			rc_dev_bpf_run(raw->dev);
 		}
 		mutex_unlock(&ir_raw_handler_lock);
 
@@ -623,6 +626,8 @@ void ir_raw_event_unregister(struct rc_dev *dev)
 			handler->raw_unregister(dev);
 	mutex_unlock(&ir_raw_handler_lock);
 
+	rc_dev_bpf_put(dev);
+
 	ir_raw_event_free(dev);
 }
 
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 6ad053e831c0..d9740599adf6 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -155,6 +155,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_INET6_CONNECT,
 	BPF_CGROUP_INET4_POST_BIND,
 	BPF_CGROUP_INET6_POST_BIND,
+	BPF_RAWIR_DECODER,
 	__MAX_BPF_ATTACH_TYPE
 };
 
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 016ef9025827..63ecc1f2e1e3 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -27,6 +27,7 @@
 #include <linux/timekeeping.h>
 #include <linux/ctype.h>
 #include <linux/nospec.h>
+#include <linux/bpf-rcdev.h>
 
 #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
 			   (map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
@@ -1556,6 +1557,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)
 	case BPF_SK_SKB_STREAM_PARSER:
 	case BPF_SK_SKB_STREAM_VERDICT:
 		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, true);
+	case BPF_RAWIR_DECODER:
+		return rc_dev_prog_attach(attr);
 	default:
 		return -EINVAL;
 	}
@@ -1626,6 +1629,8 @@ static int bpf_prog_detach(const union bpf_attr *attr)
 	case BPF_SK_SKB_STREAM_PARSER:
 	case BPF_SK_SKB_STREAM_VERDICT:
 		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, false);
+	case BPF_RAWIR_DECODER:
+		return rc_dev_prog_detach(attr);
 	default:
 		return -EINVAL;
 	}
@@ -1673,6 +1678,8 @@ static int bpf_prog_query(const union bpf_attr *attr,
 	case BPF_CGROUP_SOCK_OPS:
 	case BPF_CGROUP_DEVICE:
 		break;
+	case BPF_RAWIR_DECODER:
+		return rc_dev_prog_query(attr, uattr);
 	default:
 		return -EINVAL;
 	}
-- 
2.17.0

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

* [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi
  2018-05-14 21:10 [PATCH v1 0/4] IR decoding using BPF Sean Young
  2018-05-14 21:10 ` [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER Sean Young
  2018-05-14 21:10 ` [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used Sean Young
@ 2018-05-14 21:11 ` Sean Young
  2018-05-15  1:59   ` kbuild test robot
  2018-05-15  5:26   ` Y Song
  2018-05-14 21:11 ` [PATCH v1 4/4] samples/bpf: an example of a raw IR decoder Sean Young
  3 siblings, 2 replies; 15+ messages in thread
From: Sean Young @ 2018-05-14 21:11 UTC (permalink / raw)
  To: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;
ensure user space has a a definition.

Signed-off-by: Sean Young <sean@mess.org>
---
 include/media/rc-core.h        | 19 +------------------
 include/uapi/linux/bpf_rcdev.h | 24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+), 18 deletions(-)
 create mode 100644 include/uapi/linux/bpf_rcdev.h

diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index 6742fd86ff65..5d31e31d8ade 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -21,6 +21,7 @@
 #include <linux/kfifo.h>
 #include <linux/time.h>
 #include <linux/timer.h>
+#include <uapi/linux/bpf_rcdev.h>
 #include <media/rc-map.h>
 
 /**
@@ -299,24 +300,6 @@ void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol,
 void rc_keyup(struct rc_dev *dev);
 u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode);
 
-/*
- * From rc-raw.c
- * The Raw interface is specific to InfraRed. It may be a good idea to
- * split it later into a separate header.
- */
-struct ir_raw_event {
-	union {
-		u32             duration;
-		u32             carrier;
-	};
-	u8                      duty_cycle;
-
-	unsigned                pulse:1;
-	unsigned                reset:1;
-	unsigned                timeout:1;
-	unsigned                carrier_report:1;
-};
-
 #define DEFINE_IR_RAW_EVENT(event) struct ir_raw_event event = {}
 
 static inline void init_ir_raw_event(struct ir_raw_event *ev)
diff --git a/include/uapi/linux/bpf_rcdev.h b/include/uapi/linux/bpf_rcdev.h
new file mode 100644
index 000000000000..d8629ff2b960
--- /dev/null
+++ b/include/uapi/linux/bpf_rcdev.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (c) 2018 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#ifndef _UAPI__LINUX_BPF_RCDEV_H__
+#define _UAPI__LINUX_BPF_RCDEV_H__
+
+struct ir_raw_event {
+	union {
+		__u32           duration;
+		__u32           carrier;
+	};
+	__u8                    duty_cycle;
+
+	unsigned                pulse:1;
+	unsigned                reset:1;
+	unsigned                timeout:1;
+	unsigned                carrier_report:1;
+};
+
+#endif /* _UAPI__LINUX_BPF_RCDEV_H__ */
-- 
2.17.0

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

* [PATCH v1 4/4] samples/bpf: an example of a raw IR decoder
  2018-05-14 21:10 [PATCH v1 0/4] IR decoding using BPF Sean Young
                   ` (2 preceding siblings ...)
  2018-05-14 21:11 ` [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi Sean Young
@ 2018-05-14 21:11 ` Sean Young
  2018-05-15  5:34   ` Y Song
  3 siblings, 1 reply; 15+ messages in thread
From: Sean Young @ 2018-05-14 21:11 UTC (permalink / raw)
  To: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

This implements the grundig-16 IR protocol.

Signed-off-by: Sean Young <sean@mess.org>
---
 samples/bpf/Makefile                      |   4 +
 samples/bpf/bpf_load.c                    |   9 +-
 samples/bpf/grundig_decoder_kern.c        | 112 ++++++++++++++++++++++
 samples/bpf/grundig_decoder_user.c        |  54 +++++++++++
 tools/bpf/bpftool/prog.c                  |   1 +
 tools/include/uapi/linux/bpf.h            |  17 +++-
 tools/testing/selftests/bpf/bpf_helpers.h |   6 ++
 7 files changed, 200 insertions(+), 3 deletions(-)
 create mode 100644 samples/bpf/grundig_decoder_kern.c
 create mode 100644 samples/bpf/grundig_decoder_user.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 4d6a6edd4bf6..c6fa111f103a 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -44,6 +44,7 @@ hostprogs-y += xdp_monitor
 hostprogs-y += xdp_rxq_info
 hostprogs-y += syscall_tp
 hostprogs-y += cpustat
+hostprogs-y += grundig_decoder
 
 # Libbpf dependencies
 LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
@@ -95,6 +96,7 @@ xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
 xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o
 syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
 cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o
+grundig_decoder-objs := bpf_load.o $(LIBBPF) grundig_decoder_user.o
 
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
@@ -148,6 +150,7 @@ always += xdp_rxq_info_kern.o
 always += xdp2skb_meta_kern.o
 always += syscall_tp_kern.o
 always += cpustat_kern.o
+always += grundig_decoder_kern.o
 
 HOSTCFLAGS += -I$(objtree)/usr/include
 HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -193,6 +196,7 @@ HOSTLOADLIBES_xdp_monitor += -lelf
 HOSTLOADLIBES_xdp_rxq_info += -lelf
 HOSTLOADLIBES_syscall_tp += -lelf
 HOSTLOADLIBES_cpustat += -lelf
+HOSTLOADLIBES_grundig_decoder += -lelf
 
 # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
 #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index bebe4188b4b3..0fd389e95bb9 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -69,6 +69,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 	bool is_sockops = strncmp(event, "sockops", 7) == 0;
 	bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0;
 	bool is_sk_msg = strncmp(event, "sk_msg", 6) == 0;
+	bool is_ir_decoder = strncmp(event, "ir_decoder", 10) == 0;
 	size_t insns_cnt = size / sizeof(struct bpf_insn);
 	enum bpf_prog_type prog_type;
 	char buf[256];
@@ -102,6 +103,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 		prog_type = BPF_PROG_TYPE_SK_SKB;
 	} else if (is_sk_msg) {
 		prog_type = BPF_PROG_TYPE_SK_MSG;
+	} else if (is_ir_decoder) {
+		prog_type = BPF_PROG_TYPE_RAWIR_DECODER;
 	} else {
 		printf("Unknown event '%s'\n", event);
 		return -1;
@@ -116,7 +119,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 
 	prog_fd[prog_cnt++] = fd;
 
-	if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
+	if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
+	    is_ir_decoder)
 		return 0;
 
 	if (is_socket || is_sockops || is_sk_skb || is_sk_msg) {
@@ -607,7 +611,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
 		    memcmp(shname, "cgroup/", 7) == 0 ||
 		    memcmp(shname, "sockops", 7) == 0 ||
 		    memcmp(shname, "sk_skb", 6) == 0 ||
-		    memcmp(shname, "sk_msg", 6) == 0) {
+		    memcmp(shname, "sk_msg", 6) == 0 ||
+		    memcmp(shname, "ir_decoder", 10) == 0) {
 			ret = load_and_attach(shname, data->d_buf,
 					      data->d_size);
 			if (ret != 0)
diff --git a/samples/bpf/grundig_decoder_kern.c b/samples/bpf/grundig_decoder_kern.c
new file mode 100644
index 000000000000..c80f2c9cc69a
--- /dev/null
+++ b/samples/bpf/grundig_decoder_kern.c
@@ -0,0 +1,112 @@
+
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/bpf_rcdev.h>
+#include "bpf_helpers.h"
+#include <linux/version.h>
+
+enum grundig_state {
+	STATE_INACTIVE,
+	STATE_HEADER_SPACE,
+	STATE_LEADING_PULSE,
+	STATE_BITS_SPACE,
+	STATE_BITS_PULSE,
+};
+
+struct decoder_state {
+	u32 bits;
+	enum grundig_state state;
+	u32 count;
+	u32 last_space;
+};
+
+struct bpf_map_def SEC("maps") decoder_state_map = {
+	.type = BPF_MAP_TYPE_ARRAY,
+	.key_size = sizeof(u32),
+	.value_size = sizeof(struct decoder_state),
+	.max_entries = 1,
+};
+
+#define US_TO_NS(t) 1000*(t)
+static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin)
+{
+	return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
+}
+
+SEC("ir_decoder/grundig_decoder")
+int bpf_decoder(struct ir_raw_event *raw)
+{
+	u32 key = 0;
+	struct decoder_state init = {};
+
+	struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
+
+	if (!s)
+		s = &init;
+
+	if (raw->carrier_report) {
+		// ignore
+	} else if (raw->reset) {
+		s->state = STATE_INACTIVE;
+	} else if (s->state == STATE_INACTIVE) {
+		if (raw->pulse && eq_margin(US_TO_NS(900), raw->duration, US_TO_NS(100))) {
+			s->bits = 0;
+			s->state = STATE_HEADER_SPACE;
+			s->count = 0;
+		}
+	} else if (s->state == STATE_HEADER_SPACE) {
+		if (!raw->pulse && eq_margin(US_TO_NS(2900), raw->duration, US_TO_NS(200)))
+			s->state = STATE_LEADING_PULSE;
+		else
+			s->state = STATE_INACTIVE;
+	} else if (s->state == STATE_LEADING_PULSE) {
+		if (raw->pulse && eq_margin(US_TO_NS(1300), raw->duration, US_TO_NS(100)))
+			s->state = STATE_BITS_SPACE;
+		else
+			s->state = STATE_INACTIVE;
+	} else if (s->state == STATE_BITS_SPACE) {
+		s->last_space = raw->duration;
+		s->state = STATE_BITS_PULSE;
+	} else if (s->state == STATE_BITS_PULSE) {
+		int t = -1;
+		if (eq_margin(s->last_space, US_TO_NS(472), US_TO_NS(150)) &&
+		    eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
+			t = 0;
+		if (eq_margin(s->last_space, US_TO_NS(1139), US_TO_NS(150)) &&
+		    eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
+			t = 1;
+		if (eq_margin(s->last_space, US_TO_NS(1806), US_TO_NS(150)) &&
+		    eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
+			t = 2;
+		if (eq_margin(s->last_space, US_TO_NS(2200), US_TO_NS(150)) &&
+		    eq_margin(raw->duration, US_TO_NS(1139), US_TO_NS(150)))
+			t = 3;
+		if (t < 0) {
+			s->state = STATE_INACTIVE;
+		} else {
+			s->bits <<= 2;
+			switch (t) {
+			case 3: s->bits |= 0; break;
+			case 2: s->bits |= 3; break;
+			case 1: s->bits |= 2; break;
+			case 0: s->bits |= 1; break;
+			}
+			s->count += 2;
+			if (s->count == 16) {
+				bpf_rc_keydown(raw, 0x40, s->bits, 0);
+				s->state = STATE_INACTIVE;
+			} else {
+				s->state = STATE_BITS_SPACE;
+			}
+		}
+	}
+
+	if (s == &init)
+		bpf_map_update_elem(&decoder_state_map, &key, s, BPF_NOEXIST);
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+
+u32 _version SEC("version") = LINUX_VERSION_CODE;
+
diff --git a/samples/bpf/grundig_decoder_user.c b/samples/bpf/grundig_decoder_user.c
new file mode 100644
index 000000000000..61e8ee5f73ee
--- /dev/null
+++ b/samples/bpf/grundig_decoder_user.c
@@ -0,0 +1,54 @@
+
+#include <linux/bpf.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "bpf_load.h"
+#include "bpf_util.h"
+#include "libbpf.h"
+
+int main(int argc, char **argv)
+{
+	char filename[256];
+	int ret, lircfd;
+
+	if (argc != 2) {
+		printf("Usage: %s /dev/lircN\n", argv[0]);
+		return 2;
+	}
+
+	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+
+	if (load_bpf_file(filename)) {
+		printf("%s", bpf_log_buf);
+		return 1;
+	}
+
+	lircfd = open(argv[1], O_RDWR);
+	if (lircfd == -1) {
+		printf("failed to open lirc device %s: %m\n", argv[1]);
+		return 1;
+	}
+
+	ret = bpf_prog_attach(prog_fd[0], lircfd, BPF_RAWIR_DECODER, 0);
+	if (ret) {
+		printf("Failed to attach bpf to lirc device: %m\n");
+		return 1;
+	}
+
+	printf("Grundig IR decoder loaded and attached. Hit any key to stop\n");
+	getchar();
+
+	return 0;
+}
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index f7a810897eac..ae1c26df212d 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -68,6 +68,7 @@ static const char * const prog_type_name[] = {
 	[BPF_PROG_TYPE_SOCK_OPS]	= "sock_ops",
 	[BPF_PROG_TYPE_SK_SKB]		= "sk_skb",
 	[BPF_PROG_TYPE_CGROUP_DEVICE]	= "cgroup_device",
+	[BPF_PROG_TYPE_RAWIR_DECODER]	= "ir_decoder",
 };
 
 static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index c5ec89732a8d..d9740599adf6 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -137,6 +137,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_SK_MSG,
 	BPF_PROG_TYPE_RAW_TRACEPOINT,
 	BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+	BPF_PROG_TYPE_RAWIR_DECODER,
 };
 
 enum bpf_attach_type {
@@ -154,6 +155,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_INET6_CONNECT,
 	BPF_CGROUP_INET4_POST_BIND,
 	BPF_CGROUP_INET6_POST_BIND,
+	BPF_RAWIR_DECODER,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -755,6 +757,17 @@ union bpf_attr {
  *     @addr: pointer to struct sockaddr to bind socket to
  *     @addr_len: length of sockaddr structure
  *     Return: 0 on success or negative error code
+ *
+ * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
+ *	Report decoded scancode with toggle value
+ *	@ctx: pointer to ctx
+ *	@protocol: decoded protocol
+ *	@scancode: decoded scancode
+ *	@toggle: set to 1 if button was toggled, else 0
+ *
+ * int bpf_rc_repeat(ctx)
+ *	Repeat the last decoded scancode
+ *	@ctx: pointer to ctx
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -821,7 +834,9 @@ union bpf_attr {
 	FN(msg_apply_bytes),		\
 	FN(msg_cork_bytes),		\
 	FN(msg_pull_data),		\
-	FN(bind),
+	FN(bind),			\
+	FN(rc_repeat),			\
+	FN(rc_keydown),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
index d8223d99f96d..4bf23d3dfc33 100644
--- a/tools/testing/selftests/bpf/bpf_helpers.h
+++ b/tools/testing/selftests/bpf/bpf_helpers.h
@@ -96,6 +96,12 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
 	(void *) BPF_FUNC_msg_pull_data;
 static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
 	(void *) BPF_FUNC_bind;
+static int (*bpf_rc_repeat)(void *ctx) =
+	(void *) BPF_FUNC_rc_repeat;
+static int (*bpf_rc_keydown)(void *ctx, unsigned protocol, unsigned scancode,
+			     unsigned toggle) =
+	(void *) BPF_FUNC_rc_keydown;
+
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
-- 
2.17.0

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

* Re: [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER
  2018-05-14 21:10 ` [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER Sean Young
@ 2018-05-14 23:27   ` Randy Dunlap
  2018-05-15 12:19     ` Sean Young
  2018-05-15  4:48   ` Y Song
  1 sibling, 1 reply; 15+ messages in thread
From: Randy Dunlap @ 2018-05-14 23:27 UTC (permalink / raw)
  To: Sean Young, linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On 05/14/2018 02:10 PM, Sean Young wrote:
> Add support for BPF_PROG_IR_DECODER. This type of BPF program can call

Kconfig file below uses IR_BPF_DECODER instead of the symbol name above.

and then patch 3 says a third choice:
The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;

> rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> that the last key should be repeated.
> 
> Signed-off-by: Sean Young <sean@mess.org>
> ---
>  drivers/media/rc/Kconfig          |  8 +++
>  drivers/media/rc/Makefile         |  1 +
>  drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
>  include/linux/bpf_types.h         |  3 +
>  include/uapi/linux/bpf.h          | 16 +++++-
>  5 files changed, 120 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/rc/ir-bpf-decoder.c
> 
> diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> index eb2c3b6eca7f..10ad6167d87c 100644
> --- a/drivers/media/rc/Kconfig
> +++ b/drivers/media/rc/Kconfig
> @@ -120,6 +120,14 @@ config IR_IMON_DECODER
>  	   remote control and you would like to use it with a raw IR
>  	   receiver, or if you wish to use an encoder to transmit this IR.
>  
> +config IR_BPF_DECODER
> +	bool "Enable IR raw decoder using BPF"
> +	depends on BPF_SYSCALL
> +	depends on RC_CORE=y
> +	help
> +	   Enable this option to make it possible to load custom IR
> +	   decoders written in BPF.
> +
>  endif #RC_DECODERS
>  
>  menuconfig RC_DEVICES
> diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> index 2e1c87066f6c..12e1118430d0 100644
> --- a/drivers/media/rc/Makefile
> +++ b/drivers/media/rc/Makefile
> @@ -5,6 +5,7 @@ obj-y += keymaps/
>  obj-$(CONFIG_RC_CORE) += rc-core.o
>  rc-core-y := rc-main.o rc-ir-raw.o
>  rc-core-$(CONFIG_LIRC) += lirc_dev.o
> +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o


-- 
~Randy

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

* Re: [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi
  2018-05-14 21:11 ` [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi Sean Young
@ 2018-05-15  1:59   ` kbuild test robot
  2018-05-15  5:26   ` Y Song
  1 sibling, 0 replies; 15+ messages in thread
From: kbuild test robot @ 2018-05-15  1:59 UTC (permalink / raw)
  To: Sean Young
  Cc: kbuild-all, linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

[-- Attachment #1: Type: text/plain, Size: 870 bytes --]

Hi Sean,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v4.17-rc5]
[cannot apply to next-20180514]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Sean-Young/media-rc-introduce-BPF_PROG_IR_DECODER/20180515-093234
config: i386-tinyconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

>> ./usr/include/linux/bpf_rcdev.h:13: found __[us]{8,16,32,64} type without #include <linux/types.h>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6302 bytes --]

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

* Re: [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used
  2018-05-14 21:10 ` [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used Sean Young
@ 2018-05-15  2:34   ` kbuild test robot
  2018-05-15  5:22   ` Y Song
  1 sibling, 0 replies; 15+ messages in thread
From: kbuild test robot @ 2018-05-15  2:34 UTC (permalink / raw)
  To: Sean Young
  Cc: kbuild-all, linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

[-- Attachment #1: Type: text/plain, Size: 1224 bytes --]

Hi Sean,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.17-rc5]
[cannot apply to next-20180514]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Sean-Young/media-rc-introduce-BPF_PROG_IR_DECODER/20180515-093234
config: i386-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

>> drivers/media/rc/lirc_dev.c:32:10: fatal error: linux/bpf-rcdev.h: No such file or directory
    #include <linux/bpf-rcdev.h>
             ^~~~~~~~~~~~~~~~~~~
   compilation terminated.
--
>> kernel/bpf/syscall.c:30:10: fatal error: linux/bpf-rcdev.h: No such file or directory
    #include <linux/bpf-rcdev.h>
             ^~~~~~~~~~~~~~~~~~~
   compilation terminated.

vim +32 drivers/media/rc/lirc_dev.c

    31	
  > 32	#include <linux/bpf-rcdev.h>
    33	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 62940 bytes --]

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

* Re: [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER
  2018-05-14 21:10 ` [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER Sean Young
  2018-05-14 23:27   ` Randy Dunlap
@ 2018-05-15  4:48   ` Y Song
  2018-05-15 10:30     ` Sean Young
  1 sibling, 1 reply; 15+ messages in thread
From: Y Song @ 2018-05-15  4:48 UTC (permalink / raw)
  To: Sean Young
  Cc: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On Mon, May 14, 2018 at 2:10 PM, Sean Young <sean@mess.org> wrote:
> Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
> rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> that the last key should be repeated.
>
> Signed-off-by: Sean Young <sean@mess.org>
> ---
>  drivers/media/rc/Kconfig          |  8 +++
>  drivers/media/rc/Makefile         |  1 +
>  drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
>  include/linux/bpf_types.h         |  3 +
>  include/uapi/linux/bpf.h          | 16 +++++-
>  5 files changed, 120 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/rc/ir-bpf-decoder.c
>
> diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> index eb2c3b6eca7f..10ad6167d87c 100644
> --- a/drivers/media/rc/Kconfig
> +++ b/drivers/media/rc/Kconfig
> @@ -120,6 +120,14 @@ config IR_IMON_DECODER
>            remote control and you would like to use it with a raw IR
>            receiver, or if you wish to use an encoder to transmit this IR.
>
> +config IR_BPF_DECODER
> +       bool "Enable IR raw decoder using BPF"
> +       depends on BPF_SYSCALL
> +       depends on RC_CORE=y
> +       help
> +          Enable this option to make it possible to load custom IR
> +          decoders written in BPF.
> +
>  endif #RC_DECODERS
>
>  menuconfig RC_DEVICES
> diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> index 2e1c87066f6c..12e1118430d0 100644
> --- a/drivers/media/rc/Makefile
> +++ b/drivers/media/rc/Makefile
> @@ -5,6 +5,7 @@ obj-y += keymaps/
>  obj-$(CONFIG_RC_CORE) += rc-core.o
>  rc-core-y := rc-main.o rc-ir-raw.o
>  rc-core-$(CONFIG_LIRC) += lirc_dev.o
> +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
>  obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
>  obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
>  obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
> diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
> new file mode 100644
> index 000000000000..aaa5e208b1a5
> --- /dev/null
> +++ b/drivers/media/rc/ir-bpf-decoder.c
> @@ -0,0 +1,93 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// ir-bpf-decoder.c - handles bpf decoders
> +//
> +// Copyright (C) 2018 Sean Young <sean@mess.org>
> +
> +#include <linux/bpf.h>
> +#include <linux/filter.h>
> +#include "rc-core-priv.h"
> +
> +/*
> + * BPF interface for raw IR decoder
> + */
> +const struct bpf_prog_ops ir_decoder_prog_ops = {
> +};
> +
> +BPF_CALL_1(bpf_rc_repeat, struct ir_raw_event*, event)
> +{
> +       struct ir_raw_event_ctrl *ctrl;
> +
> +       ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> +
> +       rc_repeat(ctrl->dev);
> +       return 0;
> +}
> +
> +static const struct bpf_func_proto rc_repeat_proto = {
> +       .func      = bpf_rc_repeat,
> +       .gpl_only  = true, // rc_repeat is EXPORT_SYMBOL_GPL
> +       .ret_type  = RET_VOID,

I suggest using RET_INTEGER here since we do return an integer 0.
RET_INTEGER will also make it easy to extend if you want to return
a non-zero value for error code or other reasons.

> +       .arg1_type = ARG_PTR_TO_CTX,
> +};
> +
> +BPF_CALL_4(bpf_rc_keydown, struct ir_raw_event*, event, u32, protocol,
> +          u32, scancode, u32, toggle)
> +{
> +       struct ir_raw_event_ctrl *ctrl;
> +
> +       ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> +       rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
> +       return 0;
> +}
> +
> +static const struct bpf_func_proto rc_keydown_proto = {
> +       .func      = bpf_rc_keydown,
> +       .gpl_only  = true, // rc_keydown is EXPORT_SYMBOL_GPL
> +       .ret_type  = RET_VOID,

ditto. RET_INTEGER is preferable.

> +       .arg1_type = ARG_PTR_TO_CTX,
> +       .arg2_type = ARG_ANYTHING,
> +       .arg3_type = ARG_ANYTHING,
> +       .arg4_type = ARG_ANYTHING,
> +};
> +
> +static const struct bpf_func_proto *ir_decoder_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
> +{
> +       switch (func_id) {
> +       case BPF_FUNC_rc_repeat:
> +               return &rc_repeat_proto;
> +       case BPF_FUNC_rc_keydown:
> +               return &rc_keydown_proto;
> +       case BPF_FUNC_map_lookup_elem:
> +               return &bpf_map_lookup_elem_proto;
> +       case BPF_FUNC_map_update_elem:
> +               return &bpf_map_update_elem_proto;
> +       case BPF_FUNC_map_delete_elem:
> +               return &bpf_map_delete_elem_proto;
> +       case BPF_FUNC_ktime_get_ns:
> +               return &bpf_ktime_get_ns_proto;
> +       case BPF_FUNC_tail_call:
> +               return &bpf_tail_call_proto;
> +       case BPF_FUNC_get_prandom_u32:
> +               return &bpf_get_prandom_u32_proto;
> +       default:
> +               return NULL;
> +       }
> +}
> +
> +static bool ir_decoder_is_valid_access(int off, int size,
> +                                      enum bpf_access_type type,
> +                                      const struct bpf_prog *prog,
> +                                      struct bpf_insn_access_aux *info)
> +{
> +       if (type == BPF_WRITE)
> +               return false;
> +       if (off < 0 || off + size > sizeof(struct ir_raw_event))
> +               return false;

You probably need more than just checking the boundary.
>From patch #3, the structure is:
 struct ir_raw_event {
        union {
                __u32           duration;
                __u32           carrier;
        };
        __u8                    duty_cycle;

        unsigned                pulse:1;
        unsigned                reset:1;
        unsigned                timeout:1;
       unsigned                carrier_report:1;
};

You would like the memory access to be aligned,
so accessing duration/carrier with 4-byte alignment, and
pulse/reset/timeout/carrier_report 4-byte alignment as well.

You could only allow __u32 access to duration/carrier.
But if you want bpf program to access duration/carrier with
code like (__u16)(ctx->duration), then the compiler may
translate the load to a 2-byte load. You may need to handle
endianness here. You can check net/core/filter.c function
bpf_skb_is_valid_access for some examples.

> +
> +       return true;
> +}
> +
> +const struct bpf_verifier_ops ir_decoder_verifier_ops = {
> +       .get_func_proto  = ir_decoder_func_proto,
> +       .is_valid_access = ir_decoder_is_valid_access
> +};
> diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> index 2b28fcf6f6ae..ee5355715ee0 100644
> --- a/include/linux/bpf_types.h
> +++ b/include/linux/bpf_types.h
> @@ -25,6 +25,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
>  #ifdef CONFIG_CGROUP_BPF
>  BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
>  #endif
> +#ifdef CONFIG_IR_BPF_DECODER
> +BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_DECODER, ir_decoder)
> +#endif
>
>  BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
>  BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index c5ec89732a8d..6ad053e831c0 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -137,6 +137,7 @@ enum bpf_prog_type {
>         BPF_PROG_TYPE_SK_MSG,
>         BPF_PROG_TYPE_RAW_TRACEPOINT,
>         BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> +       BPF_PROG_TYPE_RAWIR_DECODER,
>  };
>
>  enum bpf_attach_type {
> @@ -755,6 +756,17 @@ union bpf_attr {
>   *     @addr: pointer to struct sockaddr to bind socket to
>   *     @addr_len: length of sockaddr structure
>   *     Return: 0 on success or negative error code
> + *
> + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> + *     Report decoded scancode with toggle value
> + *     @ctx: pointer to ctx
> + *     @protocol: decoded protocol
> + *     @scancode: decoded scancode
> + *     @toggle: set to 1 if button was toggled, else 0
> + *
> + * int bpf_rc_repeat(ctx)
> + *     Repeat the last decoded scancode
> + *     @ctx: pointer to ctx

The comment format has changed dramatically for
documentation reason. Could you rebase your change
on top of bpf-next tree? You will need to rewrite the above
helper description so tools can generate proper documentation
for them.

>   */
>  #define __BPF_FUNC_MAPPER(FN)          \
>         FN(unspec),                     \
> @@ -821,7 +833,9 @@ union bpf_attr {
>         FN(msg_apply_bytes),            \
>         FN(msg_cork_bytes),             \
>         FN(msg_pull_data),              \
> -       FN(bind),
> +       FN(bind),                       \
> +       FN(rc_repeat),                  \
> +       FN(rc_keydown),
>
>  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
>   * function eBPF program intends to call
> --
> 2.17.0
>

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

* Re: [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used
  2018-05-14 21:10 ` [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used Sean Young
  2018-05-15  2:34   ` kbuild test robot
@ 2018-05-15  5:22   ` Y Song
  1 sibling, 0 replies; 15+ messages in thread
From: Y Song @ 2018-05-15  5:22 UTC (permalink / raw)
  To: Sean Young
  Cc: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On Mon, May 14, 2018 at 2:10 PM, Sean Young <sean@mess.org> wrote:
> This implements attaching, detaching, querying and execution. The target
> fd has to be the /dev/lircN device.
>
> Signed-off-by: Sean Young <sean@mess.org>
> ---
>  drivers/media/rc/ir-bpf-decoder.c | 191 ++++++++++++++++++++++++++++++
>  drivers/media/rc/lirc_dev.c       |  30 +++++
>  drivers/media/rc/rc-core-priv.h   |  15 +++
>  drivers/media/rc/rc-ir-raw.c      |   5 +
>  include/uapi/linux/bpf.h          |   1 +
>  kernel/bpf/syscall.c              |   7 ++
>  6 files changed, 249 insertions(+)
>
> diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
> index aaa5e208b1a5..651590a14772 100644
> --- a/drivers/media/rc/ir-bpf-decoder.c
> +++ b/drivers/media/rc/ir-bpf-decoder.c
> @@ -91,3 +91,194 @@ const struct bpf_verifier_ops ir_decoder_verifier_ops = {
>         .get_func_proto  = ir_decoder_func_proto,
>         .is_valid_access = ir_decoder_is_valid_access
>  };
> +
> +#define BPF_MAX_PROGS 64
> +
> +int rc_dev_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)

flags is not used in this function.

> +{
> +       struct ir_raw_event_ctrl *raw;
> +       struct bpf_prog_array __rcu *old_array;
> +       struct bpf_prog_array *new_array;
> +       int ret;
> +
> +       if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> +               return -EINVAL;
> +
> +       ret = mutex_lock_interruptible(&rcdev->lock);
> +       if (ret)
> +               return ret;
> +
> +       raw = rcdev->raw;
> +
> +       if (raw->progs && bpf_prog_array_length(raw->progs) >= BPF_MAX_PROGS) {
> +               ret = -E2BIG;
> +               goto out;
> +       }
> +
> +       old_array = raw->progs;
> +       ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array);
> +       if (ret < 0)
> +               goto out;
> +
> +       rcu_assign_pointer(raw->progs, new_array);
> +       bpf_prog_array_free(old_array);
> +out:
> +       mutex_unlock(&rcdev->lock);
> +       return ret;
> +}
> +
> +int rc_dev_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)

flags is not used in this function.

> +{
> +       struct ir_raw_event_ctrl *raw;
> +       struct bpf_prog_array __rcu *old_array;
> +       struct bpf_prog_array *new_array;
> +       int ret;
> +
> +       if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> +               return -EINVAL;
> +
> +       ret = mutex_lock_interruptible(&rcdev->lock);
> +       if (ret)
> +               return ret;
> +
> +       raw = rcdev->raw;
> +
> +       old_array = raw->progs;
> +       ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array);
> +       if (ret < 0) {
> +               bpf_prog_array_delete_safe(old_array, prog);
> +       } else {
> +               rcu_assign_pointer(raw->progs, new_array);
> +               bpf_prog_array_free(old_array);
> +       }
> +
> +       bpf_prog_put(prog);
> +       mutex_unlock(&rcdev->lock);
> +       return 0;
> +}
> +
> +void rc_dev_bpf_run(struct rc_dev *rcdev)
> +{
> +       struct ir_raw_event_ctrl *raw = rcdev->raw;
> +
> +       if (raw->progs)
> +               BPF_PROG_RUN_ARRAY(raw->progs, &raw->prev_ev, BPF_PROG_RUN);
> +}
> +
> +void rc_dev_bpf_put(struct rc_dev *rcdev)
> +{
> +       struct bpf_prog_array *progs = rcdev->raw->progs;
> +       int i, size;
> +
> +       if (!progs)
> +               return;
> +
> +       size = bpf_prog_array_length(progs);
> +       for (i = 0; i < size; i++)
> +               bpf_prog_put(progs->progs[i]);
> +
> +       bpf_prog_array_free(rcdev->raw->progs);
> +}
> +
> +int rc_dev_prog_attach(const union bpf_attr *attr)
> +{
> +       struct bpf_prog *prog;
> +       struct rc_dev *rcdev;
> +       int ret;
> +
> +       if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
> +               return -EINVAL;

Looks like you really did not use flags except here.
BPF_F_ALLOW_OVERRIDE is originally used for
cgroup type of attachment and the comment explicits
saying so.

In the query below, the flags value "0" is copied to userspace.

In your case, I think you can just disallow any value, i.g.,
attr->attach_flags must be 0, and then you further down
check that if the prog is already in the array, you just return an error.

> +
> +       prog = bpf_prog_get_type(attr->attach_bpf_fd,
> +                                BPF_PROG_TYPE_RAWIR_DECODER);
> +       if (IS_ERR(prog))
> +               return PTR_ERR(prog);
> +
> +       rcdev = rc_dev_get_from_fd(attr->target_fd);
> +       if (IS_ERR(rcdev)) {
> +               bpf_prog_put(prog);
> +               return PTR_ERR(rcdev);
> +       }
> +
> +       ret = rc_dev_bpf_attach(rcdev, prog, attr->attach_flags);
> +       if (ret)
> +               bpf_prog_put(prog);
> +
> +       put_device(&rcdev->dev);
> +
> +       return ret;
> +}
> +
> +int rc_dev_prog_detach(const union bpf_attr *attr)
> +{
> +       struct bpf_prog *prog;
> +       struct rc_dev *rcdev;
> +       int ret;
> +
> +       if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
> +               return -EINVAL;
> +
> +       prog = bpf_prog_get_type(attr->attach_bpf_fd,
> +                                BPF_PROG_TYPE_RAWIR_DECODER);
> +       if (IS_ERR(prog))
> +               return PTR_ERR(prog);
> +
> +       rcdev = rc_dev_get_from_fd(attr->target_fd);
> +       if (IS_ERR(rcdev)) {
> +               bpf_prog_put(prog);
> +               return PTR_ERR(rcdev);
> +       }
> +
> +       ret = rc_dev_bpf_detach(rcdev, prog, attr->attach_flags);
> +
> +       bpf_prog_put(prog);
> +       put_device(&rcdev->dev);
> +
> +       return ret;
> +}
> +
> +int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
> +{
> +       __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
> +       struct bpf_prog_array *progs;
> +       struct rc_dev *rcdev;
> +       u32 cnt, flags = 0;
> +       int ret;
> +
> +       if (attr->query.query_flags)
> +               return -EINVAL;
> +
> +       rcdev = rc_dev_get_from_fd(attr->query.target_fd);
> +       if (IS_ERR(rcdev))
> +               return PTR_ERR(rcdev);
> +
> +       if (rcdev->driver_type != RC_DRIVER_IR_RAW) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       ret = mutex_lock_interruptible(&rcdev->lock);
> +       if (ret)
> +               goto out;
> +
> +       progs = rcdev->raw->progs;
> +       cnt = progs ? bpf_prog_array_length(progs) : 0;
> +
> +       if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) {
> +               ret = -EFAULT;
> +               goto out;
> +       }
> +       if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) {
> +               ret = -EFAULT;
> +               goto out;
> +       }
> +
> +       if (attr->query.prog_cnt != 0 && prog_ids && cnt)
> +               ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt);
> +
> +out:
> +       mutex_unlock(&rcdev->lock);
> +       put_device(&rcdev->dev);
> +
> +       return ret;
> +}
> diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
> index 24e9fbb80e81..65319f2ccc13 100644
> --- a/drivers/media/rc/lirc_dev.c
> +++ b/drivers/media/rc/lirc_dev.c
> @@ -20,6 +20,7 @@
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  #include <linux/device.h>
> +#include <linux/file.h>
>  #include <linux/idr.h>
>  #include <linux/poll.h>
>  #include <linux/sched.h>
> @@ -28,6 +29,8 @@
>  #include "rc-core-priv.h"
>  #include <uapi/linux/lirc.h>
>
> +#include <linux/bpf-rcdev.h>
> +
>  #define LIRCBUF_SIZE   256
>
>  static dev_t lirc_base_dev;
> @@ -816,4 +819,31 @@ void __exit lirc_dev_exit(void)
>         unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
>  }
>
> +struct rc_dev *rc_dev_get_from_fd(int fd)
> +{
> +       struct rc_dev *dev;
> +       struct file *f;
> +
> +       f = fget_raw(fd);
> +       if (!f)
> +               return ERR_PTR(-EBADF);
> +
> +       if (!S_ISCHR(f->f_inode->i_mode) ||
> +           imajor(f->f_inode) != MAJOR(lirc_base_dev)) {
> +               fput(f);
> +               return ERR_PTR(-EBADF);
> +       }
> +
> +       dev = container_of(f->f_inode->i_cdev, struct rc_dev, lirc_cdev);
> +       if (!dev->registered) {
> +               fput(f);
> +               return ERR_PTR(-ENODEV);
> +       }
> +
> +       get_device(&dev->dev);
> +       fput(f);
> +
> +       return dev;
> +}
> +
>  MODULE_ALIAS("lirc_dev");
> diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
> index e0e6a17460f6..b6f24f369657 100644
> --- a/drivers/media/rc/rc-core-priv.h
> +++ b/drivers/media/rc/rc-core-priv.h
> @@ -57,6 +57,9 @@ struct ir_raw_event_ctrl {
>         /* raw decoder state follows */
>         struct ir_raw_event prev_ev;
>         struct ir_raw_event this_ev;
> +#ifdef CONFIG_IR_BPF_DECODER
> +       struct bpf_prog_array *progs;
> +#endif
>         struct nec_dec {
>                 int state;
>                 unsigned count;
> @@ -288,6 +291,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
>  void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
>  int ir_lirc_register(struct rc_dev *dev);
>  void ir_lirc_unregister(struct rc_dev *dev);
> +struct rc_dev *rc_dev_get_from_fd(int fd);
>  #else
>  static inline int lirc_dev_init(void) { return 0; }
>  static inline void lirc_dev_exit(void) {}
> @@ -299,4 +303,15 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
>  static inline void ir_lirc_unregister(struct rc_dev *dev) { }
>  #endif
>
> +/*
> + * bpf interface
> + */
> +#ifdef CONFIG_IR_BPF_DECODER
> +void rc_dev_bpf_put(struct rc_dev *dev);
> +void rc_dev_bpf_run(struct rc_dev *dev);
> +#else
> +void rc_dev_bpf_put(struct rc_dev *dev) {}
> +void rc_dev_bpf_run(struct rc_dev *dev) {}
> +#endif
> +
>  #endif /* _RC_CORE_PRIV */
> diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
> index 374f83105a23..efddd9c44466 100644
> --- a/drivers/media/rc/rc-ir-raw.c
> +++ b/drivers/media/rc/rc-ir-raw.c
> @@ -8,6 +8,8 @@
>  #include <linux/mutex.h>
>  #include <linux/kmod.h>
>  #include <linux/sched.h>
> +#include <linux/filter.h>
> +#include <linux/bpf.h>
>  #include "rc-core-priv.h"
>
>  /* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
> @@ -33,6 +35,7 @@ static int ir_raw_event_thread(void *data)
>                                         handler->decode(raw->dev, ev);
>                         ir_lirc_raw_event(raw->dev, ev);
>                         raw->prev_ev = ev;
> +                       rc_dev_bpf_run(raw->dev);
>                 }
>                 mutex_unlock(&ir_raw_handler_lock);
>
> @@ -623,6 +626,8 @@ void ir_raw_event_unregister(struct rc_dev *dev)
>                         handler->raw_unregister(dev);
>         mutex_unlock(&ir_raw_handler_lock);
>
> +       rc_dev_bpf_put(dev);
> +
>         ir_raw_event_free(dev);
>  }
>
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 6ad053e831c0..d9740599adf6 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -155,6 +155,7 @@ enum bpf_attach_type {
>         BPF_CGROUP_INET6_CONNECT,
>         BPF_CGROUP_INET4_POST_BIND,
>         BPF_CGROUP_INET6_POST_BIND,
> +       BPF_RAWIR_DECODER,
>         __MAX_BPF_ATTACH_TYPE
>  };
>
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 016ef9025827..63ecc1f2e1e3 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -27,6 +27,7 @@
>  #include <linux/timekeeping.h>
>  #include <linux/ctype.h>
>  #include <linux/nospec.h>
> +#include <linux/bpf-rcdev.h>
>
>  #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
>                            (map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
> @@ -1556,6 +1557,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)
>         case BPF_SK_SKB_STREAM_PARSER:
>         case BPF_SK_SKB_STREAM_VERDICT:
>                 return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, true);
> +       case BPF_RAWIR_DECODER:
> +               return rc_dev_prog_attach(attr);
>         default:
>                 return -EINVAL;
>         }
> @@ -1626,6 +1629,8 @@ static int bpf_prog_detach(const union bpf_attr *attr)
>         case BPF_SK_SKB_STREAM_PARSER:
>         case BPF_SK_SKB_STREAM_VERDICT:
>                 return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, false);
> +       case BPF_RAWIR_DECODER:
> +               return rc_dev_prog_detach(attr);
>         default:
>                 return -EINVAL;
>         }
> @@ -1673,6 +1678,8 @@ static int bpf_prog_query(const union bpf_attr *attr,
>         case BPF_CGROUP_SOCK_OPS:
>         case BPF_CGROUP_DEVICE:
>                 break;
> +       case BPF_RAWIR_DECODER:
> +               return rc_dev_prog_query(attr, uattr);
>         default:
>                 return -EINVAL;
>         }
> --
> 2.17.0
>

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

* Re: [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi
  2018-05-14 21:11 ` [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi Sean Young
  2018-05-15  1:59   ` kbuild test robot
@ 2018-05-15  5:26   ` Y Song
  1 sibling, 0 replies; 15+ messages in thread
From: Y Song @ 2018-05-15  5:26 UTC (permalink / raw)
  To: Sean Young
  Cc: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On Mon, May 14, 2018 at 2:11 PM, Sean Young <sean@mess.org> wrote:
> The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;
> ensure user space has a a definition.
>
> Signed-off-by: Sean Young <sean@mess.org>
> ---
>  include/media/rc-core.h        | 19 +------------------
>  include/uapi/linux/bpf_rcdev.h | 24 ++++++++++++++++++++++++

Patch #2 already referenced this file. So if Patches #1 and #2
applied, there will be
a compilation error. Could you re-arrange your patchset so that after
sequentially
applying each patch, there is no compilation error?

>  2 files changed, 25 insertions(+), 18 deletions(-)
>  create mode 100644 include/uapi/linux/bpf_rcdev.h
>
> diff --git a/include/media/rc-core.h b/include/media/rc-core.h
> index 6742fd86ff65..5d31e31d8ade 100644
> --- a/include/media/rc-core.h
> +++ b/include/media/rc-core.h
> @@ -21,6 +21,7 @@
>  #include <linux/kfifo.h>
>  #include <linux/time.h>
>  #include <linux/timer.h>
> +#include <uapi/linux/bpf_rcdev.h>
>  #include <media/rc-map.h>
>
>  /**
> @@ -299,24 +300,6 @@ void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol,
>  void rc_keyup(struct rc_dev *dev);
>  u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode);
>
> -/*
> - * From rc-raw.c
> - * The Raw interface is specific to InfraRed. It may be a good idea to
> - * split it later into a separate header.
> - */
> -struct ir_raw_event {
> -       union {
> -               u32             duration;
> -               u32             carrier;
> -       };
> -       u8                      duty_cycle;
> -
> -       unsigned                pulse:1;
> -       unsigned                reset:1;
> -       unsigned                timeout:1;
> -       unsigned                carrier_report:1;
> -};
> -
>  #define DEFINE_IR_RAW_EVENT(event) struct ir_raw_event event = {}
>
>  static inline void init_ir_raw_event(struct ir_raw_event *ev)
> diff --git a/include/uapi/linux/bpf_rcdev.h b/include/uapi/linux/bpf_rcdev.h
> new file mode 100644
> index 000000000000..d8629ff2b960
> --- /dev/null
> +++ b/include/uapi/linux/bpf_rcdev.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/* Copyright (c) 2018 Sean Young <sean@mess.org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + */
> +#ifndef _UAPI__LINUX_BPF_RCDEV_H__
> +#define _UAPI__LINUX_BPF_RCDEV_H__
> +
> +struct ir_raw_event {
> +       union {
> +               __u32           duration;
> +               __u32           carrier;
> +       };
> +       __u8                    duty_cycle;
> +
> +       unsigned                pulse:1;
> +       unsigned                reset:1;
> +       unsigned                timeout:1;
> +       unsigned                carrier_report:1;
> +};
> +
> +#endif /* _UAPI__LINUX_BPF_RCDEV_H__ */
> --
> 2.17.0
>

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

* Re: [PATCH v1 4/4] samples/bpf: an example of a raw IR decoder
  2018-05-14 21:11 ` [PATCH v1 4/4] samples/bpf: an example of a raw IR decoder Sean Young
@ 2018-05-15  5:34   ` Y Song
  2018-05-15 10:40     ` Sean Young
  0 siblings, 1 reply; 15+ messages in thread
From: Y Song @ 2018-05-15  5:34 UTC (permalink / raw)
  To: Sean Young
  Cc: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On Mon, May 14, 2018 at 2:11 PM, Sean Young <sean@mess.org> wrote:
> This implements the grundig-16 IR protocol.
>
> Signed-off-by: Sean Young <sean@mess.org>
> ---
>  samples/bpf/Makefile                      |   4 +
>  samples/bpf/bpf_load.c                    |   9 +-
>  samples/bpf/grundig_decoder_kern.c        | 112 ++++++++++++++++++++++
>  samples/bpf/grundig_decoder_user.c        |  54 +++++++++++
>  tools/bpf/bpftool/prog.c                  |   1 +
>  tools/include/uapi/linux/bpf.h            |  17 +++-
>  tools/testing/selftests/bpf/bpf_helpers.h |   6 ++
>  7 files changed, 200 insertions(+), 3 deletions(-)
>  create mode 100644 samples/bpf/grundig_decoder_kern.c
>  create mode 100644 samples/bpf/grundig_decoder_user.c
>
> diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
> index 4d6a6edd4bf6..c6fa111f103a 100644
> --- a/samples/bpf/Makefile
> +++ b/samples/bpf/Makefile
> @@ -44,6 +44,7 @@ hostprogs-y += xdp_monitor
>  hostprogs-y += xdp_rxq_info
>  hostprogs-y += syscall_tp
>  hostprogs-y += cpustat
> +hostprogs-y += grundig_decoder
>
>  # Libbpf dependencies
>  LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
> @@ -95,6 +96,7 @@ xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
>  xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o
>  syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
>  cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o
> +grundig_decoder-objs := bpf_load.o $(LIBBPF) grundig_decoder_user.o
>
>  # Tell kbuild to always build the programs
>  always := $(hostprogs-y)
> @@ -148,6 +150,7 @@ always += xdp_rxq_info_kern.o
>  always += xdp2skb_meta_kern.o
>  always += syscall_tp_kern.o
>  always += cpustat_kern.o
> +always += grundig_decoder_kern.o
>
>  HOSTCFLAGS += -I$(objtree)/usr/include
>  HOSTCFLAGS += -I$(srctree)/tools/lib/
> @@ -193,6 +196,7 @@ HOSTLOADLIBES_xdp_monitor += -lelf
>  HOSTLOADLIBES_xdp_rxq_info += -lelf
>  HOSTLOADLIBES_syscall_tp += -lelf
>  HOSTLOADLIBES_cpustat += -lelf
> +HOSTLOADLIBES_grundig_decoder += -lelf
>
>  # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
>  #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
> diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
> index bebe4188b4b3..0fd389e95bb9 100644
> --- a/samples/bpf/bpf_load.c
> +++ b/samples/bpf/bpf_load.c
> @@ -69,6 +69,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>         bool is_sockops = strncmp(event, "sockops", 7) == 0;
>         bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0;
>         bool is_sk_msg = strncmp(event, "sk_msg", 6) == 0;
> +       bool is_ir_decoder = strncmp(event, "ir_decoder", 10) == 0;
>         size_t insns_cnt = size / sizeof(struct bpf_insn);
>         enum bpf_prog_type prog_type;
>         char buf[256];
> @@ -102,6 +103,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>                 prog_type = BPF_PROG_TYPE_SK_SKB;
>         } else if (is_sk_msg) {
>                 prog_type = BPF_PROG_TYPE_SK_MSG;
> +       } else if (is_ir_decoder) {
> +               prog_type = BPF_PROG_TYPE_RAWIR_DECODER;
>         } else {
>                 printf("Unknown event '%s'\n", event);
>                 return -1;
> @@ -116,7 +119,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>
>         prog_fd[prog_cnt++] = fd;
>
> -       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
> +       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
> +           is_ir_decoder)
>                 return 0;
>
>         if (is_socket || is_sockops || is_sk_skb || is_sk_msg) {
> @@ -607,7 +611,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
>                     memcmp(shname, "cgroup/", 7) == 0 ||
>                     memcmp(shname, "sockops", 7) == 0 ||
>                     memcmp(shname, "sk_skb", 6) == 0 ||
> -                   memcmp(shname, "sk_msg", 6) == 0) {
> +                   memcmp(shname, "sk_msg", 6) == 0 ||
> +                   memcmp(shname, "ir_decoder", 10) == 0) {
>                         ret = load_and_attach(shname, data->d_buf,
>                                               data->d_size);
>                         if (ret != 0)
> diff --git a/samples/bpf/grundig_decoder_kern.c b/samples/bpf/grundig_decoder_kern.c
> new file mode 100644
> index 000000000000..c80f2c9cc69a
> --- /dev/null
> +++ b/samples/bpf/grundig_decoder_kern.c
> @@ -0,0 +1,112 @@
> +
> +#include <uapi/linux/bpf.h>
> +#include <uapi/linux/bpf_rcdev.h>
> +#include "bpf_helpers.h"
> +#include <linux/version.h>
> +
> +enum grundig_state {
> +       STATE_INACTIVE,
> +       STATE_HEADER_SPACE,
> +       STATE_LEADING_PULSE,
> +       STATE_BITS_SPACE,
> +       STATE_BITS_PULSE,
> +};
> +
> +struct decoder_state {
> +       u32 bits;
> +       enum grundig_state state;
> +       u32 count;
> +       u32 last_space;
> +};
> +
> +struct bpf_map_def SEC("maps") decoder_state_map = {
> +       .type = BPF_MAP_TYPE_ARRAY,
> +       .key_size = sizeof(u32),
> +       .value_size = sizeof(struct decoder_state),
> +       .max_entries = 1,
> +};
> +
> +#define US_TO_NS(t) 1000*(t)
> +static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin)
> +{
> +       return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
> +}
> +
> +SEC("ir_decoder/grundig_decoder")
> +int bpf_decoder(struct ir_raw_event *raw)
> +{
> +       u32 key = 0;
> +       struct decoder_state init = {};
> +
> +       struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
> +
> +       if (!s)
> +               s = &init;
> +
> +       if (raw->carrier_report) {
> +               // ignore
> +       } else if (raw->reset) {
> +               s->state = STATE_INACTIVE;
> +       } else if (s->state == STATE_INACTIVE) {
> +               if (raw->pulse && eq_margin(US_TO_NS(900), raw->duration, US_TO_NS(100))) {
> +                       s->bits = 0;
> +                       s->state = STATE_HEADER_SPACE;
> +                       s->count = 0;
> +               }
> +       } else if (s->state == STATE_HEADER_SPACE) {
> +               if (!raw->pulse && eq_margin(US_TO_NS(2900), raw->duration, US_TO_NS(200)))
> +                       s->state = STATE_LEADING_PULSE;
> +               else
> +                       s->state = STATE_INACTIVE;
> +       } else if (s->state == STATE_LEADING_PULSE) {
> +               if (raw->pulse && eq_margin(US_TO_NS(1300), raw->duration, US_TO_NS(100)))
> +                       s->state = STATE_BITS_SPACE;
> +               else
> +                       s->state = STATE_INACTIVE;
> +       } else if (s->state == STATE_BITS_SPACE) {
> +               s->last_space = raw->duration;
> +               s->state = STATE_BITS_PULSE;
> +       } else if (s->state == STATE_BITS_PULSE) {
> +               int t = -1;
> +               if (eq_margin(s->last_space, US_TO_NS(472), US_TO_NS(150)) &&
> +                   eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> +                       t = 0;
> +               if (eq_margin(s->last_space, US_TO_NS(1139), US_TO_NS(150)) &&
> +                   eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> +                       t = 1;
> +               if (eq_margin(s->last_space, US_TO_NS(1806), US_TO_NS(150)) &&
> +                   eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> +                       t = 2;
> +               if (eq_margin(s->last_space, US_TO_NS(2200), US_TO_NS(150)) &&
> +                   eq_margin(raw->duration, US_TO_NS(1139), US_TO_NS(150)))
> +                       t = 3;
> +               if (t < 0) {
> +                       s->state = STATE_INACTIVE;
> +               } else {
> +                       s->bits <<= 2;
> +                       switch (t) {
> +                       case 3: s->bits |= 0; break;
> +                       case 2: s->bits |= 3; break;
> +                       case 1: s->bits |= 2; break;
> +                       case 0: s->bits |= 1; break;
> +                       }
> +                       s->count += 2;
> +                       if (s->count == 16) {
> +                               bpf_rc_keydown(raw, 0x40, s->bits, 0);
> +                               s->state = STATE_INACTIVE;
> +                       } else {
> +                               s->state = STATE_BITS_SPACE;
> +                       }
> +               }
> +       }
> +
> +       if (s == &init)
> +               bpf_map_update_elem(&decoder_state_map, &key, s, BPF_NOEXIST);
> +
> +       return 0;
> +}
> +
> +char _license[] SEC("license") = "GPL";
> +
> +u32 _version SEC("version") = LINUX_VERSION_CODE;
> +
> diff --git a/samples/bpf/grundig_decoder_user.c b/samples/bpf/grundig_decoder_user.c
> new file mode 100644
> index 000000000000..61e8ee5f73ee
> --- /dev/null
> +++ b/samples/bpf/grundig_decoder_user.c
> @@ -0,0 +1,54 @@
> +
> +#include <linux/bpf.h>
> +#include <assert.h>
> +#include <errno.h>
> +#include <signal.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <libgen.h>
> +#include <sys/resource.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include "bpf_load.h"
> +#include "bpf_util.h"
> +#include "libbpf.h"
> +
> +int main(int argc, char **argv)
> +{
> +       char filename[256];
> +       int ret, lircfd;
> +
> +       if (argc != 2) {
> +               printf("Usage: %s /dev/lircN\n", argv[0]);

Looks like the test requires /dev/lircN device. Is there any easy way
to test it?

Also, looks like the program does not depend on any kernel headers,
maybe it can be
moved to tools/testing/selftests/bpf/? There are testbot to run those
tests regularly.

> +               return 2;
> +       }
> +
> +       snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
> +
> +       if (load_bpf_file(filename)) {
> +               printf("%s", bpf_log_buf);
> +               return 1;
> +       }
> +
> +       lircfd = open(argv[1], O_RDWR);
> +       if (lircfd == -1) {
> +               printf("failed to open lirc device %s: %m\n", argv[1]);
> +               return 1;
> +       }
> +
> +       ret = bpf_prog_attach(prog_fd[0], lircfd, BPF_RAWIR_DECODER, 0);
> +       if (ret) {
> +               printf("Failed to attach bpf to lirc device: %m\n");
> +               return 1;
> +       }
> +
> +       printf("Grundig IR decoder loaded and attached. Hit any key to stop\n");
> +       getchar();
> +
> +       return 0;
> +}
> diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> index f7a810897eac..ae1c26df212d 100644
> --- a/tools/bpf/bpftool/prog.c
> +++ b/tools/bpf/bpftool/prog.c
> @@ -68,6 +68,7 @@ static const char * const prog_type_name[] = {
>         [BPF_PROG_TYPE_SOCK_OPS]        = "sock_ops",
>         [BPF_PROG_TYPE_SK_SKB]          = "sk_skb",
>         [BPF_PROG_TYPE_CGROUP_DEVICE]   = "cgroup_device",
> +       [BPF_PROG_TYPE_RAWIR_DECODER]   = "ir_decoder",
>  };
>
>  static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
> diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> index c5ec89732a8d..d9740599adf6 100644
> --- a/tools/include/uapi/linux/bpf.h
> +++ b/tools/include/uapi/linux/bpf.h
> @@ -137,6 +137,7 @@ enum bpf_prog_type {
>         BPF_PROG_TYPE_SK_MSG,
>         BPF_PROG_TYPE_RAW_TRACEPOINT,
>         BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> +       BPF_PROG_TYPE_RAWIR_DECODER,
>  };
>
>  enum bpf_attach_type {
> @@ -154,6 +155,7 @@ enum bpf_attach_type {
>         BPF_CGROUP_INET6_CONNECT,
>         BPF_CGROUP_INET4_POST_BIND,
>         BPF_CGROUP_INET6_POST_BIND,
> +       BPF_RAWIR_DECODER,
>         __MAX_BPF_ATTACH_TYPE
>  };
>
> @@ -755,6 +757,17 @@ union bpf_attr {
>   *     @addr: pointer to struct sockaddr to bind socket to
>   *     @addr_len: length of sockaddr structure
>   *     Return: 0 on success or negative error code
> + *
> + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> + *     Report decoded scancode with toggle value
> + *     @ctx: pointer to ctx
> + *     @protocol: decoded protocol
> + *     @scancode: decoded scancode
> + *     @toggle: set to 1 if button was toggled, else 0
> + *
> + * int bpf_rc_repeat(ctx)
> + *     Repeat the last decoded scancode
> + *     @ctx: pointer to ctx
>   */
>  #define __BPF_FUNC_MAPPER(FN)          \
>         FN(unspec),                     \
> @@ -821,7 +834,9 @@ union bpf_attr {
>         FN(msg_apply_bytes),            \
>         FN(msg_cork_bytes),             \
>         FN(msg_pull_data),              \
> -       FN(bind),
> +       FN(bind),                       \
> +       FN(rc_repeat),                  \
> +       FN(rc_keydown),
>
>  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
>   * function eBPF program intends to call
> diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
> index d8223d99f96d..4bf23d3dfc33 100644
> --- a/tools/testing/selftests/bpf/bpf_helpers.h
> +++ b/tools/testing/selftests/bpf/bpf_helpers.h
> @@ -96,6 +96,12 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
>         (void *) BPF_FUNC_msg_pull_data;
>  static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
>         (void *) BPF_FUNC_bind;
> +static int (*bpf_rc_repeat)(void *ctx) =
> +       (void *) BPF_FUNC_rc_repeat;
> +static int (*bpf_rc_keydown)(void *ctx, unsigned protocol, unsigned scancode,
> +                            unsigned toggle) =
> +       (void *) BPF_FUNC_rc_keydown;
> +
>
>  /* llvm builtin functions that eBPF C program may use to
>   * emit BPF_LD_ABS and BPF_LD_IND instructions
> --
> 2.17.0
>

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

* Re: [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER
  2018-05-15  4:48   ` Y Song
@ 2018-05-15 10:30     ` Sean Young
  0 siblings, 0 replies; 15+ messages in thread
From: Sean Young @ 2018-05-15 10:30 UTC (permalink / raw)
  To: Y Song
  Cc: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On Mon, May 14, 2018 at 09:48:05PM -0700, Y Song wrote:
> On Mon, May 14, 2018 at 2:10 PM, Sean Young <sean@mess.org> wrote:
> > Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
> > rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> > that the last key should be repeated.
> >
> > Signed-off-by: Sean Young <sean@mess.org>
> > ---
> >  drivers/media/rc/Kconfig          |  8 +++
> >  drivers/media/rc/Makefile         |  1 +
> >  drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
> >  include/linux/bpf_types.h         |  3 +
> >  include/uapi/linux/bpf.h          | 16 +++++-
> >  5 files changed, 120 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/media/rc/ir-bpf-decoder.c
> >
> > diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> > index eb2c3b6eca7f..10ad6167d87c 100644
> > --- a/drivers/media/rc/Kconfig
> > +++ b/drivers/media/rc/Kconfig
> > @@ -120,6 +120,14 @@ config IR_IMON_DECODER
> >            remote control and you would like to use it with a raw IR
> >            receiver, or if you wish to use an encoder to transmit this IR.
> >
> > +config IR_BPF_DECODER
> > +       bool "Enable IR raw decoder using BPF"
> > +       depends on BPF_SYSCALL
> > +       depends on RC_CORE=y
> > +       help
> > +          Enable this option to make it possible to load custom IR
> > +          decoders written in BPF.
> > +
> >  endif #RC_DECODERS
> >
> >  menuconfig RC_DEVICES
> > diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> > index 2e1c87066f6c..12e1118430d0 100644
> > --- a/drivers/media/rc/Makefile
> > +++ b/drivers/media/rc/Makefile
> > @@ -5,6 +5,7 @@ obj-y += keymaps/
> >  obj-$(CONFIG_RC_CORE) += rc-core.o
> >  rc-core-y := rc-main.o rc-ir-raw.o
> >  rc-core-$(CONFIG_LIRC) += lirc_dev.o
> > +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
> >  obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
> >  obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
> >  obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
> > diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
> > new file mode 100644
> > index 000000000000..aaa5e208b1a5
> > --- /dev/null
> > +++ b/drivers/media/rc/ir-bpf-decoder.c
> > @@ -0,0 +1,93 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// ir-bpf-decoder.c - handles bpf decoders
> > +//
> > +// Copyright (C) 2018 Sean Young <sean@mess.org>
> > +
> > +#include <linux/bpf.h>
> > +#include <linux/filter.h>
> > +#include "rc-core-priv.h"
> > +
> > +/*
> > + * BPF interface for raw IR decoder
> > + */
> > +const struct bpf_prog_ops ir_decoder_prog_ops = {
> > +};
> > +
> > +BPF_CALL_1(bpf_rc_repeat, struct ir_raw_event*, event)
> > +{
> > +       struct ir_raw_event_ctrl *ctrl;
> > +
> > +       ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> > +
> > +       rc_repeat(ctrl->dev);
> > +       return 0;
> > +}
> > +
> > +static const struct bpf_func_proto rc_repeat_proto = {
> > +       .func      = bpf_rc_repeat,
> > +       .gpl_only  = true, // rc_repeat is EXPORT_SYMBOL_GPL
> > +       .ret_type  = RET_VOID,
> 
> I suggest using RET_INTEGER here since we do return an integer 0.
> RET_INTEGER will also make it easy to extend if you want to return
> a non-zero value for error code or other reasons.

Ok.

> > +       .arg1_type = ARG_PTR_TO_CTX,
> > +};
> > +
> > +BPF_CALL_4(bpf_rc_keydown, struct ir_raw_event*, event, u32, protocol,
> > +          u32, scancode, u32, toggle)
> > +{
> > +       struct ir_raw_event_ctrl *ctrl;
> > +
> > +       ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> > +       rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
> > +       return 0;
> > +}
> > +
> > +static const struct bpf_func_proto rc_keydown_proto = {
> > +       .func      = bpf_rc_keydown,
> > +       .gpl_only  = true, // rc_keydown is EXPORT_SYMBOL_GPL
> > +       .ret_type  = RET_VOID,
> 
> ditto. RET_INTEGER is preferable.

Ok.

> > +       .arg1_type = ARG_PTR_TO_CTX,
> > +       .arg2_type = ARG_ANYTHING,
> > +       .arg3_type = ARG_ANYTHING,
> > +       .arg4_type = ARG_ANYTHING,
> > +};
> > +
> > +static const struct bpf_func_proto *ir_decoder_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
> > +{
> > +       switch (func_id) {
> > +       case BPF_FUNC_rc_repeat:
> > +               return &rc_repeat_proto;
> > +       case BPF_FUNC_rc_keydown:
> > +               return &rc_keydown_proto;
> > +       case BPF_FUNC_map_lookup_elem:
> > +               return &bpf_map_lookup_elem_proto;
> > +       case BPF_FUNC_map_update_elem:
> > +               return &bpf_map_update_elem_proto;
> > +       case BPF_FUNC_map_delete_elem:
> > +               return &bpf_map_delete_elem_proto;
> > +       case BPF_FUNC_ktime_get_ns:
> > +               return &bpf_ktime_get_ns_proto;
> > +       case BPF_FUNC_tail_call:
> > +               return &bpf_tail_call_proto;
> > +       case BPF_FUNC_get_prandom_u32:
> > +               return &bpf_get_prandom_u32_proto;
> > +       default:
> > +               return NULL;
> > +       }
> > +}
> > +
> > +static bool ir_decoder_is_valid_access(int off, int size,
> > +                                      enum bpf_access_type type,
> > +                                      const struct bpf_prog *prog,
> > +                                      struct bpf_insn_access_aux *info)
> > +{
> > +       if (type == BPF_WRITE)
> > +               return false;
> > +       if (off < 0 || off + size > sizeof(struct ir_raw_event))
> > +               return false;
> 
> You probably need more than just checking the boundary.
> >From patch #3, the structure is:
>  struct ir_raw_event {
>         union {
>                 __u32           duration;
>                 __u32           carrier;
>         };
>         __u8                    duty_cycle;
> 
>         unsigned                pulse:1;
>         unsigned                reset:1;
>         unsigned                timeout:1;
>        unsigned                carrier_report:1;
> };
> 
> You would like the memory access to be aligned,
> so accessing duration/carrier with 4-byte alignment, and
> pulse/reset/timeout/carrier_report 4-byte alignment as well.
> 
> You could only allow __u32 access to duration/carrier.
> But if you want bpf program to access duration/carrier with
> code like (__u16)(ctx->duration), then the compiler may
> translate the load to a 2-byte load. You may need to handle
> endianness here. You can check net/core/filter.c function
> bpf_skb_is_valid_access for some examples.

Thanks, yes that makes sense. Actually exposing a struct with bit fields
isn't great. I think I can rework into something simpler (two u32 fields).

> > +
> > +       return true;
> > +}
> > +
> > +const struct bpf_verifier_ops ir_decoder_verifier_ops = {
> > +       .get_func_proto  = ir_decoder_func_proto,
> > +       .is_valid_access = ir_decoder_is_valid_access
> > +};
> > diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> > index 2b28fcf6f6ae..ee5355715ee0 100644
> > --- a/include/linux/bpf_types.h
> > +++ b/include/linux/bpf_types.h
> > @@ -25,6 +25,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
> >  #ifdef CONFIG_CGROUP_BPF
> >  BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
> >  #endif
> > +#ifdef CONFIG_IR_BPF_DECODER
> > +BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_DECODER, ir_decoder)
> > +#endif
> >
> >  BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
> >  BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
> > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > index c5ec89732a8d..6ad053e831c0 100644
> > --- a/include/uapi/linux/bpf.h
> > +++ b/include/uapi/linux/bpf.h
> > @@ -137,6 +137,7 @@ enum bpf_prog_type {
> >         BPF_PROG_TYPE_SK_MSG,
> >         BPF_PROG_TYPE_RAW_TRACEPOINT,
> >         BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> > +       BPF_PROG_TYPE_RAWIR_DECODER,
> >  };
> >
> >  enum bpf_attach_type {
> > @@ -755,6 +756,17 @@ union bpf_attr {
> >   *     @addr: pointer to struct sockaddr to bind socket to
> >   *     @addr_len: length of sockaddr structure
> >   *     Return: 0 on success or negative error code
> > + *
> > + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> > + *     Report decoded scancode with toggle value
> > + *     @ctx: pointer to ctx
> > + *     @protocol: decoded protocol
> > + *     @scancode: decoded scancode
> > + *     @toggle: set to 1 if button was toggled, else 0
> > + *
> > + * int bpf_rc_repeat(ctx)
> > + *     Repeat the last decoded scancode
> > + *     @ctx: pointer to ctx
> 
> The comment format has changed dramatically for
> documentation reason. Could you rebase your change
> on top of bpf-next tree? You will need to rewrite the above
> helper description so tools can generate proper documentation
> for them.

Ah, I need to rebase on top of bpf-next.

Thanks!

> >   */
> >  #define __BPF_FUNC_MAPPER(FN)          \
> >         FN(unspec),                     \
> > @@ -821,7 +833,9 @@ union bpf_attr {
> >         FN(msg_apply_bytes),            \
> >         FN(msg_cork_bytes),             \
> >         FN(msg_pull_data),              \
> > -       FN(bind),
> > +       FN(bind),                       \
> > +       FN(rc_repeat),                  \
> > +       FN(rc_keydown),
> >
> >  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> >   * function eBPF program intends to call
> > --
> > 2.17.0
> >

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

* Re: [PATCH v1 4/4] samples/bpf: an example of a raw IR decoder
  2018-05-15  5:34   ` Y Song
@ 2018-05-15 10:40     ` Sean Young
  0 siblings, 0 replies; 15+ messages in thread
From: Sean Young @ 2018-05-15 10:40 UTC (permalink / raw)
  To: Y Song
  Cc: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On Mon, May 14, 2018 at 10:34:57PM -0700, Y Song wrote:
> On Mon, May 14, 2018 at 2:11 PM, Sean Young <sean@mess.org> wrote:
> > This implements the grundig-16 IR protocol.
> >
> > Signed-off-by: Sean Young <sean@mess.org>
> > ---
> >  samples/bpf/Makefile                      |   4 +
> >  samples/bpf/bpf_load.c                    |   9 +-
> >  samples/bpf/grundig_decoder_kern.c        | 112 ++++++++++++++++++++++
> >  samples/bpf/grundig_decoder_user.c        |  54 +++++++++++
> >  tools/bpf/bpftool/prog.c                  |   1 +
> >  tools/include/uapi/linux/bpf.h            |  17 +++-
> >  tools/testing/selftests/bpf/bpf_helpers.h |   6 ++
> >  7 files changed, 200 insertions(+), 3 deletions(-)
> >  create mode 100644 samples/bpf/grundig_decoder_kern.c
> >  create mode 100644 samples/bpf/grundig_decoder_user.c
> >
> > diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
> > index 4d6a6edd4bf6..c6fa111f103a 100644
> > --- a/samples/bpf/Makefile
> > +++ b/samples/bpf/Makefile
> > @@ -44,6 +44,7 @@ hostprogs-y += xdp_monitor
> >  hostprogs-y += xdp_rxq_info
> >  hostprogs-y += syscall_tp
> >  hostprogs-y += cpustat
> > +hostprogs-y += grundig_decoder
> >
> >  # Libbpf dependencies
> >  LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
> > @@ -95,6 +96,7 @@ xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
> >  xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o
> >  syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
> >  cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o
> > +grundig_decoder-objs := bpf_load.o $(LIBBPF) grundig_decoder_user.o
> >
> >  # Tell kbuild to always build the programs
> >  always := $(hostprogs-y)
> > @@ -148,6 +150,7 @@ always += xdp_rxq_info_kern.o
> >  always += xdp2skb_meta_kern.o
> >  always += syscall_tp_kern.o
> >  always += cpustat_kern.o
> > +always += grundig_decoder_kern.o
> >
> >  HOSTCFLAGS += -I$(objtree)/usr/include
> >  HOSTCFLAGS += -I$(srctree)/tools/lib/
> > @@ -193,6 +196,7 @@ HOSTLOADLIBES_xdp_monitor += -lelf
> >  HOSTLOADLIBES_xdp_rxq_info += -lelf
> >  HOSTLOADLIBES_syscall_tp += -lelf
> >  HOSTLOADLIBES_cpustat += -lelf
> > +HOSTLOADLIBES_grundig_decoder += -lelf
> >
> >  # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
> >  #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
> > diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
> > index bebe4188b4b3..0fd389e95bb9 100644
> > --- a/samples/bpf/bpf_load.c
> > +++ b/samples/bpf/bpf_load.c
> > @@ -69,6 +69,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> >         bool is_sockops = strncmp(event, "sockops", 7) == 0;
> >         bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0;
> >         bool is_sk_msg = strncmp(event, "sk_msg", 6) == 0;
> > +       bool is_ir_decoder = strncmp(event, "ir_decoder", 10) == 0;
> >         size_t insns_cnt = size / sizeof(struct bpf_insn);
> >         enum bpf_prog_type prog_type;
> >         char buf[256];
> > @@ -102,6 +103,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> >                 prog_type = BPF_PROG_TYPE_SK_SKB;
> >         } else if (is_sk_msg) {
> >                 prog_type = BPF_PROG_TYPE_SK_MSG;
> > +       } else if (is_ir_decoder) {
> > +               prog_type = BPF_PROG_TYPE_RAWIR_DECODER;
> >         } else {
> >                 printf("Unknown event '%s'\n", event);
> >                 return -1;
> > @@ -116,7 +119,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> >
> >         prog_fd[prog_cnt++] = fd;
> >
> > -       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
> > +       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
> > +           is_ir_decoder)
> >                 return 0;
> >
> >         if (is_socket || is_sockops || is_sk_skb || is_sk_msg) {
> > @@ -607,7 +611,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
> >                     memcmp(shname, "cgroup/", 7) == 0 ||
> >                     memcmp(shname, "sockops", 7) == 0 ||
> >                     memcmp(shname, "sk_skb", 6) == 0 ||
> > -                   memcmp(shname, "sk_msg", 6) == 0) {
> > +                   memcmp(shname, "sk_msg", 6) == 0 ||
> > +                   memcmp(shname, "ir_decoder", 10) == 0) {
> >                         ret = load_and_attach(shname, data->d_buf,
> >                                               data->d_size);
> >                         if (ret != 0)
> > diff --git a/samples/bpf/grundig_decoder_kern.c b/samples/bpf/grundig_decoder_kern.c
> > new file mode 100644
> > index 000000000000..c80f2c9cc69a
> > --- /dev/null
> > +++ b/samples/bpf/grundig_decoder_kern.c
> > @@ -0,0 +1,112 @@
> > +
> > +#include <uapi/linux/bpf.h>
> > +#include <uapi/linux/bpf_rcdev.h>
> > +#include "bpf_helpers.h"
> > +#include <linux/version.h>
> > +
> > +enum grundig_state {
> > +       STATE_INACTIVE,
> > +       STATE_HEADER_SPACE,
> > +       STATE_LEADING_PULSE,
> > +       STATE_BITS_SPACE,
> > +       STATE_BITS_PULSE,
> > +};
> > +
> > +struct decoder_state {
> > +       u32 bits;
> > +       enum grundig_state state;
> > +       u32 count;
> > +       u32 last_space;
> > +};
> > +
> > +struct bpf_map_def SEC("maps") decoder_state_map = {
> > +       .type = BPF_MAP_TYPE_ARRAY,
> > +       .key_size = sizeof(u32),
> > +       .value_size = sizeof(struct decoder_state),
> > +       .max_entries = 1,
> > +};
> > +
> > +#define US_TO_NS(t) 1000*(t)
> > +static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin)
> > +{
> > +       return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
> > +}
> > +
> > +SEC("ir_decoder/grundig_decoder")
> > +int bpf_decoder(struct ir_raw_event *raw)
> > +{
> > +       u32 key = 0;
> > +       struct decoder_state init = {};
> > +
> > +       struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
> > +
> > +       if (!s)
> > +               s = &init;
> > +
> > +       if (raw->carrier_report) {
> > +               // ignore
> > +       } else if (raw->reset) {
> > +               s->state = STATE_INACTIVE;
> > +       } else if (s->state == STATE_INACTIVE) {
> > +               if (raw->pulse && eq_margin(US_TO_NS(900), raw->duration, US_TO_NS(100))) {
> > +                       s->bits = 0;
> > +                       s->state = STATE_HEADER_SPACE;
> > +                       s->count = 0;
> > +               }
> > +       } else if (s->state == STATE_HEADER_SPACE) {
> > +               if (!raw->pulse && eq_margin(US_TO_NS(2900), raw->duration, US_TO_NS(200)))
> > +                       s->state = STATE_LEADING_PULSE;
> > +               else
> > +                       s->state = STATE_INACTIVE;
> > +       } else if (s->state == STATE_LEADING_PULSE) {
> > +               if (raw->pulse && eq_margin(US_TO_NS(1300), raw->duration, US_TO_NS(100)))
> > +                       s->state = STATE_BITS_SPACE;
> > +               else
> > +                       s->state = STATE_INACTIVE;
> > +       } else if (s->state == STATE_BITS_SPACE) {
> > +               s->last_space = raw->duration;
> > +               s->state = STATE_BITS_PULSE;
> > +       } else if (s->state == STATE_BITS_PULSE) {
> > +               int t = -1;
> > +               if (eq_margin(s->last_space, US_TO_NS(472), US_TO_NS(150)) &&
> > +                   eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> > +                       t = 0;
> > +               if (eq_margin(s->last_space, US_TO_NS(1139), US_TO_NS(150)) &&
> > +                   eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> > +                       t = 1;
> > +               if (eq_margin(s->last_space, US_TO_NS(1806), US_TO_NS(150)) &&
> > +                   eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> > +                       t = 2;
> > +               if (eq_margin(s->last_space, US_TO_NS(2200), US_TO_NS(150)) &&
> > +                   eq_margin(raw->duration, US_TO_NS(1139), US_TO_NS(150)))
> > +                       t = 3;
> > +               if (t < 0) {
> > +                       s->state = STATE_INACTIVE;
> > +               } else {
> > +                       s->bits <<= 2;
> > +                       switch (t) {
> > +                       case 3: s->bits |= 0; break;
> > +                       case 2: s->bits |= 3; break;
> > +                       case 1: s->bits |= 2; break;
> > +                       case 0: s->bits |= 1; break;
> > +                       }
> > +                       s->count += 2;
> > +                       if (s->count == 16) {
> > +                               bpf_rc_keydown(raw, 0x40, s->bits, 0);
> > +                               s->state = STATE_INACTIVE;
> > +                       } else {
> > +                               s->state = STATE_BITS_SPACE;
> > +                       }
> > +               }
> > +       }
> > +
> > +       if (s == &init)
> > +               bpf_map_update_elem(&decoder_state_map, &key, s, BPF_NOEXIST);
> > +
> > +       return 0;
> > +}
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +u32 _version SEC("version") = LINUX_VERSION_CODE;
> > +
> > diff --git a/samples/bpf/grundig_decoder_user.c b/samples/bpf/grundig_decoder_user.c
> > new file mode 100644
> > index 000000000000..61e8ee5f73ee
> > --- /dev/null
> > +++ b/samples/bpf/grundig_decoder_user.c
> > @@ -0,0 +1,54 @@
> > +
> > +#include <linux/bpf.h>
> > +#include <assert.h>
> > +#include <errno.h>
> > +#include <signal.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <stdbool.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +#include <libgen.h>
> > +#include <sys/resource.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +#include "bpf_load.h"
> > +#include "bpf_util.h"
> > +#include "libbpf.h"
> > +
> > +int main(int argc, char **argv)
> > +{
> > +       char filename[256];
> > +       int ret, lircfd;
> > +
> > +       if (argc != 2) {
> > +               printf("Usage: %s /dev/lircN\n", argv[0]);
> 
> Looks like the test requires /dev/lircN device. Is there any easy way
> to test it?

It can be tested using rc-loopback.

> Also, looks like the program does not depend on any kernel headers,
> maybe it can be
> moved to tools/testing/selftests/bpf/? There are testbot to run those
> tests regularly.

That's a good idea. Let me see what I can do.

Thanks for all review comments, I agree with all them (so I won't have
to reply to every message).

I'll re-roll a v2 of this series in the next couple of days.

Thanks

Sean

> 
> > +               return 2;
> > +       }
> > +
> > +       snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
> > +
> > +       if (load_bpf_file(filename)) {
> > +               printf("%s", bpf_log_buf);
> > +               return 1;
> > +       }
> > +
> > +       lircfd = open(argv[1], O_RDWR);
> > +       if (lircfd == -1) {
> > +               printf("failed to open lirc device %s: %m\n", argv[1]);
> > +               return 1;
> > +       }
> > +
> > +       ret = bpf_prog_attach(prog_fd[0], lircfd, BPF_RAWIR_DECODER, 0);
> > +       if (ret) {
> > +               printf("Failed to attach bpf to lirc device: %m\n");
> > +               return 1;
> > +       }
> > +
> > +       printf("Grundig IR decoder loaded and attached. Hit any key to stop\n");
> > +       getchar();
> > +
> > +       return 0;
> > +}
> > diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> > index f7a810897eac..ae1c26df212d 100644
> > --- a/tools/bpf/bpftool/prog.c
> > +++ b/tools/bpf/bpftool/prog.c
> > @@ -68,6 +68,7 @@ static const char * const prog_type_name[] = {
> >         [BPF_PROG_TYPE_SOCK_OPS]        = "sock_ops",
> >         [BPF_PROG_TYPE_SK_SKB]          = "sk_skb",
> >         [BPF_PROG_TYPE_CGROUP_DEVICE]   = "cgroup_device",
> > +       [BPF_PROG_TYPE_RAWIR_DECODER]   = "ir_decoder",
> >  };
> >
> >  static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
> > diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> > index c5ec89732a8d..d9740599adf6 100644
> > --- a/tools/include/uapi/linux/bpf.h
> > +++ b/tools/include/uapi/linux/bpf.h
> > @@ -137,6 +137,7 @@ enum bpf_prog_type {
> >         BPF_PROG_TYPE_SK_MSG,
> >         BPF_PROG_TYPE_RAW_TRACEPOINT,
> >         BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> > +       BPF_PROG_TYPE_RAWIR_DECODER,
> >  };
> >
> >  enum bpf_attach_type {
> > @@ -154,6 +155,7 @@ enum bpf_attach_type {
> >         BPF_CGROUP_INET6_CONNECT,
> >         BPF_CGROUP_INET4_POST_BIND,
> >         BPF_CGROUP_INET6_POST_BIND,
> > +       BPF_RAWIR_DECODER,
> >         __MAX_BPF_ATTACH_TYPE
> >  };
> >
> > @@ -755,6 +757,17 @@ union bpf_attr {
> >   *     @addr: pointer to struct sockaddr to bind socket to
> >   *     @addr_len: length of sockaddr structure
> >   *     Return: 0 on success or negative error code
> > + *
> > + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> > + *     Report decoded scancode with toggle value
> > + *     @ctx: pointer to ctx
> > + *     @protocol: decoded protocol
> > + *     @scancode: decoded scancode
> > + *     @toggle: set to 1 if button was toggled, else 0
> > + *
> > + * int bpf_rc_repeat(ctx)
> > + *     Repeat the last decoded scancode
> > + *     @ctx: pointer to ctx
> >   */
> >  #define __BPF_FUNC_MAPPER(FN)          \
> >         FN(unspec),                     \
> > @@ -821,7 +834,9 @@ union bpf_attr {
> >         FN(msg_apply_bytes),            \
> >         FN(msg_cork_bytes),             \
> >         FN(msg_pull_data),              \
> > -       FN(bind),
> > +       FN(bind),                       \
> > +       FN(rc_repeat),                  \
> > +       FN(rc_keydown),
> >
> >  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> >   * function eBPF program intends to call
> > diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
> > index d8223d99f96d..4bf23d3dfc33 100644
> > --- a/tools/testing/selftests/bpf/bpf_helpers.h
> > +++ b/tools/testing/selftests/bpf/bpf_helpers.h
> > @@ -96,6 +96,12 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
> >         (void *) BPF_FUNC_msg_pull_data;
> >  static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
> >         (void *) BPF_FUNC_bind;
> > +static int (*bpf_rc_repeat)(void *ctx) =
> > +       (void *) BPF_FUNC_rc_repeat;
> > +static int (*bpf_rc_keydown)(void *ctx, unsigned protocol, unsigned scancode,
> > +                            unsigned toggle) =
> > +       (void *) BPF_FUNC_rc_keydown;
> > +
> >
> >  /* llvm builtin functions that eBPF C program may use to
> >   * emit BPF_LD_ABS and BPF_LD_IND instructions
> > --
> > 2.17.0
> >

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

* Re: [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER
  2018-05-14 23:27   ` Randy Dunlap
@ 2018-05-15 12:19     ` Sean Young
  0 siblings, 0 replies; 15+ messages in thread
From: Sean Young @ 2018-05-15 12:19 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: linux-media, linux-kernel, Alexei Starovoitov,
	Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
	Devin Heitmueller

On Mon, May 14, 2018 at 04:27:19PM -0700, Randy Dunlap wrote:
> On 05/14/2018 02:10 PM, Sean Young wrote:
> > Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
> 
> Kconfig file below uses IR_BPF_DECODER instead of the symbol name above.
> 
> and then patch 3 says a third choice:
> The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;


Yes, you're right. I guess the source/trigger is raw IR events; decoding
is something you're likely to do, but not necessarily. So:

bpf type: BPF_PROG_TYPE_RAWIR_EVENT, has context struct bpf_rawir_event. 

Then we can call the Kconfig option CONFIG_BPF_RAW_IR_EVENT


Sean
> 
> > rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> > that the last key should be repeated.
> > 
> > Signed-off-by: Sean Young <sean@mess.org>
> > ---
> >  drivers/media/rc/Kconfig          |  8 +++
> >  drivers/media/rc/Makefile         |  1 +
> >  drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
> >  include/linux/bpf_types.h         |  3 +
> >  include/uapi/linux/bpf.h          | 16 +++++-
> >  5 files changed, 120 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/media/rc/ir-bpf-decoder.c
> > 
> > diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> > index eb2c3b6eca7f..10ad6167d87c 100644
> > --- a/drivers/media/rc/Kconfig
> > +++ b/drivers/media/rc/Kconfig
> > @@ -120,6 +120,14 @@ config IR_IMON_DECODER
> >  	   remote control and you would like to use it with a raw IR
> >  	   receiver, or if you wish to use an encoder to transmit this IR.
> >  
> > +config IR_BPF_DECODER
> > +	bool "Enable IR raw decoder using BPF"
> > +	depends on BPF_SYSCALL
> > +	depends on RC_CORE=y
> > +	help
> > +	   Enable this option to make it possible to load custom IR
> > +	   decoders written in BPF.
> > +
> >  endif #RC_DECODERS
> >  
> >  menuconfig RC_DEVICES
> > diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> > index 2e1c87066f6c..12e1118430d0 100644
> > --- a/drivers/media/rc/Makefile
> > +++ b/drivers/media/rc/Makefile
> > @@ -5,6 +5,7 @@ obj-y += keymaps/
> >  obj-$(CONFIG_RC_CORE) += rc-core.o
> >  rc-core-y := rc-main.o rc-ir-raw.o
> >  rc-core-$(CONFIG_LIRC) += lirc_dev.o
> > +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
> 
> 
> -- 
> ~Randy

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

end of thread, other threads:[~2018-05-15 12:19 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-14 21:10 [PATCH v1 0/4] IR decoding using BPF Sean Young
2018-05-14 21:10 ` [PATCH v1 1/4] media: rc: introduce BPF_PROG_IR_DECODER Sean Young
2018-05-14 23:27   ` Randy Dunlap
2018-05-15 12:19     ` Sean Young
2018-05-15  4:48   ` Y Song
2018-05-15 10:30     ` Sean Young
2018-05-14 21:10 ` [PATCH v1 2/4] media: bpf: allow raw IR decoder bpf programs to be used Sean Young
2018-05-15  2:34   ` kbuild test robot
2018-05-15  5:22   ` Y Song
2018-05-14 21:11 ` [PATCH v1 3/4] media: rc bpf: move ir_raw_event to uapi Sean Young
2018-05-15  1:59   ` kbuild test robot
2018-05-15  5:26   ` Y Song
2018-05-14 21:11 ` [PATCH v1 4/4] samples/bpf: an example of a raw IR decoder Sean Young
2018-05-15  5:34   ` Y Song
2018-05-15 10:40     ` Sean Young

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.