فهرست منبع

Update v5.4 patches

Changes:
  SAM:
    - Fix bug that can cause IRQ storm when SSAM wake IRQ gets triggered.
    - Continued internal restructuring.
    - Other small bug fixes.

Links:
- kernel: https://github.com/linux-surface/kernel/commit/9d7730b64c0610b043b7339a9d678ef0e7b1aa1e
- SAM: https://github.com/linux-surface/surface-aggregator-module/commit/b9fe8f28b70b8a399c43177629a6a73641a87808
Maximilian Luz 4 سال پیش
والد
کامیت
8e8811cd28

+ 1 - 1
patches/5.4/0001-surface3-power.patch

@@ -1,4 +1,4 @@
-From 7f67d3c72b8968df814d0b94c2f2f8c6b621fbdf Mon Sep 17 00:00:00 2001
+From 14170de6d5e067c52508fbd1964b586154611f8d Mon Sep 17 00:00:00 2001
 From: qzed <qzed@users.noreply.github.com>
 From: qzed <qzed@users.noreply.github.com>
 Date: Tue, 17 Sep 2019 17:17:56 +0200
 Date: Tue, 17 Sep 2019 17:17:56 +0200
 Subject: [PATCH 1/6] surface3-power
 Subject: [PATCH 1/6] surface3-power

+ 1 - 1
patches/5.4/0002-surface3-oemb.patch

@@ -1,4 +1,4 @@
-From e09db7fd5b8a8589a26b618fb1aa8903edc337fc Mon Sep 17 00:00:00 2001
+From 13678467f26a34ff4a23c0581233d894a05e65b5 Mon Sep 17 00:00:00 2001
 From: Chih-Wei Huang <cwhuang@linux.org.tw>
 From: Chih-Wei Huang <cwhuang@linux.org.tw>
 Date: Tue, 18 Sep 2018 11:01:37 +0800
 Date: Tue, 18 Sep 2018 11:01:37 +0800
 Subject: [PATCH 2/6] surface3-oemb
 Subject: [PATCH 2/6] surface3-oemb

+ 295 - 101
patches/5.4/0003-surface-sam.patch

@@ -1,4 +1,4 @@
-From 25788573eb6b46e5a525013489433a3a7a6ffffd Mon Sep 17 00:00:00 2001
+From cebebc5233f7fc9406332e77ada2950a4e1896ab Mon Sep 17 00:00:00 2001
 From: qzed <qzed@users.noreply.github.com>
 From: qzed <qzed@users.noreply.github.com>
 Date: Mon, 26 Aug 2019 01:11:08 +0200
 Date: Mon, 26 Aug 2019 01:11:08 +0200
 Subject: [PATCH 3/6] surface-sam
 Subject: [PATCH 3/6] surface-sam
@@ -22,12 +22,12 @@ Subject: [PATCH 3/6] surface-sam
  .../x86/surface_sam/surface_sam_sid_power.h   |   16 +
  .../x86/surface_sam/surface_sam_sid_power.h   |   16 +
  .../x86/surface_sam/surface_sam_sid_vhf.c     |  429 ++
  .../x86/surface_sam/surface_sam_sid_vhf.c     |  429 ++
  .../x86/surface_sam/surface_sam_sid_vhf.h     |   14 +
  .../x86/surface_sam/surface_sam_sid_vhf.h     |   14 +
- .../x86/surface_sam/surface_sam_ssh.c         | 5190 +++++++++++++++++
+ .../x86/surface_sam/surface_sam_ssh.c         | 5329 +++++++++++++++++
  .../x86/surface_sam/surface_sam_ssh.h         |  717 +++
  .../x86/surface_sam/surface_sam_ssh.h         |  717 +++
- .../x86/surface_sam/surface_sam_ssh_trace.h   |  532 ++
+ .../x86/surface_sam/surface_sam_ssh_trace.h   |  587 ++
  .../x86/surface_sam/surface_sam_vhf.c         |  266 +
  .../x86/surface_sam/surface_sam_vhf.c         |  266 +
  drivers/tty/serdev/core.c                     |  111 +-
  drivers/tty/serdev/core.c                     |  111 +-
- 23 files changed, 12349 insertions(+), 16 deletions(-)
+ 23 files changed, 12543 insertions(+), 16 deletions(-)
  create mode 100644 drivers/platform/x86/surface_sam/Kconfig
  create mode 100644 drivers/platform/x86/surface_sam/Kconfig
  create mode 100644 drivers/platform/x86/surface_sam/Makefile
  create mode 100644 drivers/platform/x86/surface_sam/Makefile
  create mode 100644 drivers/platform/x86/surface_sam/surface_sam_debugfs.c
  create mode 100644 drivers/platform/x86/surface_sam/surface_sam_debugfs.c
@@ -5732,10 +5732,10 @@ index 0000000000000..d956de5cf877a
 +#endif /* _SURFACE_SAM_SID_VHF_H */
 +#endif /* _SURFACE_SAM_SID_VHF_H */
 diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.c b/drivers/platform/x86/surface_sam/surface_sam_ssh.c
 diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.c b/drivers/platform/x86/surface_sam/surface_sam_ssh.c
 new file mode 100644
 new file mode 100644
-index 0000000000000..4144fe0b3d192
+index 0000000000000..4551b75570f22
 --- /dev/null
 --- /dev/null
 +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.c
 +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.c
-@@ -0,0 +1,5190 @@
+@@ -0,0 +1,5329 @@
 +// SPDX-License-Identifier: GPL-2.0-or-later
 +// SPDX-License-Identifier: GPL-2.0-or-later
 +/*
 +/*
 + * Surface Serial Hub (SSH) driver for communication with the Surface/System
 + * Surface Serial Hub (SSH) driver for communication with the Surface/System
@@ -5806,11 +5806,6 @@ index 0000000000000..4144fe0b3d192
 +	return rqid > 0 ? rqid + 1u : rqid + SSH_NUM_EVENTS + 1u;
 +	return rqid > 0 ? rqid + 1u : rqid + SSH_NUM_EVENTS + 1u;
 +}
 +}
 +
 +
-+static inline u16 ssh_event_to_rqid(u16 event)
-+{
-+	return event + 1u;
-+}
-+
 +static inline u16 ssh_rqid_to_event(u16 rqid)
 +static inline u16 ssh_rqid_to_event(u16 rqid)
 +{
 +{
 +	return rqid - 1u;
 +	return rqid - 1u;
@@ -5826,11 +5821,6 @@ index 0000000000000..4144fe0b3d192
 +	return tc;
 +	return tc;
 +}
 +}
 +
 +
-+static inline int ssh_tc_to_event(u8 tc)
-+{
-+	return ssh_rqid_to_event(ssh_tc_to_rqid(tc));
-+}
-+
 +static inline u8 ssh_channel_to_index(u8 channel)
 +static inline u8 ssh_channel_to_index(u8 channel)
 +{
 +{
 +	return channel - 1u;
 +	return channel - 1u;
@@ -5906,31 +5896,6 @@ index 0000000000000..4144fe0b3d192
 +	msgb->ptr = ptr;
 +	msgb->ptr = ptr;
 +}
 +}
 +
 +
-+static inline int msgb_alloc(struct msgbuf *msgb, size_t cap, gfp_t flags)
-+{
-+	u8 *buf;
-+
-+	buf = kzalloc(cap, flags);
-+	if (!buf)
-+		return -ENOMEM;
-+
-+	msgb_init(msgb, buf, cap);
-+	return 0;
-+}
-+
-+static inline void msgb_free(struct msgbuf *msgb)
-+{
-+	kfree(msgb->begin);
-+	msgb->begin = NULL;
-+	msgb->end = NULL;
-+	msgb->ptr = NULL;
-+}
-+
-+static inline void msgb_reset(struct msgbuf *msgb)
-+{
-+	msgb->ptr = msgb->begin;
-+}
-+
 +static inline size_t msgb_bytes_used(const struct msgbuf *msgb)
 +static inline size_t msgb_bytes_used(const struct msgbuf *msgb)
 +{
 +{
 +	return msgb->ptr - msgb->begin;
 +	return msgb->ptr - msgb->begin;
@@ -6204,11 +6169,6 @@ index 0000000000000..4144fe0b3d192
 +	buf->cap = 0;
 +	buf->cap = 0;
 +}
 +}
 +
 +
-+static inline void sshp_buf_reset(struct sshp_buf *buf)
-+{
-+	buf->len = 0;
-+}
-+
 +static inline void sshp_buf_drop(struct sshp_buf *buf, size_t n)
 +static inline void sshp_buf_drop(struct sshp_buf *buf, size_t n)
 +{
 +{
 +	memmove(buf->ptr, buf->ptr + n, buf->len - n);
 +	memmove(buf->ptr, buf->ptr + n, buf->len - n);
@@ -6623,10 +6583,8 @@ index 0000000000000..4144fe0b3d192
 +	}
 +	}
 +}
 +}
 +
 +
-+static inline int ssh_ptl_write_buf(struct ssh_ptl *ptl,
-+				    struct ssh_packet *packet,
-+				    const unsigned char *buf,
-+				    size_t count)
++static int ssh_ptl_write_buf(struct ssh_ptl *ptl, struct ssh_packet *packet,
++			     const unsigned char *buf, size_t count)
 +{
 +{
 +	int status;
 +	int status;
 +
 +
@@ -6643,7 +6601,7 @@ index 0000000000000..4144fe0b3d192
 +	return serdev_device_write_buf(ptl->serdev, buf, count);
 +	return serdev_device_write_buf(ptl->serdev, buf, count);
 +}
 +}
 +
 +
-+static inline void ssh_ptl_tx_inject_invalid_data(struct ssh_packet *packet)
++static void ssh_ptl_tx_inject_invalid_data(struct ssh_packet *packet)
 +{
 +{
 +	// ignore packets that don't carry any data (i.e. flush)
 +	// ignore packets that don't carry any data (i.e. flush)
 +	if (!packet->data.ptr || !packet->data.len)
 +	if (!packet->data.ptr || !packet->data.len)
@@ -6669,8 +6627,8 @@ index 0000000000000..4144fe0b3d192
 +	memset(packet->data.ptr, 0xb3, packet->data.len);
 +	memset(packet->data.ptr, 0xb3, packet->data.len);
 +}
 +}
 +
 +
-+static inline void ssh_ptl_rx_inject_invalid_syn(struct ssh_ptl *ptl,
-+						 struct ssam_span *data)
++static void ssh_ptl_rx_inject_invalid_syn(struct ssh_ptl *ptl,
++					  struct ssam_span *data)
 +{
 +{
 +	struct ssam_span frame;
 +	struct ssam_span frame;
 +
 +
@@ -6686,8 +6644,8 @@ index 0000000000000..4144fe0b3d192
 +	data->ptr[1] = 0xb3;	// set second byte of SYN to "random" value
 +	data->ptr[1] = 0xb3;	// set second byte of SYN to "random" value
 +}
 +}
 +
 +
-+static inline void ssh_ptl_rx_inject_invalid_data(struct ssh_ptl *ptl,
-+						  struct ssam_span *frame)
++static void ssh_ptl_rx_inject_invalid_data(struct ssh_ptl *ptl,
++					   struct ssam_span *frame)
 +{
 +{
 +	size_t payload_len, message_len;
 +	size_t payload_len, message_len;
 +	struct ssh_frame *sshf;
 +	struct ssh_frame *sshf;
@@ -6803,41 +6761,51 @@ index 0000000000000..4144fe0b3d192
 +}
 +}
 +
 +
 +
 +
-+static int ptl_alloc_ctrl_packet(struct ssh_ptl *ptl,
-+				 struct ssh_packet **packet,
-+				 struct ssam_span *buffer, gfp_t flags)
++static struct kmem_cache *ssh_ctrl_packet_cache;
++
++static int __init ssh_ctrl_packet_cache_init(void)
 +{
 +{
-+	// TODO: cache packets
-+	// - Potential problem with kmem_cache: Minimum alloc size of that is
-+	//   PAGE_SIZE (???), which is somewhat overkill here.
-+	// - Note: Mempool always tries to allocate with alloc callback first.
-+	//   Buffered objects are only chosen on allocation failure.
-+	//
-+	// => either kmem_cache or custom, try kmem_cache first and check via
-+	//    /proc/slabinfo
-+	//
-+	// Note: kmem_cache_create needs unique name
++	const unsigned int size = sizeof(struct ssh_packet) + SSH_MSG_LEN_CTRL;
++	const unsigned int align = __alignof__(struct ssh_packet);
++	struct kmem_cache *cache;
++
++	cache = kmem_cache_create("ssam_ctrl_packet", size, align, 0, NULL);
++	if (!cache)
++		return -ENOMEM;
++
++	ssh_ctrl_packet_cache = cache;
++	return 0;
++}
++
++static void __exit ssh_ctrl_packet_cache_destroy(void)
++{
++	kmem_cache_destroy(ssh_ctrl_packet_cache);
++	ssh_ctrl_packet_cache = NULL;
++}
 +
 +
-+	*packet = kzalloc(sizeof(struct ssh_packet) + SSH_MSG_LEN_CTRL, flags);
++static int ssh_ctrl_packet_alloc(struct ssh_packet **packet,
++				 struct ssam_span *buffer, gfp_t flags)
++{
++	*packet = kmem_cache_alloc(ssh_ctrl_packet_cache, flags);
 +	if (!*packet)
 +	if (!*packet)
 +		return -ENOMEM;
 +		return -ENOMEM;
 +
 +
 +	buffer->ptr = (u8 *)(*packet + 1);
 +	buffer->ptr = (u8 *)(*packet + 1);
 +	buffer->len = SSH_MSG_LEN_CTRL;
 +	buffer->len = SSH_MSG_LEN_CTRL;
 +
 +
++	trace_ssam_ctrl_packet_alloc(*packet, buffer->len);
 +	return 0;
 +	return 0;
 +}
 +}
 +
 +
-+static void ptl_free_ctrl_packet(struct ssh_packet *p)
++static void ssh_ctrl_packet_free(struct ssh_packet *p)
 +{
 +{
-+	// TODO: cache packets
-+
-+	kfree(p);
++	trace_ssam_ctrl_packet_free(p);
++	kmem_cache_free(ssh_ctrl_packet_cache, p);
 +}
 +}
 +
 +
 +static const struct ssh_packet_ops ssh_ptl_ctrl_packet_ops = {
 +static const struct ssh_packet_ops ssh_ptl_ctrl_packet_ops = {
 +	.complete = NULL,
 +	.complete = NULL,
-+	.release = ptl_free_ctrl_packet,
++	.release = ssh_ctrl_packet_free,
 +};
 +};
 +
 +
 +
 +
@@ -7687,7 +7655,7 @@ index 0000000000000..4144fe0b3d192
 +	struct msgbuf msgb;
 +	struct msgbuf msgb;
 +	int status;
 +	int status;
 +
 +
-+	status = ptl_alloc_ctrl_packet(ptl, &packet, &buf, GFP_KERNEL);
++	status = ssh_ctrl_packet_alloc(&packet, &buf, GFP_KERNEL);
 +	if (status) {
 +	if (status) {
 +		ptl_err(ptl, "ptl: failed to allocate ACK packet\n");
 +		ptl_err(ptl, "ptl: failed to allocate ACK packet\n");
 +		return;
 +		return;
@@ -7714,7 +7682,7 @@ index 0000000000000..4144fe0b3d192
 +	struct msgbuf msgb;
 +	struct msgbuf msgb;
 +	int status;
 +	int status;
 +
 +
-+	status = ptl_alloc_ctrl_packet(ptl, &packet, &buf, GFP_KERNEL);
++	status = ssh_ctrl_packet_alloc(&packet, &buf, GFP_KERNEL);
 +	if (status) {
 +	if (status) {
 +		ptl_err(ptl, "ptl: failed to allocate NAK packet\n");
 +		ptl_err(ptl, "ptl: failed to allocate NAK packet\n");
 +		return;
 +		return;
@@ -8021,7 +7989,7 @@ index 0000000000000..4144fe0b3d192
 +
 +
 +static inline struct device *ssh_ptl_get_device(struct ssh_ptl *ptl)
 +static inline struct device *ssh_ptl_get_device(struct ssh_ptl *ptl)
 +{
 +{
-+	return &ptl->serdev->dev;
++	return ptl->serdev ? &ptl->serdev->dev : NULL;
 +}
 +}
 +
 +
 +static int ssh_ptl_init(struct ssh_ptl *ptl, struct serdev_device *serdev,
 +static int ssh_ptl_init(struct ssh_ptl *ptl, struct serdev_device *serdev,
@@ -9393,6 +9361,11 @@ index 0000000000000..4144fe0b3d192
 +	return -ENOENT;
 +	return -ENOENT;
 +}
 +}
 +
 +
++static bool ssam_nf_refcount_empty(struct ssam_nf *nf)
++{
++	return RB_EMPTY_ROOT(&nf->refcount);
++}
++
 +static void ssam_nf_call(struct ssam_nf *nf, struct device *dev, u16 rqid,
 +static void ssam_nf_call(struct ssam_nf *nf, struct device *dev, u16 rqid,
 +			 struct ssam_event *event)
 +			 struct ssam_event *event)
 +{
 +{
@@ -9461,10 +9434,17 @@ index 0000000000000..4144fe0b3d192
 +
 +
 +
 +
 +struct ssam_cplt;
 +struct ssam_cplt;
++struct ssam_event_item;
++
++struct ssam_event_item_ops {
++	void (*free)(struct ssam_event_item *);
++};
 +
 +
 +struct ssam_event_item {
 +struct ssam_event_item {
 +	struct list_head node;
 +	struct list_head node;
 +	u16 rqid;
 +	u16 rqid;
++
++	struct ssam_event_item_ops ops;
 +	struct ssam_event event;	// must be last
 +	struct ssam_event event;	// must be last
 +};
 +};
 +
 +
@@ -9491,6 +9471,79 @@ index 0000000000000..4144fe0b3d192
 +};
 +};
 +
 +
 +
 +
++/**
++ * Maximum payload length for cached `ssam_event_item`s.
++ *
++ * This length has been chosen to be accomodate standard touchpad and keyboard
++ * input events. Events with larger payloads will be allocated separately.
++ */
++#define SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN	32
++
++static struct kmem_cache *ssam_event_item_cache;
++
++static int ssam_event_item_cache_init(void)
++{
++	const unsigned int size = sizeof(struct ssam_event_item)
++				  + SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN;
++	const unsigned int align = __alignof__(struct ssam_event_item);
++	struct kmem_cache *cache;
++
++	cache = kmem_cache_create("ssam_event_item", size, align, 0, NULL);
++	if (!cache)
++		return -ENOMEM;
++
++	ssam_event_item_cache = cache;
++	return 0;
++}
++
++static void ssam_event_item_cache_destroy(void)
++{
++	kmem_cache_destroy(ssam_event_item_cache);
++	ssam_event_item_cache = NULL;
++}
++
++static void __ssam_event_item_free_cached(struct ssam_event_item *item)
++{
++	kmem_cache_free(ssam_event_item_cache, item);
++}
++
++static void __ssam_event_item_free_generic(struct ssam_event_item *item)
++{
++	kfree(item);
++}
++
++static inline void ssam_event_item_free(struct ssam_event_item *item)
++{
++	trace_ssam_event_item_free(item);
++	item->ops.free(item);
++}
++
++static struct ssam_event_item *ssam_event_item_alloc(size_t len, gfp_t flags)
++{
++	struct ssam_event_item *item;
++
++	if (len <= SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN) {
++		item = kmem_cache_alloc(ssam_event_item_cache, GFP_KERNEL);
++		if (!item)
++			return NULL;
++
++		item->ops.free = __ssam_event_item_free_cached;
++	} else {
++		const size_t n = sizeof(struct ssam_event_item) + len;
++		item = kzalloc(n, GFP_KERNEL);
++		if (!item)
++			return NULL;
++
++		item->ops.free = __ssam_event_item_free_generic;
++	}
++
++	item->event.length = len;
++
++	trace_ssam_event_item_alloc(item, len);
++	return item;
++}
++
++
 +static void ssam_event_queue_push(struct ssam_event_queue *q,
 +static void ssam_event_queue_push(struct ssam_event_queue *q,
 +				  struct ssam_event_item *item)
 +				  struct ssam_event_item *item)
 +{
 +{
@@ -9586,7 +9639,7 @@ index 0000000000000..4144fe0b3d192
 +			return;
 +			return;
 +
 +
 +		ssam_nf_call(nf, dev, item->rqid, &item->event);
 +		ssam_nf_call(nf, dev, item->rqid, &item->event);
-+		kfree(item);
++		ssam_event_item_free(item);
 +	}
 +	}
 +
 +
 +	if (!ssam_event_queue_is_empty(queue))
 +	if (!ssam_event_queue_is_empty(queue))
@@ -9629,6 +9682,12 @@ index 0000000000000..4144fe0b3d192
 +
 +
 +static void ssam_cplt_destroy(struct ssam_cplt *cplt)
 +static void ssam_cplt_destroy(struct ssam_cplt *cplt)
 +{
 +{
++	/*
++	 * Note: destroy_workqueue ensures that all currently queued work will
++	 * be fully completed and the workqueue drained. This means that this
++	 * call will inherently also free any queued ssam_event_items, thus we
++	 * don't have to take care of that here explicitly.
++	 */
 +	destroy_workqueue(cplt->wq);
 +	destroy_workqueue(cplt->wq);
 +	ssam_nf_destroy(&cplt->event.notif);
 +	ssam_nf_destroy(&cplt->event.notif);
 +}
 +}
@@ -9679,7 +9738,7 @@ index 0000000000000..4144fe0b3d192
 +
 +
 +struct device *ssam_controller_device(struct ssam_controller *c)
 +struct device *ssam_controller_device(struct ssam_controller *c)
 +{
 +{
-+	return (c && c->rtl.ptl.serdev) ? &c->rtl.ptl.serdev->dev : NULL;
++	return ssh_rtl_get_device(&c->rtl);
 +}
 +}
 +EXPORT_SYMBOL_GPL(ssam_controller_device);
 +EXPORT_SYMBOL_GPL(ssam_controller_device);
 +
 +
@@ -9691,7 +9750,7 @@ index 0000000000000..4144fe0b3d192
 +	struct ssam_controller *ctrl = to_ssam_controller(rtl, rtl);
 +	struct ssam_controller *ctrl = to_ssam_controller(rtl, rtl);
 +	struct ssam_event_item *item;
 +	struct ssam_event_item *item;
 +
 +
-+	item = kzalloc(sizeof(struct ssam_event_item) + data->len, GFP_KERNEL);
++	item = ssam_event_item_alloc(data->len, GFP_KERNEL);
 +	if (!item)
 +	if (!item)
 +		return;
 +		return;
 +
 +
@@ -9700,7 +9759,6 @@ index 0000000000000..4144fe0b3d192
 +	item->event.command_id = cmd->cid;
 +	item->event.command_id = cmd->cid;
 +	item->event.instance_id = cmd->iid;
 +	item->event.instance_id = cmd->iid;
 +	item->event.channel = cmd->chn_in;
 +	item->event.channel = cmd->chn_in;
-+	item->event.length  = data->len;
 +	memcpy(&item->event.data[0], data->ptr, data->len);
 +	memcpy(&item->event.data[0], data->ptr, data->len);
 +
 +
 +	ssam_cplt_submit_event(&ctrl->cplt, item);
 +	ssam_cplt_submit_event(&ctrl->cplt, item);
@@ -9711,6 +9769,10 @@ index 0000000000000..4144fe0b3d192
 +};
 +};
 +
 +
 +
 +
++static bool ssam_notifier_empty(struct ssam_controller *ctrl);
++static void ssam_notifier_unregister_all(struct ssam_controller *ctrl);
++
++
 +#define SSAM_SSH_DSM_REVISION	0
 +#define SSAM_SSH_DSM_REVISION	0
 +#define SSAM_SSH_DSM_NOTIF_D0	8
 +#define SSAM_SSH_DSM_NOTIF_D0	8
 +static const guid_t SSAM_SSH_DSM_UUID = GUID_INIT(0xd5e383e1, 0xd892, 0x4a76,
 +static const guid_t SSAM_SSH_DSM_UUID = GUID_INIT(0xd5e383e1, 0xd892, 0x4a76,
@@ -9786,7 +9848,6 @@ index 0000000000000..4144fe0b3d192
 +	// initialize request and packet transmission layers
 +	// initialize request and packet transmission layers
 +	status = ssh_rtl_init(&ctrl->rtl, serdev, &ssam_rtl_ops);
 +	status = ssh_rtl_init(&ctrl->rtl, serdev, &ssam_rtl_ops);
 +	if (status) {
 +	if (status) {
-+		ssam_cplt_flush(&ctrl->cplt);
 +		ssam_cplt_destroy(&ctrl->cplt);
 +		ssam_cplt_destroy(&ctrl->cplt);
 +		return status;
 +		return status;
 +	}
 +	}
@@ -9832,9 +9893,22 @@ index 0000000000000..4144fe0b3d192
 +			 status);
 +			 status);
 +	}
 +	}
 +
 +
-+	// flush out all currently completing requests and events
++	// try to flush out all currently completing requests and events
 +	ssam_cplt_flush(&ctrl->cplt);
 +	ssam_cplt_flush(&ctrl->cplt);
 +
 +
++	/*
++	 * We expect all notifiers to have been removed by the respective client
++	 * driver that set them up at this point. If this warning occurs, some
++	 * client driver has not done that...
++	 */
++	WARN_ON(!ssam_notifier_empty(ctrl));
++
++	/*
++	 * Nevertheless, we should still take care of drivers that don't behave
++	 * well. Thus disable all enabled events, unregister all notifiers.
++	 */
++	ssam_notifier_unregister_all(ctrl);
++
 +	// cancel rem. requests, ensure no new ones can be queued, stop threads
 +	// cancel rem. requests, ensure no new ones can be queued, stop threads
 +	ssh_rtl_tx_flush(&ctrl->rtl);
 +	ssh_rtl_tx_flush(&ctrl->rtl);
 +	ssh_rtl_shutdown(&ctrl->rtl);
 +	ssh_rtl_shutdown(&ctrl->rtl);
@@ -9848,13 +9922,12 @@ index 0000000000000..4144fe0b3d192
 +		return;
 +		return;
 +
 +
 +	/*
 +	/*
-+	 * Ensure _all_ events are completed. New ones could still have been
-+	 * received after the previous flush in ssam_controller_shutdown, before
-+	 * the request transport layer has been shut down. At this point we can
-+	 * be sure that no new requests will be queued for completion after this
-+	 * call.
++	 * Note: New events could still have been received after the previous
++	 * flush in ssam_controller_shutdown, before the request transport layer
++	 * has been shut down. At this point, after the shutdown, we can be sure
++	 * that no new events will be queued. The call to ssam_cplt_destroy will
++	 * ensure that those remaining are being completed and freed.
 +	 */
 +	 */
-+	ssam_cplt_flush(&ctrl->cplt);
 +
 +
 +	// actually free resources
 +	// actually free resources
 +	ssam_cplt_destroy(&ctrl->cplt);
 +	ssam_cplt_destroy(&ctrl->cplt);
@@ -9863,7 +9936,7 @@ index 0000000000000..4144fe0b3d192
 +	smp_store_release(&ctrl->state, SSAM_CONTROLLER_UNINITIALIZED);
 +	smp_store_release(&ctrl->state, SSAM_CONTROLLER_UNINITIALIZED);
 +}
 +}
 +
 +
-+static inline int ssam_controller_suspend(struct ssam_controller *ctrl)
++static int ssam_controller_suspend(struct ssam_controller *ctrl)
 +{
 +{
 +	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED)
 +	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED)
 +		return -EINVAL;
 +		return -EINVAL;
@@ -9873,7 +9946,7 @@ index 0000000000000..4144fe0b3d192
 +	return 0;
 +	return 0;
 +}
 +}
 +
 +
-+static inline int ssam_controller_resume(struct ssam_controller *ctrl)
++static int ssam_controller_resume(struct ssam_controller *ctrl)
 +{
 +{
 +	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_SUSPENDED)
 +	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_SUSPENDED)
 +		return -EINVAL;
 +		return -EINVAL;
@@ -10353,14 +10426,16 @@ index 0000000000000..4144fe0b3d192
 +	if (!ssh_rqid_is_event(rqid))
 +	if (!ssh_rqid_is_event(rqid))
 +		return -EINVAL;
 +		return -EINVAL;
 +
 +
-+	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED)
-+		return -ENXIO;
-+
 +	nf = &ctrl->cplt.event.notif;
 +	nf = &ctrl->cplt.event.notif;
 +	nf_head = &nf->head[ssh_rqid_to_event(rqid)];
 +	nf_head = &nf->head[ssh_rqid_to_event(rqid)];
 +
 +
 +	mutex_lock(&nf->lock);
 +	mutex_lock(&nf->lock);
 +
 +
++	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) {
++		mutex_unlock(&nf->lock);
++		return -ENXIO;
++	}
++
 +	rc = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
 +	rc = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
 +	if (rc < 0) {
 +	if (rc < 0) {
 +		mutex_unlock(&nf->lock);
 +		mutex_unlock(&nf->lock);
@@ -10407,14 +10482,16 @@ index 0000000000000..4144fe0b3d192
 +	if (!ssh_rqid_is_event(rqid))
 +	if (!ssh_rqid_is_event(rqid))
 +		return -EINVAL;
 +		return -EINVAL;
 +
 +
-+	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED)
-+		return -ENXIO;
-+
 +	nf = &ctrl->cplt.event.notif;
 +	nf = &ctrl->cplt.event.notif;
 +	nf_head = &nf->head[ssh_rqid_to_event(rqid)];
 +	nf_head = &nf->head[ssh_rqid_to_event(rqid)];
 +
 +
 +	mutex_lock(&nf->lock);
 +	mutex_lock(&nf->lock);
 +
 +
++	if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) {
++		mutex_unlock(&nf->lock);
++		return -ENXIO;
++	}
++
 +	rc = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
 +	rc = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
 +	if (rc < 0) {
 +	if (rc < 0) {
 +		mutex_unlock(&nf->lock);
 +		mutex_unlock(&nf->lock);
@@ -10438,6 +10515,33 @@ index 0000000000000..4144fe0b3d192
 +}
 +}
 +EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
 +EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
 +
 +
