msm: ipa: Prevent multiple header deletion from user space

An IPA header or processing context can be added once
and later deleted once from user space.
Multiple deletion may cause invalid state of the headers
software cache.

Change-Id: Ic0b8472b7fd8a76233a007d90c832af726184574
CRs-fixed: 1097714
Signed-off-by: Ghanim Fodi <gfodi@codeaurora.org>
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 0dd5542..feec2aa 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -733,7 +733,8 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (ipa2_del_hdr((struct ipa_ioc_del_hdr *)param)) {
+		if (ipa2_del_hdr_by_user((struct ipa_ioc_del_hdr *)param,
+			true)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1417,8 +1418,8 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (ipa2_del_hdr_proc_ctx(
-			(struct ipa_ioc_del_hdr_proc_ctx *)param)) {
+		if (ipa2_del_hdr_proc_ctx_by_user(
+			(struct ipa_ioc_del_hdr_proc_ctx *)param, true)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -2715,7 +2716,7 @@
 	if (ipa_ctx->dflt_v4_rt_rule_hdl)
 		__ipa_del_rt_rule(ipa_ctx->dflt_v4_rt_rule_hdl);
 	if (ipa_ctx->excp_hdr_hdl)
-		__ipa_del_hdr(ipa_ctx->excp_hdr_hdl);
+		__ipa_del_hdr(ipa_ctx->excp_hdr_hdl, false);
 	ipa2_teardown_sys_pipe(ipa_ctx->clnt_hdl_cmd);
 fail_cmd:
 	return result;
@@ -2727,7 +2728,7 @@
 	ipa2_teardown_sys_pipe(ipa_ctx->clnt_hdl_data_in);
 	__ipa_del_rt_rule(ipa_ctx->dflt_v6_rt_rule_hdl);
 	__ipa_del_rt_rule(ipa_ctx->dflt_v4_rt_rule_hdl);
-	__ipa_del_hdr(ipa_ctx->excp_hdr_hdl);
+	__ipa_del_hdr(ipa_ctx->excp_hdr_hdl, false);
 	ipa2_teardown_sys_pipe(ipa_ctx->clnt_hdl_cmd);
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index ab14cb7..5bc106a 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -741,7 +741,8 @@
 	return -EPERM;
 }
 
-static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr)
+static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl,
+	bool release_hdr, bool by_user)
 {
 	struct ipa_hdr_proc_ctx_entry *entry;
 	struct ipa_hdr_proc_ctx_tbl *htbl = &ipa_ctx->hdr_proc_ctx_tbl;
@@ -755,6 +756,14 @@
 	IPADBG("del ctx proc cnt=%d ofst=%d\n",
 		htbl->proc_ctx_cnt, entry->offset_entry->offset);
 
+	if (by_user && entry->user_deleted) {
+		IPAERR("proc_ctx already deleted by user\n");
+		return -EINVAL;
+	}
+
+	if (by_user)
+		entry->user_deleted = true;
+
 	if (--entry->ref_cnt) {
 		IPADBG("proc_ctx_hdl %x ref_cnt %d\n",
 			proc_ctx_hdl, entry->ref_cnt);
@@ -762,7 +771,7 @@
 	}
 
 	if (release_hdr)
-		__ipa_del_hdr(entry->hdr->id);
+		__ipa_del_hdr(entry->hdr->id, false);
 
 	/* move the offset entry to appropriate free list */
 	list_move(&entry->offset_entry->link,
@@ -779,7 +788,7 @@
 }
 
 
-int __ipa_del_hdr(u32 hdr_hdl)
+int __ipa_del_hdr(u32 hdr_hdl, bool by_user)
 {
 	struct ipa_hdr_entry *entry;
 	struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl;
@@ -790,7 +799,7 @@
 		return -EINVAL;
 	}
 
-	if (!entry || (entry->cookie != IPA_COOKIE)) {
+	if (entry->cookie != IPA_COOKIE) {
 		IPAERR("bad parm\n");
 		return -EINVAL;
 	}
@@ -802,6 +811,14 @@
 		IPADBG("del hdr of sz=%d hdr_cnt=%d ofst=%d\n", entry->hdr_len,
 			htbl->hdr_cnt, entry->offset_entry->offset);
 
+	if (by_user && entry->user_deleted) {
+		IPAERR("hdr already deleted by user\n");
+		return -EINVAL;
+	}
+
+	if (by_user)
+		entry->user_deleted = true;
+
 	if (--entry->ref_cnt) {
 		IPADBG("hdr_hdl %x ref_cnt %d\n", hdr_hdl, entry->ref_cnt);
 		return 0;
@@ -812,7 +829,7 @@
 			entry->phys_base,
 			entry->hdr_len,
 			DMA_TO_DEVICE);
-		__ipa_del_hdr_proc_ctx(entry->proc_ctx->id, false);
+		__ipa_del_hdr_proc_ctx(entry->proc_ctx->id, false, false);
 	} else {
 		/* move the offset entry to appropriate free list */
 		list_move(&entry->offset_entry->link,
@@ -879,15 +896,16 @@
 }
 
 /**
- * ipa2_del_hdr() - Remove the specified headers from SW and optionally commit
- * them to IPA HW
+ * ipa2_del_hdr_by_user() - Remove the specified headers
+ * from SW and optionally commit them to IPA HW
  * @hdls:	[inout] set of headers to delete
+ * @by_user:	Operation requested by user?
  *
  * Returns:	0 on success, negative on failure
  *
  * Note:	Should not be called from atomic context
  */
-int ipa2_del_hdr(struct ipa_ioc_del_hdr *hdls)
+int ipa2_del_hdr_by_user(struct ipa_ioc_del_hdr *hdls, bool by_user)
 {
 	int i;
 	int result = -EFAULT;
@@ -904,7 +922,7 @@
 
 	mutex_lock(&ipa_ctx->lock);
 	for (i = 0; i < hdls->num_hdls; i++) {
-		if (__ipa_del_hdr(hdls->hdl[i].hdl)) {
+		if (__ipa_del_hdr(hdls->hdl[i].hdl, by_user)) {
 			IPAERR("failed to del hdr %i\n", i);
 			hdls->hdl[i].status = -1;
 		} else {
@@ -925,6 +943,20 @@
 }
 
 /**
+ * ipa2_del_hdr() - Remove the specified headers from SW
+ * and optionally commit them to IPA HW
+ * @hdls:	[inout] set of headers to delete
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa2_del_hdr(struct ipa_ioc_del_hdr *hdls)
+{
+	return ipa2_del_hdr_by_user(hdls, false);
+}
+
+/**
  * ipa2_add_hdr_proc_ctx() - add the specified headers to SW
  * and optionally commit them to IPA HW
  * @proc_ctxs:	[inout] set of processing context headers to add
@@ -976,16 +1008,18 @@
 }
 
 /**
- * ipa2_del_hdr_proc_ctx() -
+ * ipa2_del_hdr_proc_ctx_by_user() -
  * Remove the specified processing context headers from SW and
  * optionally commit them to IPA HW.
  * @hdls:	[inout] set of processing context headers to delete
+ * @by_user:	Operation requested by user?
  *
  * Returns:	0 on success, negative on failure
  *
  * Note:	Should not be called from atomic context
  */
-int ipa2_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls)
+int ipa2_del_hdr_proc_ctx_by_user(struct ipa_ioc_del_hdr_proc_ctx *hdls,
+	bool by_user)
 {
 	int i;
 	int result;
@@ -1004,7 +1038,7 @@
 
 	mutex_lock(&ipa_ctx->lock);
 	for (i = 0; i < hdls->num_hdls; i++) {
-		if (__ipa_del_hdr_proc_ctx(hdls->hdl[i].hdl, true)) {
+		if (__ipa_del_hdr_proc_ctx(hdls->hdl[i].hdl, true, by_user)) {
 			IPAERR("failed to del hdr %i\n", i);
 			hdls->hdl[i].status = -1;
 		} else {
@@ -1025,6 +1059,21 @@
 }
 
 /**
+ * ipa2_del_hdr_proc_ctx() -
+ * Remove the specified processing context headers from SW and
+ * optionally commit them to IPA HW.
+ * @hdls:	[inout] set of processing context headers to delete
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa2_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls)
+{
+	return ipa2_del_hdr_proc_ctx_by_user(hdls, false);
+}
+
+/**
  * ipa2_commit_hdr() - commit to IPA HW the current header table in SW
  *
  * Returns:	0 on success, negative on failure
@@ -1252,7 +1301,7 @@
 {
 	int result = 0;
 
-	if (__ipa_del_hdr(hdr_hdl)) {
+	if (__ipa_del_hdr(hdr_hdl, false)) {
 		IPADBG("fail to del hdr %x\n", hdr_hdl);
 		result = -EFAULT;
 		goto bail;
@@ -1280,7 +1329,7 @@
 {
 	int result = 0;
 
-	if (__ipa_del_hdr_proc_ctx(proc_ctx_hdl, true)) {
+	if (__ipa_del_hdr_proc_ctx(proc_ctx_hdl, true, false)) {
 		IPADBG("fail to del hdr %x\n", proc_ctx_hdl);
 		result = -EFAULT;
 		goto bail;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index 350b5a1..f0eaad8 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -256,6 +256,7 @@
  * @id: header entry id
  * @is_eth2_ofst_valid: is eth2_ofst field valid?
  * @eth2_ofst: offset to start of Ethernet-II/802.3 header
+ * @user_deleted: is the header deleted by the user?
  */
 struct ipa_hdr_entry {
 	struct list_head link;
@@ -273,6 +274,7 @@
 	int id;
 	u8 is_eth2_ofst_valid;
 	u16 eth2_ofst;
+	bool user_deleted;
 };
 
 /**
@@ -336,6 +338,7 @@
  * @cookie: cookie used for validity check
  * @ref_cnt: reference counter of routing table
  * @id: processing context header entry id
+ * @user_deleted: is the hdr processing context deleted by the user?
  */
 struct ipa_hdr_proc_ctx_entry {
 	struct list_head link;
@@ -345,6 +348,7 @@
 	u32 cookie;
 	u32 ref_cnt;
 	int id;
+	bool user_deleted;
 };
 
 /**
@@ -1367,6 +1371,8 @@
 
 int ipa2_del_hdr(struct ipa_ioc_del_hdr *hdls);
 
+int ipa2_del_hdr_by_user(struct ipa_ioc_del_hdr *hdls, bool by_user);
+
 int ipa2_commit_hdr(void);
 
 int ipa2_reset_hdr(void);
@@ -1384,6 +1390,9 @@
 
 int ipa2_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls);
 
+int ipa2_del_hdr_proc_ctx_by_user(struct ipa_ioc_del_hdr_proc_ctx *hdls,
+	bool by_user);
+
 /*
  * Routing
  */
@@ -1671,7 +1680,7 @@
 void ipa2_active_clients_log_clear(void);
 int ipa_interrupts_init(u32 ipa_irq, u32 ee, struct device *ipa_dev);
 int __ipa_del_rt_rule(u32 rule_hdl);
-int __ipa_del_hdr(u32 hdr_hdl);
+int __ipa_del_hdr(u32 hdr_hdl, bool by_user);
 int __ipa_release_hdr(u32 hdr_hdl);
 int __ipa_release_hdr_proc_ctx(u32 proc_ctx_hdl);
 int _ipa_read_gen_reg_v1_1(char *buff, int max_len);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 9ffe9e9..d5daadc 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -784,7 +784,8 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (ipa3_del_hdr((struct ipa_ioc_del_hdr *)param)) {
+		if (ipa3_del_hdr_by_user((struct ipa_ioc_del_hdr *)param,
+			true)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1553,8 +1554,8 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (ipa3_del_hdr_proc_ctx(
-			(struct ipa_ioc_del_hdr_proc_ctx *)param)) {
+		if (ipa3_del_hdr_proc_ctx_by_user(
+			(struct ipa_ioc_del_hdr_proc_ctx *)param, true)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -2917,7 +2918,7 @@
 	if (ipa3_ctx->dflt_v4_rt_rule_hdl)
 		__ipa3_del_rt_rule(ipa3_ctx->dflt_v4_rt_rule_hdl);
 	if (ipa3_ctx->excp_hdr_hdl)
-		__ipa3_del_hdr(ipa3_ctx->excp_hdr_hdl);
+		__ipa3_del_hdr(ipa3_ctx->excp_hdr_hdl, false);
 	ipa3_teardown_sys_pipe(ipa3_ctx->clnt_hdl_cmd);
 fail_cmd:
 	return result;
@@ -2929,7 +2930,7 @@
 	ipa3_teardown_sys_pipe(ipa3_ctx->clnt_hdl_data_in);
 	__ipa3_del_rt_rule(ipa3_ctx->dflt_v6_rt_rule_hdl);
 	__ipa3_del_rt_rule(ipa3_ctx->dflt_v4_rt_rule_hdl);
-	__ipa3_del_hdr(ipa3_ctx->excp_hdr_hdl);
+	__ipa3_del_hdr(ipa3_ctx->excp_hdr_hdl, false);
 	ipa3_teardown_sys_pipe(ipa3_ctx->clnt_hdl_cmd);
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 6274579..69db99a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -576,7 +576,8 @@
 	return -EPERM;
 }
 
-static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr)
+static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl,
+	bool release_hdr, bool by_user)
 {
 	struct ipa3_hdr_proc_ctx_entry *entry;
 	struct ipa3_hdr_proc_ctx_tbl *htbl = &ipa3_ctx->hdr_proc_ctx_tbl;
@@ -590,6 +591,14 @@
 	IPADBG("del proc ctx cnt=%d ofst=%d\n",
 		htbl->proc_ctx_cnt, entry->offset_entry->offset);
 
+	if (by_user && entry->user_deleted) {
+		IPAERR("proc_ctx already deleted by user\n");
+		return -EINVAL;
+	}
+
+	if (by_user)
+		entry->user_deleted = true;
+
 	if (--entry->ref_cnt) {
 		IPADBG("proc_ctx_hdl %x ref_cnt %d\n",
 			proc_ctx_hdl, entry->ref_cnt);
@@ -597,7 +606,7 @@
 	}
 
 	if (release_hdr)
-		__ipa3_del_hdr(entry->hdr->id);
+		__ipa3_del_hdr(entry->hdr->id, false);
 
 	/* move the offset entry to appropriate free list */
 	list_move(&entry->offset_entry->link,
@@ -614,7 +623,7 @@
 }
 
 
-int __ipa3_del_hdr(u32 hdr_hdl)
+int __ipa3_del_hdr(u32 hdr_hdl, bool by_user)
 {
 	struct ipa3_hdr_entry *entry;
 	struct ipa3_hdr_tbl *htbl = &ipa3_ctx->hdr_tbl;
@@ -625,7 +634,7 @@
 		return -EINVAL;
 	}
 
-	if (!entry || (entry->cookie != IPA_COOKIE)) {
+	if (entry->cookie != IPA_COOKIE) {
 		IPAERR("bad parm\n");
 		return -EINVAL;
 	}
@@ -638,6 +647,14 @@
 			entry->hdr_len, htbl->hdr_cnt,
 			entry->offset_entry->offset);
 
+	if (by_user && entry->user_deleted) {
+		IPAERR("proc_ctx already deleted by user\n");
+		return -EINVAL;
+	}
+
+	if (by_user)
+		entry->user_deleted = true;
+
 	if (--entry->ref_cnt) {
 		IPADBG("hdr_hdl %x ref_cnt %d\n", hdr_hdl, entry->ref_cnt);
 		return 0;
@@ -648,7 +665,7 @@
 			entry->phys_base,
 			entry->hdr_len,
 			DMA_TO_DEVICE);
-		__ipa3_del_hdr_proc_ctx(entry->proc_ctx->id, false);
+		__ipa3_del_hdr_proc_ctx(entry->proc_ctx->id, false, false);
 	} else {
 		/* move the offset entry to appropriate free list */
 		list_move(&entry->offset_entry->link,
@@ -710,15 +727,16 @@
 }
 
 /**
- * ipa3_del_hdr() - Remove the specified headers from SW and optionally commit
- * them to IPA HW
+ * ipa3_del_hdr_by_user() - Remove the specified headers
+ * from SW and optionally commit them to IPA HW
  * @hdls:	[inout] set of headers to delete
+ * @by_user:	Operation requested by user?
  *
  * Returns:	0 on success, negative on failure
  *
  * Note:	Should not be called from atomic context
  */
-int ipa3_del_hdr(struct ipa_ioc_del_hdr *hdls)
+int ipa3_del_hdr_by_user(struct ipa_ioc_del_hdr *hdls, bool by_user)
 {
 	int i;
 	int result = -EFAULT;
@@ -730,7 +748,7 @@
 
 	mutex_lock(&ipa3_ctx->lock);
 	for (i = 0; i < hdls->num_hdls; i++) {
-		if (__ipa3_del_hdr(hdls->hdl[i].hdl)) {
+		if (__ipa3_del_hdr(hdls->hdl[i].hdl, by_user)) {
 			IPAERR("failed to del hdr %i\n", i);
 			hdls->hdl[i].status = -1;
 		} else {
@@ -751,6 +769,20 @@
 }
 
 /**
+ * ipa3_del_hdr() - Remove the specified headers from SW
+ * and optionally commit them to IPA HW
+ * @hdls:	[inout] set of headers to delete
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa3_del_hdr(struct ipa_ioc_del_hdr *hdls)
+{
+	return ipa3_del_hdr_by_user(hdls, false);
+}
+
+/**
  * ipa3_add_hdr_proc_ctx() - add the specified headers to SW
  * and optionally commit them to IPA HW
  * @proc_ctxs:	[inout] set of processing context headers to add
@@ -795,16 +827,18 @@
 }
 
 /**
- * ipa3_del_hdr_proc_ctx() -
+ * ipa3_del_hdr_proc_ctx_by_user() -
  * Remove the specified processing context headers from SW and
  * optionally commit them to IPA HW.
  * @hdls:	[inout] set of processing context headers to delete
+ * @by_user:	Operation requested by user?
  *
  * Returns:	0 on success, negative on failure
  *
  * Note:	Should not be called from atomic context
  */
-int ipa3_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls)
+int ipa3_del_hdr_proc_ctx_by_user(struct ipa_ioc_del_hdr_proc_ctx *hdls,
+	bool by_user)
 {
 	int i;
 	int result;
@@ -816,7 +850,7 @@
 
 	mutex_lock(&ipa3_ctx->lock);
 	for (i = 0; i < hdls->num_hdls; i++) {
-		if (__ipa3_del_hdr_proc_ctx(hdls->hdl[i].hdl, true)) {
+		if (__ipa3_del_hdr_proc_ctx(hdls->hdl[i].hdl, true, by_user)) {
 			IPAERR("failed to del hdr %i\n", i);
 			hdls->hdl[i].status = -1;
 		} else {
@@ -837,6 +871,21 @@
 }
 
 /**
+ * ipa3_del_hdr_proc_ctx() -
+ * Remove the specified processing context headers from SW and
+ * optionally commit them to IPA HW.
+ * @hdls:	[inout] set of processing context headers to delete
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa3_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls)
+{
+	return ipa3_del_hdr_proc_ctx_by_user(hdls, false);
+}
+
+/**
  * ipa3_commit_hdr() - commit to IPA HW the current header table in SW
  *
  * Returns:	0 on success, negative on failure
@@ -1064,7 +1113,7 @@
 {
 	int result = 0;
 
-	if (__ipa3_del_hdr(hdr_hdl)) {
+	if (__ipa3_del_hdr(hdr_hdl, false)) {
 		IPADBG("fail to del hdr %x\n", hdr_hdl);
 		result = -EFAULT;
 		goto bail;
@@ -1092,7 +1141,7 @@
 {
 	int result = 0;
 
-	if (__ipa3_del_hdr_proc_ctx(proc_ctx_hdl, true)) {
+	if (__ipa3_del_hdr_proc_ctx(proc_ctx_hdl, true, false)) {
 		IPADBG("fail to del hdr %x\n", proc_ctx_hdl);
 		result = -EFAULT;
 		goto bail;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 8976a01..ee6f43a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -281,6 +281,7 @@
  * @id: header entry id
  * @is_eth2_ofst_valid: is eth2_ofst field valid?
  * @eth2_ofst: offset to start of Ethernet-II/802.3 header
+ * @user_deleted: is the header deleted by the user?
  */
 struct ipa3_hdr_entry {
 	struct list_head link;
@@ -298,6 +299,7 @@
 	int id;
 	u8 is_eth2_ofst_valid;
 	u16 eth2_ofst;
+	bool user_deleted;
 };
 
 /**
@@ -337,6 +339,7 @@
  * @cookie: cookie used for validity check
  * @ref_cnt: reference counter of routing table
  * @id: processing context header entry id
+ * @user_deleted: is the hdr processing context deleted by the user?
  */
 struct ipa3_hdr_proc_ctx_entry {
 	struct list_head link;
@@ -346,6 +349,7 @@
 	u32 cookie;
 	u32 ref_cnt;
 	int id;
+	bool user_deleted;
 };
 
 /**
@@ -1547,6 +1551,8 @@
 
 int ipa3_del_hdr(struct ipa_ioc_del_hdr *hdls);
 
+int ipa3_del_hdr_by_user(struct ipa_ioc_del_hdr *hdls, bool by_user);
+
 int ipa3_commit_hdr(void);
 
 int ipa3_reset_hdr(void);
@@ -1564,6 +1570,9 @@
 
 int ipa3_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls);
 
+int ipa3_del_hdr_proc_ctx_by_user(struct ipa_ioc_del_hdr_proc_ctx *hdls,
+	bool by_user);
+
 /*
  * Routing
  */
@@ -1864,7 +1873,7 @@
 void ipa3_active_clients_log_clear(void);
 int ipa3_interrupts_init(u32 ipa_irq, u32 ee, struct device *ipa_dev);
 int __ipa3_del_rt_rule(u32 rule_hdl);
-int __ipa3_del_hdr(u32 hdr_hdl);
+int __ipa3_del_hdr(u32 hdr_hdl, bool by_user);
 int __ipa3_release_hdr(u32 hdr_hdl);
 int __ipa3_release_hdr_proc_ctx(u32 proc_ctx_hdl);
 int _ipa_read_ep_reg_v3_0(char *buf, int max_len, int pipe);