Reduce memory requirement for MT syncing
authorPeter Hutterer <peter.hutterer@who-t.net>
Thu, 27 Feb 2014 03:10:35 +0000 (13:10 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Wed, 5 Mar 2014 23:21:02 +0000 (09:21 +1000)
Changes the algorithm: before we'd ioctl all axes for all slots, then generate
events for all slots one-by-one.

Now we ioctl the slot state for each axis, copy the new event value into
the device and mark a bitfield that we've updated the value. Then loop through
the slots and generate events where changed.

Side-effect: this makes it easy to check if anything in the slot has updated,
so we can skip empty slot events during sync.

Min memory requirement for the state storage was:
  MAX_SLOTS  * (ABS_MT_CNT + 1) * sizeof(int) = 1980
Min memory requirement now:
  (ABS_MT_CNT + 1) * sizeof(int) + NLONGS((MAX_SLOTS * ABS_MT_CNT) bits) = 544

This is sigsafe code, so this was stack memory. Reducing the requirement
allows us to up MAX_SLOTS in the future if we need to.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
libevdev/libevdev.c

index dd03d771bc67fcd7ebeb7518dc80853d826c5d81..a77c466e6ac5566b5adbd6ea59aa649ed3487aa0 100644 (file)
@@ -552,26 +552,26 @@ static int
 sync_mt_state(struct libevdev *dev, int create_events)
 {
        int rc;
-       int i;
+       int axis, slot;
        int ioctl_success = 0;
        struct mt_state {
                int code;
                int val[MAX_SLOTS];
-       } mt_state[ABS_MT_CNT];
+       } mt_state;
+       unsigned long slot_update[NLONGS(MAX_SLOTS * ABS_MT_CNT)] = {0};
 
-       memset(&mt_state, 0, sizeof(mt_state));
+#define AXISBIT(_slot, _axis) (_slot * ABS_MT_CNT + _axis - ABS_MT_MIN)
 
-       for (i = ABS_MT_MIN; i <= ABS_MT_MAX; i++) {
-               int idx;
-               if (i == ABS_MT_SLOT)
+       for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) {
+               if (axis == ABS_MT_SLOT)
                        continue;
 
-               if (!libevdev_has_event_code(dev, EV_ABS, i))
+               if (!libevdev_has_event_code(dev, EV_ABS, axis))
                        continue;
 
-               idx = i - ABS_MT_MIN;
-               mt_state[idx].code = i;
-               rc = ioctl(dev->fd, EVIOCGMTSLOTS(sizeof(struct mt_state)), &mt_state[idx]);
+               memset(&mt_state, 0, sizeof(mt_state));
+               mt_state.code = axis;
+               rc = ioctl(dev->fd, EVIOCGMTSLOTS(sizeof(struct mt_state)), &mt_state);
                if (rc < 0) {
                        /* if the first ioctl fails with -EINVAL, chances are the kernel
                           doesn't support the ioctl. Simply continue */
@@ -579,39 +579,49 @@ sync_mt_state(struct libevdev *dev, int create_events)
                                rc = 0;
                        } else /* if the second, ... ioctl fails, really fail */
                                goto out;
-               } else if (ioctl_success == 0)
-                       ioctl_success = 1;
-       }
+               } else {
+                       if (ioctl_success == 0)
+                               ioctl_success = 1;
+
+                       for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) {
+
+                               if (*slot_value(dev, slot, axis) == mt_state.val[slot])
+                                       continue;
+
+                               *slot_value(dev, slot, axis) = mt_state.val[slot];
+
+                               set_bit(slot_update, AXISBIT(slot, axis));
+                               /* note that this slot has updates */
+                               set_bit(slot_update, AXISBIT(slot, ABS_MT_SLOT));
+                       }
 
-       for (i = 0; i < min(dev->num_slots, MAX_SLOTS); i++) {
-               int j;
-               struct input_event *ev;
 
-               if (create_events) {
-                       ev = queue_push(dev);
-                       init_event(dev, ev, EV_ABS, ABS_MT_SLOT, i);
                }
+       }
 
-               for (j = ABS_MT_MIN; j <= ABS_MT_MAX; j++) {
-                       int jdx = j - ABS_MT_MIN;
+       for (slot = 0; create_events && slot < min(dev->num_slots, MAX_SLOTS); slot++) {
+               struct input_event *ev;
 
-                       if (j == ABS_MT_SLOT)
-                               continue;
+               if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT)))
+                       continue;
 
-                       if (!libevdev_has_event_code(dev, EV_ABS, j))
-                               continue;
+               ev = queue_push(dev);
+               init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot);
 
-                       if (*slot_value(dev, i, j) == mt_state[jdx].val[i])
+               for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) {
+                       if (axis == ABS_MT_SLOT ||
+                           !libevdev_has_event_code(dev, EV_ABS, axis))
                                continue;
 
-                       if (create_events) {
+                       if (bit_is_set(slot_update, AXISBIT(slot, axis))) {
                                ev = queue_push(dev);
-                               init_event(dev, ev, EV_ABS, j, mt_state[jdx].val[i]);
+                               init_event(dev, ev, EV_ABS, axis, *slot_value(dev, slot, axis));
                        }
-                       *slot_value(dev, i, j) = mt_state[jdx].val[i];
                }
        }
 
+#undef AXISBIT
+
        rc = 0;
 out:
        return rc ? -errno : 0;