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

Commit be89ad02 authored by Subash Abhinov Kasiviswanathan's avatar Subash Abhinov Kasiviswanathan
Browse files

drivers: shs: Protect all file system operations using shs ep lock



This add synchronization between the file system operations and the
updates which occur within the shs workqueue. Fixes the following
when there are two instances of userspace handlers running and are
killed together-

<6> Unable to handle kernel paging request at virtual address ffffffbfadadadb4
<2> pc : __free_pages+0x24/0xc0
<2> lr : free_pages+0x38/0x48
<2> Call trace:
<2>  __free_pages+0x24/0xc0
<2>  free_pages+0x38/0x48
<2>  rmnet_shs_release_caps+0x9c/0xb0 [rmnet_shs]
<2>  close_pdeo+0x94/0x120
<2>  proc_reg_release+0x64/0x88
<2>  __fput+0xdc/0x1d8
<2>  ____fput+0x1c/0x28
<2>  task_work_run+0x48/0xd0
<2>  do_notify_resume+0x950/0x1160
<2>  work_pending+0x8/0x14

CRs-fixed: 2576578
Change-Id: I67d1fc4d1f3c93d4497e988c2118c410091f0dd2
Signed-off-by: default avatarSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>
parent 74ba5897
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -2194,3 +2194,13 @@ u64 rmnet_shs_wq_get_max_allowed_pps(u16 cpu)

	return rmnet_shs_cpu_rx_max_pps_thresh[cpu];
}

void rmnet_shs_wq_ep_lock_bh(void)
{
	spin_lock_bh(&rmnet_shs_ep_lock);
}

void rmnet_shs_wq_ep_unlock_bh(void)
{
	spin_unlock_bh(&rmnet_shs_ep_lock);
}
+5 −1
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, 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
@@ -285,4 +285,8 @@ int rmnet_shs_wq_try_to_move_flow(u16 cur_cpu, u16 dest_cpu, u32 hash_to_move,

int rmnet_shs_wq_set_flow_segmentation(u32 hash_to_set, u8 seg_enable);

void rmnet_shs_wq_ep_lock_bh(void);

void rmnet_shs_wq_ep_unlock_bh(void);

#endif /*_RMNET_SHS_WQ_H_*/
+50 −11
Original line number Diff line number Diff line
@@ -49,13 +49,14 @@ static int rmnet_shs_vm_fault(struct vm_fault *vmf)
	struct page *page = NULL;
	struct rmnet_shs_mmap_info *info;


	rmnet_shs_wq_ep_lock_bh();
	info = (struct rmnet_shs_mmap_info *) vmf->vma->vm_private_data;
	if (info->data) {
		page = virt_to_page(info->data);
		get_page(page);
		vmf->page = page;
	}
	rmnet_shs_wq_ep_unlock_bh();

	return 0;
}
@@ -80,13 +81,16 @@ static int rmnet_shs_open_caps(struct inode *inode, struct file *filp)
	struct rmnet_shs_mmap_info *info;

	rm_err("%s", "SHS_MEM: rmnet_shs_open - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	if (!cap_shared) {
		info = kzalloc(sizeof(struct rmnet_shs_mmap_info), GFP_KERNEL);
		info = kzalloc(sizeof(struct rmnet_shs_mmap_info), GFP_ATOMIC);
		if (!info) {
			rmnet_shs_wq_ep_unlock_bh();
			rm_err("%s", "SHS_MEM: rmnet_shs_open - FAILED\n");
			return -ENOMEM;
		}
		info->data = (char *)get_zeroed_page(GFP_KERNEL);
		info->data = (char *)get_zeroed_page(GFP_ATOMIC);
		cap_shared = info;
		rm_err("SHS_MEM: virt_to_phys = 0x%llx cap_shared = 0x%llx\n",
		       (unsigned long long)virt_to_phys((void *)info),
@@ -94,6 +98,7 @@ static int rmnet_shs_open_caps(struct inode *inode, struct file *filp)
	}

	filp->private_data = cap_shared;
	rmnet_shs_wq_ep_unlock_bh();

	rm_err("%s", "SHS_MEM: rmnet_shs_open - OK\n");

@@ -105,19 +110,24 @@ static int rmnet_shs_open_g_flows(struct inode *inode, struct file *filp)
	struct rmnet_shs_mmap_info *info;

	rm_err("%s", "SHS_MEM: rmnet_shs_open g_flows - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	if (!gflow_shared) {
		info = kzalloc(sizeof(struct rmnet_shs_mmap_info), GFP_KERNEL);
		info = kzalloc(sizeof(struct rmnet_shs_mmap_info), GFP_ATOMIC);
		if (!info) {
			rmnet_shs_wq_ep_unlock_bh();
			rm_err("%s", "SHS_MEM: rmnet_shs_open - FAILED\n");
			return -ENOMEM;
		}
		info->data = (char *)get_zeroed_page(GFP_KERNEL);
		info->data = (char *)get_zeroed_page(GFP_ATOMIC);
		gflow_shared = info;
		rm_err("SHS_MEM: virt_to_phys = 0x%llx gflow_shared = 0x%llx\n",
		       (unsigned long long)virt_to_phys((void *)info),
		       (unsigned long long)virt_to_phys((void *)gflow_shared));
	}
	filp->private_data = gflow_shared;
	rmnet_shs_wq_ep_unlock_bh();

	return 0;
}