++static bool ssam_notifier_empty(struct ssam_controller *ctrl)
++{
++	struct ssam_nf *nf = &ctrl->cplt.event.notif;
++	bool result;
++
++	mutex_lock(&nf->lock);
++	result = ssam_nf_refcount_empty(nf);
++	mutex_unlock(&nf->lock);
++
++	return result;
++}
++
++static void ssam_notifier_unregister_all(struct ssam_controller *ctrl)
++{
++	struct ssam_nf *nf = &ctrl->cplt.event.notif;
++	struct ssam_nf_refcount_entry *pos, *n;
++
++	mutex_lock(&nf->lock);
++	rbtree_postorder_for_each_entry_safe(pos, n, &nf->refcount, node) {
++		// ignore errors, will get logged in call
++		ssam_ssh_event_disable(ctrl, pos->key.reg, pos->key.id, 0);
++		kfree(pos);
++	}
++	nf->refcount = RB_ROOT;
++	mutex_unlock(&nf->lock);
++}
++
 +
 +
 +/* -- Wakeup IRQ. ----------------------------------------------------------- */
 +/* -- Wakeup IRQ. ----------------------------------------------------------- */
 +
 +
@@ -10480,12 +10584,24 @@ index 0000000000000..4144fe0b3d192
 +
 +
 +static int ssam_irq_setup(struct ssam_controller *ctrl)
 +static int ssam_irq_setup(struct ssam_controller *ctrl)
 +{
 +{
-+	const int irqf = IRQF_SHARED | IRQF_ONESHOT;
 +	struct device *dev = ssam_controller_device(ctrl);
 +	struct device *dev = ssam_controller_device(ctrl);
 +	struct gpio_desc *gpiod;
 +	struct gpio_desc *gpiod;
 +	int irq;
 +	int irq;
 +	int status;
 +	int status;
 +
 +
++	/*
++	 * The actual GPIO interrupt is declared in ACPI as TRIGGER_HIGH.
++	 * However, the GPIO line only gets reset by sending the GPIO callback
++	 * command to SAM (or alternatively the display-on notification). As
++	 * proper handling for this interrupt is not implemented yet, leaving
++	 * the IRQ at TRIGGER_HIGH would cause an IRQ storm (as the callback
++	 * never gets sent and thus the line line never gets reset). To avoid
++	 * this, mark the IRQ as TRIGGER_RISING for now, only creating a single
++	 * interrupt, and let the SAM resume callback during the controller
++	 * resume process clear it.
++	 */
++	const int irqf = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING;
++
 +	gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS);
 +	gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS);
 +	if (IS_ERR(gpiod))
 +	if (IS_ERR(gpiod))
 +		return PTR_ERR(gpiod);
 +		return PTR_ERR(gpiod);
@@ -10902,12 +11018,35 @@ index 0000000000000..4144fe0b3d192
 +
 +
 +static int __init surface_sam_ssh_init(void)
 +static int __init surface_sam_ssh_init(void)
 +{
 +{
-+	return serdev_device_driver_register(&surface_sam_ssh);
++	int status;
++
++	status = ssh_ctrl_packet_cache_init();
++	if (status)
++		goto err_cpkg;
++
++	status = ssam_event_item_cache_init();
++	if (status)
++		goto err_evitem;
++
++	status = serdev_device_driver_register(&surface_sam_ssh);
++	if (status)
++		goto err_register;
++
++	return 0;
++
++err_register:
++	ssam_event_item_cache_destroy();
++err_evitem:
++	ssh_ctrl_packet_cache_destroy();
++err_cpkg:
++	return status;
 +}
 +}
 +
 +
 +static void __exit surface_sam_ssh_exit(void)
 +static void __exit surface_sam_ssh_exit(void)
 +{
 +{
 +	serdev_device_driver_unregister(&surface_sam_ssh);
 +	serdev_device_driver_unregister(&surface_sam_ssh);
++	ssam_event_item_cache_destroy();
++	ssh_ctrl_packet_cache_destroy();
 +}
 +}
 +
 +
 +/*
 +/*
@@ -11651,10 +11790,10 @@ index 0000000000000..ba57adb2a3c9d
 +#endif /* _SURFACE_SAM_SSH_H */
 +#endif /* _SURFACE_SAM_SSH_H */
 diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h
 diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h
 new file mode 100644
 new file mode 100644
-index 0000000000000..4755183fa423b
+index 0000000000000..8ea9a2fc99d7e
 --- /dev/null
 --- /dev/null
 +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h
 +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h
