Re: [PATCH v3 1/2] media: rc: introduce BPF_PROG_RAWIR_EVENT

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

Again thanks for a thoughtful review. This will definitely will improve
the code.

On Thu, May 17, 2018 at 10:02:52AM -0700, Y Song wrote:
> On Wed, May 16, 2018 at 2:04 PM, Sean Young <sean@xxxxxxxx> wrote:
> > Add support for BPF_PROG_RAWIR_EVENT. 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.
> >
> > The bpf program can be attached to using the bpf(BPF_PROG_ATTACH) syscall;
> > the target_fd must be the /dev/lircN device.
> >
> > Signed-off-by: Sean Young <sean@xxxxxxxx>
> > ---
> >  drivers/media/rc/Kconfig           |  13 ++
> >  drivers/media/rc/Makefile          |   1 +
> >  drivers/media/rc/bpf-rawir-event.c | 363 +++++++++++++++++++++++++++++
> >  drivers/media/rc/lirc_dev.c        |  24 ++
> >  drivers/media/rc/rc-core-priv.h    |  24 ++
> >  drivers/media/rc/rc-ir-raw.c       |  14 +-
> >  include/linux/bpf_rcdev.h          |  30 +++
> >  include/linux/bpf_types.h          |   3 +
> >  include/uapi/linux/bpf.h           |  55 ++++-
> >  kernel/bpf/syscall.c               |   7 +
> >  10 files changed, 531 insertions(+), 3 deletions(-)
> >  create mode 100644 drivers/media/rc/bpf-rawir-event.c
> >  create mode 100644 include/linux/bpf_rcdev.h
> >
> > diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> > index eb2c3b6eca7f..2172d65b0213 100644
> > --- a/drivers/media/rc/Kconfig
> > +++ b/drivers/media/rc/Kconfig
> > @@ -25,6 +25,19 @@ config LIRC
> >            passes raw IR to and from userspace, which is needed for
> >            IR transmitting (aka "blasting") and for the lirc daemon.
> >
> > +config BPF_RAWIR_EVENT
> > +       bool "Support for eBPF programs attached to lirc devices"
> > +       depends on BPF_SYSCALL
> > +       depends on RC_CORE=y
> > +       depends on LIRC
> > +       help
> > +          Allow attaching eBPF programs to a lirc device using the bpf(2)
> > +          syscall command BPF_PROG_ATTACH. This is supported for raw IR
> > +          receivers.
> > +
> > +          These eBPF programs can be used to decode IR into scancodes, for
> > +          IR protocols not supported by the kernel decoders.
> > +
> >  menuconfig RC_DECODERS
> >         bool "Remote controller decoders"
> >         depends on RC_CORE
> > diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> > index 2e1c87066f6c..74907823bef8 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_BPF_RAWIR_EVENT) += bpf-rawir-event.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/bpf-rawir-event.c b/drivers/media/rc/bpf-rawir-event.c
> > new file mode 100644
> > index 000000000000..7cb48b8d87b5
> > --- /dev/null
> > +++ b/drivers/media/rc/bpf-rawir-event.c
> > @@ -0,0 +1,363 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// bpf-rawir-event.c - handles bpf
> > +//
> > +// Copyright (C) 2018 Sean Young <sean@xxxxxxxx>
> > +
> > +#include <linux/bpf.h>
> > +#include <linux/filter.h>
> > +#include <linux/bpf_rcdev.h>
> > +#include "rc-core-priv.h"
> > +
> > +/*
> > + * BPF interface for raw IR
> > + */
> > +const struct bpf_prog_ops rawir_event_prog_ops = {
> > +};
> > +
> > +BPF_CALL_1(bpf_rc_repeat, struct bpf_rawir_event*, event)
> > +{
> > +       struct ir_raw_event_ctrl *ctrl;
> > +
> > +       ctrl = container_of(event, struct ir_raw_event_ctrl, bpf_rawir_event);
> > +
> > +       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_INTEGER,
> > +       .arg1_type = ARG_PTR_TO_CTX,
> > +};
> > +
> > +BPF_CALL_4(bpf_rc_keydown, struct bpf_rawir_event*, event, u32, protocol,
> > +          u32, scancode, u32, toggle)
> > +{
> > +       struct ir_raw_event_ctrl *ctrl;
> > +
> > +       ctrl = container_of(event, struct ir_raw_event_ctrl, bpf_rawir_event);
> > +
> > +       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_INTEGER,
> > +       .arg1_type = ARG_PTR_TO_CTX,
> > +       .arg2_type = ARG_ANYTHING,
> > +       .arg3_type = ARG_ANYTHING,
> > +       .arg4_type = ARG_ANYTHING,
> > +};
> > +
> > +static const struct bpf_func_proto *
> > +rawir_event_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;
> > +       case BPF_FUNC_trace_printk:
> > +               if (capable(CAP_SYS_ADMIN))
> > +                       return bpf_get_trace_printk_proto();
> > +               /* fall through */
> > +       default:
> > +               return NULL;
> > +       }
> > +}
> > +
> > +static bool rawir_event_is_valid_access(int off, int size,
> > +                                       enum bpf_access_type type,
> > +                                       const struct bpf_prog *prog,
> > +                                       struct bpf_insn_access_aux *info)
> > +{
> > +       /* struct bpf_rawir_event has two u32 fields */
> > +       if (type == BPF_WRITE)
> > +               return false;
> > +
> > +       if (size != sizeof(__u32))
> > +               return false;
> > +
> > +       if (!(off == offsetof(struct bpf_rawir_event, duration) ||
> > +             off == offsetof(struct bpf_rawir_event, type)))
> > +               return false;
> > +
> > +       return true;
> > +}
> > +
> > +const struct bpf_verifier_ops rawir_event_verifier_ops = {
> > +       .get_func_proto  = rawir_event_func_proto,
> > +       .is_valid_access = rawir_event_is_valid_access
> > +};
> > +
> > +#define BPF_MAX_PROGS 64
> > +
> > +static int rc_dev_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog)
> > +{
> > +       struct ir_raw_event_ctrl *raw;
> > +       struct bpf_prog_list *pl;
> > +       int ret, size;
> > +
> > +       if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> > +               return -EINVAL;
> > +
> > +       ret = mutex_lock_interruptible(&ir_raw_handler_lock);
> > +       if (ret)
> > +               return ret;
> > +
> > +       raw = rcdev->raw;
> > +       if (!raw) {
> > +               ret = -ENODEV;
> > +               goto out;
> > +       }
> > +
> > +       size = 0;
> > +       list_for_each_entry(pl, &raw->progs, node) {
> > +               if (pl->prog == prog) {
> > +                       ret = -EEXIST;
> > +                       goto out;
> > +               }
> > +
> > +               size++;
> > +       }
> > +
> > +       if (size >= BPF_MAX_PROGS) {
> > +               ret = -E2BIG;
> > +               goto out;
> > +       }
> > +
> > +       pl = kmalloc(sizeof(*pl), GFP_KERNEL);
> > +       if (!pl) {
> > +               ret = -ENOMEM;
> > +               goto out;
> > +       }
> > +
> > +       pl->prog = prog;
> > +       list_add(&pl->node, &raw->progs);
> > +out:
> > +       mutex_unlock(&ir_raw_handler_lock);
> > +       return ret;
> > +}
> > +
> > +static int rc_dev_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog)
> > +{
> > +       struct ir_raw_event_ctrl *raw;
> > +       struct bpf_prog_list *pl, *tmp;
> > +       int ret;
> > +
> > +       if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> > +               return -EINVAL;
> > +
> > +       ret = mutex_lock_interruptible(&ir_raw_handler_lock);
> > +       if (ret)
> > +               return ret;
> > +
> > +       raw = rcdev->raw;
> > +       if (!raw) {
> > +               ret = -ENODEV;
> > +               goto out;
> > +       }
> > +
> > +       ret = -ENOENT;
> > +
> > +       list_for_each_entry_safe(pl, tmp, &raw->progs, node) {
> > +               if (pl->prog == prog) {
> > +                       list_del(&pl->node);
> > +                       kfree(pl);
> > +                       bpf_prog_put(prog);
> > +                       ret = 0;
> > +                       goto out;
> > +               }
> > +       }
> > +out:
> > +       mutex_unlock(&ir_raw_handler_lock);
> > +       return ret;
> > +}
> > +
> > +void rc_dev_bpf_init(struct rc_dev *rcdev)
> > +{
> > +       INIT_LIST_HEAD(&rcdev->raw->progs);
> > +}
> > +
> > +void rc_dev_bpf_run(struct rc_dev *rcdev, struct ir_raw_event ev)
> > +{
> > +       struct ir_raw_event_ctrl *raw = rcdev->raw;
> > +       struct bpf_prog_list *pl;
> > +
> > +       if (list_empty(&raw->progs))
> > +               return;
> > +
> > +       if (unlikely(ev.carrier_report)) {
> > +               raw->bpf_rawir_event.carrier = ev.carrier;
> > +               raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_CARRIER;
> > +       } else {
> > +               raw->bpf_rawir_event.duration = ev.duration;
> > +
> > +               if (ev.pulse)
> > +                       raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_PULSE;
> > +               else if (ev.timeout)
> > +                       raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_TIMEOUT;
> > +               else if (ev.reset)
> > +                       raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_RESET;
> > +               else
> > +                       raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_SPACE;
> > +       }
> > +
> > +       list_for_each_entry(pl, &raw->progs, node)
> > +               BPF_PROG_RUN(pl->prog, &raw->bpf_rawir_event);
> 
> Is the raw->progs protected by locks? It is possible that attaching/detaching
> could manipulate raw->progs at the same time? In perf/cgroup prog array
> case, the prog array run is protected by rcu and the dummy prog idea is
> to avoid the prog is skipped by reshuffling.

Yes, it is. The caller takes the appropriate lock. These functions could do
with some comments.

> Also, the original idea about using prog array is the least overhead since you
> want to BPF programs to execute as soon as possible.

Now, for our purposes the we're not bothered by a few milliseconds delay,
so I would pick whatever uses less cpu/memory overall. There is some overhead
in using rcu but the array is nice since it uses less memory than the
double-linked list, and less cache misses.

So I wanted bpf_prog_detach() to return -ENOENT if the prog was not in the list,
however bpf_prog_array_copy() and bpf_prog_array_delete_safe() do not return
anything useful for that.

Maybe I should look at adding a count-delta return to bpf_prog_array_copy(),
and a bool return to bpf_prog_array_delete_safe(). Or I could scan the array
an extra time to see if it is present. Any other ideas?

Also, BPF_F_OVERRIDE is not relevant for this but possibly BPF_F_ALLOW_MULTI
could invoke the same behaviour as for cgroups. What do you think?

> > +}
> > +
> > +void rc_dev_bpf_free(struct rc_dev *rcdev)
> > +{
> > +       struct bpf_prog_list *pl, *tmp;
> > +
> > +       list_for_each_entry_safe(pl, tmp, &rcdev->raw->progs, node) {
> > +               list_del(&pl->node);
> > +               bpf_prog_put(pl->prog);
> > +               kfree(pl);
> > +       }
> > +}
> > +
> > +int rc_dev_prog_attach(const union bpf_attr *attr)
> > +{
> > +       struct bpf_prog *prog;
> > +       struct rc_dev *rcdev;
> > +       int ret;
> > +
> > +       if (attr->attach_flags)
> > +               return -EINVAL;
> > +
> > +       prog = bpf_prog_get_type(attr->attach_bpf_fd,
> > +                                BPF_PROG_TYPE_RAWIR_EVENT);
> > +       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);
> > +       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)
> > +               return -EINVAL;
> > +
> > +       prog = bpf_prog_get_type(attr->attach_bpf_fd,
> > +                                BPF_PROG_TYPE_RAWIR_EVENT);
> > +       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);
> > +
> > +       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 ir_raw_event_ctrl *raw;
> > +       struct bpf_prog_list *pl;
> > +       struct rc_dev *rcdev;
> > +       u32 cnt, flags = 0, *ids, i;
> > +       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;
> 
> mutex_lock_interruptible() has not been called. You can just return here.