@@ -126,34 +136,42 @@ static int rmnet_shs_open_ss_flows(struct inode *inode, struct file *filp)
	struct rmnet_shs_mmap_info *info;

	rm_err("%s", "SHS_MEM: rmnet_shs_open ss_flows - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	if (!ssflow_shared) {
		info = kzalloc(sizeof(struct rmnet_shs_mmap_info), GFP_KERNEL);
		info = kzalloc(sizeof(struct rmnet_shs_mmap_info), GFP_ATOMIC);
		if (!info) {
			rmnet_shs_wq_ep_unlock_bh();
			rm_err("%s", "SHS_MEM: rmnet_shs_open - FAILED\n");
			return -ENOMEM;
		}
		info->data = (char *)get_zeroed_page(GFP_KERNEL);
		info->data = (char *)get_zeroed_page(GFP_ATOMIC);
		ssflow_shared = info;
		rm_err("SHS_MEM: virt_to_phys = 0x%llx ssflow_shared = 0x%llx\n",
		       (unsigned long long)virt_to_phys((void *)info),
		       (unsigned long long)virt_to_phys((void *)ssflow_shared));
	}
	filp->private_data = ssflow_shared;
	rmnet_shs_wq_ep_unlock_bh();

	return 0;
}

static ssize_t rmnet_shs_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
	struct rmnet_shs_mmap_info *info;
	int ret;
	int ret = 0;

	rm_err("%s", "SHS_MEM: rmnet_shs_read - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	info = filp->private_data;
	ret = min_t(size_t, len, RMNET_SHS_BUFFER_SIZE);
	if (copy_to_user(buf, info->data, ret))
		ret = -EFAULT;
	rmnet_shs_wq_ep_unlock_bh();

	return 0;
	return ret;
}

static ssize_t rmnet_shs_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
@@ -162,12 +180,17 @@ static ssize_t rmnet_shs_write(struct file *filp, const char __user *buf, size_t
	int ret;

	rm_err("%s", "SHS_MEM: rmnet_shs_write - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	info = filp->private_data;
	ret = min_t(size_t, len, RMNET_SHS_BUFFER_SIZE);
	if (copy_from_user(info->data, buf, ret))
		return -EFAULT;
		ret = -EFAULT;
	else
		return len;
		ret = len;
	rmnet_shs_wq_ep_unlock_bh();

	return ret;
}

static int rmnet_shs_release_caps(struct inode *inode, struct file *filp)
@@ -175,6 +198,8 @@ static int rmnet_shs_release_caps(struct inode *inode, struct file *filp)
	struct rmnet_shs_mmap_info *info;

	rm_err("%s", "SHS_MEM: rmnet_shs_release - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	if (cap_shared) {
		info = filp->private_data;
		cap_shared = NULL;
@@ -182,6 +207,8 @@ static int rmnet_shs_release_caps(struct inode *inode, struct file *filp)
		kfree(info);
		filp->private_data = NULL;
	}
	rmnet_shs_wq_ep_unlock_bh();

	return 0;
}

@@ -190,6 +217,8 @@ static int rmnet_shs_release_g_flows(struct inode *inode, struct file *filp)
	struct rmnet_shs_mmap_info *info;

	rm_err("%s", "SHS_MEM: rmnet_shs_release - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	if (gflow_shared) {
		info = filp->private_data;
		gflow_shared = NULL;
@@ -197,6 +226,8 @@ static int rmnet_shs_release_g_flows(struct inode *inode, struct file *filp)
		kfree(info);
		filp->private_data = NULL;
	}
	rmnet_shs_wq_ep_unlock_bh();

	return 0;
}

@@ -205,6 +236,8 @@ static int rmnet_shs_release_ss_flows(struct inode *inode, struct file *filp)
	struct rmnet_shs_mmap_info *info;

	rm_err("%s", "SHS_MEM: rmnet_shs_release - entry\n");

	rmnet_shs_wq_ep_lock_bh();
	if (ssflow_shared) {
		info = filp->private_data;
		ssflow_shared = NULL;
@@ -212,6 +245,8 @@ static int rmnet_shs_release_ss_flows(struct inode *inode, struct file *filp)
		kfree(info);
		filp->private_data = NULL;
	}
	rmnet_shs_wq_ep_unlock_bh();

	return 0;
}

@@ -607,9 +642,11 @@ void rmnet_shs_wq_mem_init(void)
	proc_create(RMNET_SHS_PROC_G_FLOWS, 0644, shs_proc_dir, &rmnet_shs_g_flows_fops);
	proc_create(RMNET_SHS_PROC_SS_FLOWS, 0644, shs_proc_dir, &rmnet_shs_ss_flows_fops);

	rmnet_shs_wq_ep_lock_bh();
	cap_shared = NULL;
	gflow_shared = NULL;
	ssflow_shared = NULL;
	rmnet_shs_wq_ep_unlock_bh();
}

/* Remove shs files and folders from proc fs */
@@ -620,7 +657,9 @@ void rmnet_shs_wq_mem_deinit(void)
	remove_proc_entry(RMNET_SHS_PROC_SS_FLOWS, shs_proc_dir);
	remove_proc_entry(RMNET_SHS_PROC_DIR, NULL);

	rmnet_shs_wq_ep_lock_bh();
	cap_shared = NULL;
	gflow_shared = NULL;
	ssflow_shared = NULL;
	rmnet_shs_wq_ep_unlock_bh();
}