-@@ -0,0 +1,532 @@
+@@ -0,0 +1,587 @@
 +#undef TRACE_SYSTEM
 +#undef TRACE_SYSTEM
 +#define TRACE_SYSTEM surface_sam_ssh
 +#define TRACE_SYSTEM surface_sam_ssh
 +
 +
@@ -12125,6 +12264,55 @@ index 0000000000000..4755183fa423b
 +	)
 +	)
 +
 +
 +
 +
++DECLARE_EVENT_CLASS(ssam_alloc_class,
++	TP_PROTO(void *ptr, size_t len),
++
++	TP_ARGS(ptr, len),
++
++	TP_STRUCT__entry(
++		__array(char, uid, SSAM_PTR_UID_LEN)
++		__field(size_t, len)
++	),
++
++	TP_fast_assign(
++		ssam_trace_ptr_uid(ptr, __entry->uid);
++		__entry->len = len;
++	),
++
++	TP_printk("uid=%s, len=%zu", __entry->uid, __entry->len)
++);
++
++#define DEFINE_SSAM_ALLOC_EVENT(name)					\
++	DEFINE_EVENT(ssam_alloc_class, ssam_##name,			\
++		TP_PROTO(void *ptr, size_t len),			\
++		TP_ARGS(ptr, len)					\
++	)
++
++
++DECLARE_EVENT_CLASS(ssam_free_class,
++	TP_PROTO(void *ptr),
++
++	TP_ARGS(ptr),
++
++	TP_STRUCT__entry(
++		__array(char, uid, SSAM_PTR_UID_LEN)
++		__field(size_t, len)
++	),
++
++	TP_fast_assign(
++		ssam_trace_ptr_uid(ptr, __entry->uid);
++	),
++
++	TP_printk("uid=%s", __entry->uid)
++);
++
++#define DEFINE_SSAM_FREE_EVENT(name)					\
++	DEFINE_EVENT(ssam_free_class, ssam_##name,			\
++		TP_PROTO(void *ptr),					\
++		TP_ARGS(ptr)						\
++	)
++
++
 +DECLARE_EVENT_CLASS(ssam_generic_uint_class,
 +DECLARE_EVENT_CLASS(ssam_generic_uint_class,
 +	TP_PROTO(const char* property, unsigned int value),
 +	TP_PROTO(const char* property, unsigned int value),
 +
 +
@@ -12177,6 +12365,12 @@ index 0000000000000..4755183fa423b
 +DEFINE_SSAM_FRAME_EVENT(ei_rx_corrupt_data);
 +DEFINE_SSAM_FRAME_EVENT(ei_rx_corrupt_data);
 +DEFINE_SSAM_REQUEST_EVENT(ei_rx_drop_response);
 +DEFINE_SSAM_REQUEST_EVENT(ei_rx_drop_response);
 +
 +
++DEFINE_SSAM_ALLOC_EVENT(ctrl_packet_alloc);
++DEFINE_SSAM_FREE_EVENT(ctrl_packet_free);
++
++DEFINE_SSAM_ALLOC_EVENT(event_item_alloc);
++DEFINE_SSAM_FREE_EVENT(event_item_free);
++
 +#endif /* _SURFACE_SAM_SSH_TRACE_H */
 +#endif /* _SURFACE_SAM_SSH_TRACE_H */
 +
 +
 +/* This part must be outside protection */
 +/* This part must be outside protection */

+ 1 - 1
patches/5.4/0004-surface-sam-over-hid.patch

@@ -1,4 +1,4 @@
-From 14bc7782c703218156fe5fd6b8df97a5f25f8d3e Mon Sep 17 00:00:00 2001
+From e0785bb83ac72633ffed972f49e580a38a2eab2f Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 25 Jul 2020 17:19:53 +0200
 Date: Sat, 25 Jul 2020 17:19:53 +0200
 Subject: [PATCH 4/6] surface-sam-over-hid
 Subject: [PATCH 4/6] surface-sam-over-hid

+ 1 - 1
patches/5.4/0005-wifi.patch

@@ -1,4 +1,4 @@
-From 25533d6a5d98372ae1a7e04c7fb0fd35bd4d19f6 Mon Sep 17 00:00:00 2001
+From de6c10d7cb76f7303b91fafe33572b0fdb705395 Mon Sep 17 00:00:00 2001
 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com>
 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com>
 Date: Thu, 20 Feb 2020 16:51:11 +0900
 Date: Thu, 20 Feb 2020 16:51:11 +0900
 Subject: [PATCH 5/6] wifi
 Subject: [PATCH 5/6] wifi

+ 1 - 1
patches/5.4/0006-ipts.patch

@@ -1,4 +1,4 @@
-From 2a57236358e1eaf2bdb4f3f6a247d1a8e25afd8b Mon Sep 17 00:00:00 2001
+From eed561223bfe2cd6ff92e65ba89211a4bad92517 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Mon, 27 Jan 2020 21:16:20 +0100
 Date: Mon, 27 Jan 2020 21:16:20 +0100
 Subject: [PATCH 6/6] ipts
 Subject: [PATCH 6/6] ipts