Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df2d62be authored by Sean Tranchetti's avatar Sean Tranchetti Committed by Divya Sharma
Browse files

rmnet_core: Check for contiguous QMAP headers during deaggregation



After the introduction of scatter gather support, it's possible that the
QMAP headers may be split across pages. This patch adds proper handling
for the headers during deaggregation from the original SKB from the
physical device when building the descriptors used elsewhere in the rmnet
driver.

Change-Id: If62cf8b4993fe01b6f43e9f16f08c134d18437b8
Signed-off-by: default avatarSean Tranchetti <stranche@codeaurora.org>
parent 578beef0
Loading
Loading
Loading
Loading
+33 −7
Original line number Diff line number Diff line
@@ -558,20 +558,36 @@ int rmnet_frag_flow_command(struct rmnet_frag_descriptor *frag_desc,
}
EXPORT_SYMBOL(rmnet_frag_flow_command);

static int rmnet_frag_deaggregate_one(struct skb_shared_info *shinfo,
static int rmnet_frag_deaggregate_one(struct sk_buff *skb,
				      struct rmnet_port *port,
				      struct list_head *list,
				      u32 start_frag)
{
	struct skb_shared_info *shinfo = skb_shinfo(skb);
	struct rmnet_frag_descriptor *frag_desc;
	struct rmnet_map_header *maph;
	struct rmnet_map_header *maph, __maph;
	skb_frag_t *frag;
	u32 i;
	u32 pkt_len;
	int rc;

	frag = &shinfo->frags[start_frag];
	/* Grab the QMAP header. Careful, as there's no guarantee that it's
	 * continugous!
	 */
	if (likely(skb_frag_size(frag) >= sizeof(*maph))) {
		maph = skb_frag_address(frag);
	} else {
		/* The header's split across pages. We can rebuild it.
		 * Probably not faster or stronger than before. But certainly
		 * more linear.
		 */
		if (skb_copy_bits(skb, 0, &__maph, sizeof(&__maph)) < 0)
			return -1;

		maph = &__maph;
	}

	pkt_len = ntohs(maph->pkt_len);
	/* Catch empty frames */
	if (!pkt_len)
@@ -590,9 +606,19 @@ static int rmnet_frag_deaggregate_one(struct skb_shared_info *shinfo,
		u32 hsize = 0;
		u8 type;

		type = ((struct rmnet_map_v5_coal_header *)
			(maph + 1))->header_type;
		switch (type) {
		/* Check the type. This seems like should be overkill for less
		 * than a single byte, doesn't it?
		 */
		if (likely(skb_frag_size(frag) >= sizeof(*maph) + 1)) {
			type = *((u8 *)maph + sizeof(*maph));
		} else {
			if (skb_copy_bits(skb, sizeof(*maph), &type,
					  sizeof(type)) < 0)
				return -1;
		}

		/* Type only uses the first 7 bits */
		switch ((type & 0xFE) >> 1) {
		case RMNET_MAP_HEADER_TYPE_COALESCING:
			hsize = sizeof(struct rmnet_map_v5_coal_header);
			break;
@@ -647,7 +673,7 @@ void rmnet_frag_deaggregate(struct sk_buff *skb, struct rmnet_port *port,
	int rc;

	while (i < shinfo->nr_frags) {
		rc = rmnet_frag_deaggregate_one(shinfo, port, list, i);
		rc = rmnet_frag_deaggregate_one(skb, port, list, i);
		if (rc < 0)
			return;