Yep.

> > +       }
> > +
> > +       ret = mutex_lock_interruptible(&ir_raw_handler_lock);
> > +       if (ret)
> > +               goto out;
> 
> Maybe you can rename label "out" to "unlock" since it
> is really an unlock and out?

Good point.

> > +
> > +       raw = rcdev->raw;
> > +       if (!raw) {
> > +               ret = -ENODEV;
> > +               goto out;
> > +       }
> > +
> > +       cnt = 0;
> > +       list_for_each_entry(pl, &raw->progs, node)
> > +               cnt++;
> > +
> > +       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) {
> > +               if (attr->query.prog_cnt < cnt)
> > +                       cnt = attr->query.prog_cnt;
> > +
> > +               ids = kmalloc_array(cnt, sizeof(u32), GFP_KERNEL);
> > +               if (!ids) {
> > +                       ret = -ENOMEM;
> > +                       goto out;
> > +               }
> > +
> > +               i = 0;
> > +               list_for_each_entry(pl, &raw->progs, node) {
> > +                       ids[i++] = pl->prog->aux->id;
> > +                       if (i == cnt)
> > +                               break;
> > +               }
> > +
> > +               ret = copy_to_user(prog_ids, ids, cnt * sizeof(u32));
> 
> Do you want to give user a chance to know that the "cnt" is not big enough
> by return -ENOSPC if cnt is smaller than the number of progs in the array?

True.

> > +       }
> > +out:
> > +       mutex_unlock(&ir_raw_handler_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..193540ded626 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>
> > @@ -816,4 +817,27 @@ 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 fd f = fdget(fd);
> > +       struct lirc_fh *fh;
> > +       struct rc_dev *dev;
> > +
> > +       if (!f.file)
> > +               return ERR_PTR(-EBADF);
> > +
> > +       if (f.file->f_op != &lirc_fops) {
> > +               fdput(f);
> > +               return ERR_PTR(-EINVAL);
> > +       }
> > +
> > +       fh = f.file->private_data;
> > +       dev = fh->rc;
> > +
> > +       get_device(&dev->dev);
> > +       fdput(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..148db73cfa0c 100644
> > --- a/drivers/media/rc/rc-core-priv.h
> > +++ b/drivers/media/rc/rc-core-priv.h
> > @@ -13,6 +13,7 @@
> >  #define        MAX_IR_EVENT_SIZE       512
> >
> >  #include <linux/slab.h>
> > +#include <uapi/linux/bpf.h>
> >  #include <media/rc-core.h>
> >
> >  /**
> > @@ -57,6 +58,11 @@ struct ir_raw_event_ctrl {
> >         /* raw decoder state follows */
> >         struct ir_raw_event prev_ev;
> >         struct ir_raw_event this_ev;
> > +
> > +#ifdef CONFIG_BPF_RAWIR_EVENT
> > +       struct bpf_rawir_event          bpf_rawir_event;
> > +       struct list_head                progs;
> > +#endif
> >         struct nec_dec {
> >                 int state;
> >                 unsigned count;
> > @@ -126,6 +132,9 @@ struct ir_raw_event_ctrl {
> >         } imon;
> >  };
> >
> > +/* Mutex for locking raw IR processing and handler change */
> > +extern struct mutex ir_raw_handler_lock;
> > +
> >  /* macros for IR decoders */
> >  static inline bool geq_margin(unsigned d1, unsigned d2, unsigned margin)
> >  {
> > @@ -288,6 +297,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 +309,18 @@ 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_BPF_RAWIR_EVENT
> > +void rc_dev_bpf_init(struct rc_dev *dev);
> > +void rc_dev_bpf_free(struct rc_dev *dev);
> > +void rc_dev_bpf_run(struct rc_dev *dev, struct ir_raw_event ev);
> > +#else
> > +static inline void rc_dev_bpf_init(struct rc_dev *dev) { }
> > +static inline void rc_dev_bpf_free(struct rc_dev *dev) { }
> > +static inline void rc_dev_bpf_run(struct rc_dev *dev, struct ir_raw_event ev)
> > +{ }
> > +#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..e68cdd4fbf5d 100644
> > --- a/drivers/media/rc/rc-ir-raw.c
> > +++ b/drivers/media/rc/rc-ir-raw.c
> > @@ -14,7 +14,7 @@
> >  static LIST_HEAD(ir_raw_client_list);
> >
> >  /* Used to handle IR raw handler extensions */
> > -static DEFINE_MUTEX(ir_raw_handler_lock);
> > +DEFINE_MUTEX(ir_raw_handler_lock);
> >  static LIST_HEAD(ir_raw_handler_list);
> >  static atomic64_t available_protocols = ATOMIC64_INIT(0);
> >
> > @@ -32,6 +32,7 @@ static int ir_raw_event_thread(void *data)
> >                                     handler->protocols || !handler->protocols)
> >                                         handler->decode(raw->dev, ev);
> >                         ir_lirc_raw_event(raw->dev, ev);
> > +                       rc_dev_bpf_run(raw->dev, ev);
> >                         raw->prev_ev = ev;
> >                 }
> >                 mutex_unlock(&ir_raw_handler_lock);
> > @@ -572,6 +573,7 @@ int ir_raw_event_prepare(struct rc_dev *dev)
> >         spin_lock_init(&dev->raw->edge_spinlock);
> >         timer_setup(&dev->raw->edge_handle, ir_raw_edge_handle, 0);
> >         INIT_KFIFO(dev->raw->kfifo);
> > +       rc_dev_bpf_init(dev);
> >
> >         return 0;
> >  }
> > @@ -621,9 +623,17 @@ void ir_raw_event_unregister(struct rc_dev *dev)
> >         list_for_each_entry(handler, &ir_raw_handler_list, list)
> >                 if (handler->raw_unregister)
> >                         handler->raw_unregister(dev);
> > -       mutex_unlock(&ir_raw_handler_lock);
> > +
> > +       rc_dev_bpf_free(dev);
> >
> >         ir_raw_event_free(dev);
> > +
> > +       /*
> > +        * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so
> > +        * ensure that the raw member is null on unlock; this is how
> > +        * "device gone" is checked.
> > +        */
> > +       mutex_unlock(&ir_raw_handler_lock);
> >  }
> >
> >  /*
> > diff --git a/include/linux/bpf_rcdev.h b/include/linux/bpf_rcdev.h
> > new file mode 100644
> > index 000000000000..17a30f30436a
> > --- /dev/null
> > +++ b/include/linux/bpf_rcdev.h
> > @@ -0,0 +1,30 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _BPF_RCDEV_H
> > +#define _BPF_RCDEV_H
> > +
> > +#include <linux/bpf.h>
> > +#include <uapi/linux/bpf.h>
> > +
> > +#ifdef CONFIG_BPF_RAWIR_EVENT
> > +int rc_dev_prog_attach(const union bpf_attr *attr);
> > +int rc_dev_prog_detach(const union bpf_attr *attr);
> > +int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr);
> > +#else
> > +static inline int rc_dev_prog_attach(const union bpf_attr *attr)
> > +{
> > +       return -EINVAL;
> > +}
> > +
> > +static inline int rc_dev_prog_detach(const union bpf_attr *attr)
> > +{
> > +       return -EINVAL;
> > +}
> > +
> > +static inline int rc_dev_prog_query(const union bpf_attr *attr,
> > +                                   union bpf_attr __user *uattr)
> > +{
> > +       return -EINVAL;
> > +}
> > +#endif
> > +
> > +#endif /* _BPF_RCDEV_H */
> > diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> > index b67f8793de0d..e2b1b12474d4 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_BPF_RAWIR_EVENT
> > +BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_EVENT, rawir_event)
> > +#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 d94d333a8225..243e141e8a5b 100644
> > --- a/include/uapi/linux/bpf.h
> > +++ b/include/uapi/linux/bpf.h
> > @@ -141,6 +141,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_EVENT,
> >  };
> >
> >  enum bpf_attach_type {
> > @@ -158,6 +159,7 @@ enum bpf_attach_type {
> >         BPF_CGROUP_INET6_CONNECT,
> >         BPF_CGROUP_INET4_POST_BIND,
> >         BPF_CGROUP_INET6_POST_BIND,
> > +       BPF_RAWIR_EVENT,
> >         __MAX_BPF_ATTACH_TYPE
> >  };
> >
> > @@ -1902,6 +1904,35 @@ union bpf_attr {
> >   *             egress otherwise). This is the only flag supported for now.
> >   *     Return
> >   *             **SK_PASS** on success, or **SK_DROP** on error.
> > + *
> > + * int bpf_rc_keydown(void *ctx, u32 protocol, u32 scancode, u32 toggle)
> > + *     Description
> > + *             Report decoded scancode with toggle value. For use in
> > + *             BPF_PROG_TYPE_RAWIR_EVENT, to report a successfully
> > + *             decoded scancode. This is will generate a keydown event,
> > + *             and a keyup event once the scancode is no longer repeated.
> > + *
> > + *             *ctx* pointer to bpf_rawir_event, *protocol* is decoded
> > + *             protocol (see RC_PROTO_* enum).
> > + *
> > + *             Some protocols include a toggle bit, in case the button
> > + *             was released and pressed again between consecutive scancodes,
> > + *             copy this bit into *toggle* if it exists, else set to 0.
> > + *
> > + *     Return
> > + *             Always return 0 (for now)
> > + *
> > + * int bpf_rc_repeat(void *ctx)
> > + *     Description
> > + *             Repeat the last decoded scancode; some IR protocols like
> > + *             NEC have a special IR message for repeat last button,
> > + *             in case user is holding a button down; the scancode is
> > + *             not repeated.
> > + *
> > + *             *ctx* pointer to bpf_rawir_event.
> > + *
> > + *     Return
> > + *             Always return 0 (for now)
> >   */
> >  #define __BPF_FUNC_MAPPER(FN)          \
> >         FN(unspec),                     \
> > @@ -1976,7 +2007,9 @@ union bpf_attr {
> >         FN(fib_lookup),                 \
> >         FN(sock_hash_update),           \
> >         FN(msg_redirect_hash),          \
> > -       FN(sk_redirect_hash),
> > +       FN(sk_redirect_hash),           \
> > +       FN(rc_repeat),                  \
> > +       FN(rc_keydown),
> >
> >  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> >   * function eBPF program intends to call
> > @@ -2043,6 +2076,26 @@ enum bpf_hdr_start_off {
> >         BPF_HDR_START_NET,
> >  };
> >
> > +/*
> > + * user accessible mirror of in-kernel ir_raw_event
> > + */
> > +#define BPF_RAWIR_EVENT_SPACE          0
> > +#define BPF_RAWIR_EVENT_PULSE          1
> > +#define BPF_RAWIR_EVENT_TIMEOUT                2
> > +#define BPF_RAWIR_EVENT_RESET          3
> > +#define BPF_RAWIR_EVENT_CARRIER                4
> > +#define BPF_RAWIR_EVENT_DUTY_CYCLE     5
> > +
> > +struct bpf_rawir_event {
> > +       union {
> > +               __u32   duration;
> > +               __u32   carrier;
> > +               __u32   duty_cycle;
> > +       };
> > +
> > +       __u32   type;
> > +};
> > +
> >  /* user accessible mirror of in-kernel sk_buff.
> >   * new fields can only be added to the end of this structure
> >   */
> > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > index e2aeb5e89f44..75c089f407c8 100644
> > --- a/kernel/bpf/syscall.c
> > +++ b/kernel/bpf/syscall.c
> > @@ -11,6 +11,7 @@
> >   */
> >  #include <linux/bpf.h>
> >  #include <linux/bpf_trace.h>
> > +#include <linux/bpf_rcdev.h>
> >  #include <linux/btf.h>
> >  #include <linux/syscalls.h>
> >  #include <linux/slab.h>
> > @@ -1567,6 +1568,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_EVENT:
> > +               return rc_dev_prog_attach(attr);
> >         default:
> >                 return -EINVAL;
> >         }
> > @@ -1637,6 +1640,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_EVENT:
> > +               return rc_dev_prog_detach(attr);
> >         default:
> >                 return -EINVAL;
> >         }
> > @@ -1684,6 +1689,8 @@ static int bpf_prog_query(const union bpf_attr *attr,
> >         case BPF_CGROUP_SOCK_OPS:
> >         case BPF_CGROUP_DEVICE:
> >                 break;
> > +       case BPF_RAWIR_EVENT:
> > +               return rc_dev_prog_query(attr, uattr);
> >         default:
> >                 return -EINVAL;
> >         }
> > --
> > 2.17.0
> >



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux