Unverified Commit 6d0ee9b0 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!11437 sdma-dae:fix code review problems

Merge Pull Request from: @zhangshuowen96 
 
SDMA-dae has some bug: lack of input parameters validations, UAF and so on. 
 
Link:https://gitee.com/openeuler/kernel/pulls/11437

 

Reviewed-by: default avatarYang Yingliang <yangyingliang@huawei.com>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
parents c431540f 1eb55075
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -14,8 +14,11 @@
#define HISI_SDMA_MMAP_CQE			1
#define HISI_SDMA_MMAP_IO			2
#define HISI_SDMA_MMAP_SHMEM			3
#define HISI_SDMA_FSM_TIMEOUT			100
#define HISI_SDMA_FSM_INTERVAL			20
#define HISI_SDMA_FSM_TIMEOUT			10

#define HISI_SDMA_MAX_BASE_ADDR_SIZE		0x100000
#define HISI_SDMA_MAX_COMMEN_BASE_ADDR_SIZE	0x10000
#define HISI_SDMA_CHANNEL_IOMEM_SIZE		0x1000
#define HISI_SDMA_SQ_ENTRY_SIZE			64UL
#define HISI_SDMA_CQ_ENTRY_SIZE			16UL
@@ -135,7 +138,7 @@ struct hisi_sdma_share_chn {
};

struct hisi_sdma_reg_info {
	int chn;
	u32 chn;
	int type;
	u32 reg_value;
};
@@ -168,7 +171,7 @@ struct hisi_sdma_sqe_task {
};

struct hisi_sdma_task_info {
	int chn;
	u32 chn;
	u32 req_cnt;
	u32 task_cnt;
	uintptr_t task_addr;
@@ -181,8 +184,8 @@ struct hisi_sdma_ioctl_func_list {
};

#define IOCTL_SDMA_GET_PROCESS_ID	_IOR('s', 1, u32)
#define IOCTL_SDMA_GET_CHN		_IOR('s', 2, int)
#define IOCTL_SDMA_PUT_CHN		_IOW('s', 3, int)
#define IOCTL_SDMA_GET_CHN		_IOR('s', 2, u32)
#define IOCTL_SDMA_PUT_CHN		_IOW('s', 3, u32)
#define IOCTL_SDMA_GET_STREAMID		_IOR('s', 4, u32)
#define IOCTL_SDMA_PIN_UMEM		_IOWR('s', 5, struct hisi_sdma_umem_info)
#define IOCTL_SDMA_UNPIN_UMEM		_IOW('s', 6, u64)
+42 −20
Original line number Diff line number Diff line
@@ -17,12 +17,11 @@ struct hisi_sdma_own_pid_hte {
	u32 pasid;
	struct hlist_node node;
	DECLARE_HASHTABLE(sdma_submitter_pid_ht, HISI_SDMA_AUTH_HASH_BUCKETS_BITS);
	spinlock_t submitter_pid_lock;
};

struct hisi_sdma_auth_ht {
	DECLARE_HASHTABLE(sdma_owner_pid_ht, HISI_SDMA_AUTH_HASH_BUCKETS_BITS);
	spinlock_t owner_pid_lock;
	rwlock_t owner_pid_lock;
};

static struct hisi_sdma_auth_ht *g_authority;
@@ -34,7 +33,7 @@ int sdma_authority_hash_init(void)
		return -ENOMEM;

	hash_init(g_authority->sdma_owner_pid_ht);
	spin_lock_init(&g_authority->owner_pid_lock);
	rwlock_init(&g_authority->owner_pid_lock);

	return 0;
}
@@ -45,13 +44,10 @@ static void entry_free_pid_ht(struct hisi_sdma_own_pid_hte *entry)
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&entry->submitter_pid_lock);
	hash_for_each_safe(entry->sdma_submitter_pid_ht, bkt, tmp, sub_entry, pnode) {
		hash_del(&sub_entry->pnode);
		kfree(sub_entry);
		sub_entry = NULL;
	}
	spin_unlock(&entry->submitter_pid_lock);
	hash_del(&entry->node);
	kfree(entry);
}
@@ -62,11 +58,11 @@ void sdma_authority_ht_free(void)
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&g_authority->owner_pid_lock);
	write_lock(&g_authority->owner_pid_lock);
	hash_for_each_safe(g_authority->sdma_owner_pid_ht, bkt, tmp, entry, node)
		entry_free_pid_ht(entry);

	spin_unlock(&g_authority->owner_pid_lock);
	write_unlock(&g_authority->owner_pid_lock);

	kfree(g_authority);
	g_authority = NULL;
@@ -91,22 +87,19 @@ void sdma_free_authority_ht_with_pid(u32 pid)
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&g_authority->owner_pid_lock);
	write_lock(&g_authority->owner_pid_lock);
	hash_for_each_safe(g_authority->sdma_owner_pid_ht, bkt, tmp, entry, node) {
		if (entry->pid == pid)
			entry_free_pid_ht(entry);
		else {
			spin_lock(&entry->submitter_pid_lock);
			sub_entry = sdma_search_submitter_pid(entry, pid);
			if (sub_entry) {
				hash_del(&sub_entry->pnode);
				kfree(sub_entry);
				sub_entry = NULL;
			}
			spin_unlock(&entry->submitter_pid_lock);
		}
	}
	spin_unlock(&g_authority->owner_pid_lock);
	write_unlock(&g_authority->owner_pid_lock);
}

static struct hisi_sdma_own_pid_hte *sdma_search_owner_pid_ht(u32 pid)
@@ -120,24 +113,52 @@ static struct hisi_sdma_own_pid_hte *sdma_search_owner_pid_ht(u32 pid)
	return NULL;
}

static void sdma_clear_residual_auth_ht(struct hisi_sdma_own_pid_hte *entry, u32 *list, u32 pos,
					bool *stored_info)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	u32 i = pos;

	while (i > 0) {
		i--;
		if (stored_info[i]) {
			sub_entry = sdma_search_submitter_pid(entry, list[i]);
			if (sub_entry) {
				hash_del(&sub_entry->pnode);
				kfree(sub_entry);
			}
		}
	}
}

static int sdma_add_authority_ht(struct hisi_sdma_own_pid_hte *entry, u32 count, u32 *list)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	bool *stored;
	u32 i;

	stored = kcalloc(count, sizeof(bool), GFP_KERNEL);
	if (!stored)
		return -ENOMEM;

	for (i = 0; i < count; i++) {
		sub_entry = sdma_search_submitter_pid(entry, list[i]);
		if (sub_entry)
			continue;

		sub_entry = kzalloc(sizeof(struct hisi_sdma_sub_pid_hte), GFP_KERNEL);
		if (!sub_entry)
		if (!sub_entry) {
			sdma_clear_residual_auth_ht(entry, list, i, stored);
			kfree(stored);
			return -ENOMEM;
		}

		sub_entry->pid = list[i];
		hash_add(entry->sdma_submitter_pid_ht, &sub_entry->pnode, sub_entry->pid);
		stored[i] = true;
	}

	kfree(stored);
	return 0;
}

@@ -152,7 +173,6 @@ static int sdma_create_authority_ht(u32 pid, u32 pasid, u32 num, u32 *list)

	entry->pid = pid;
	entry->pasid = pasid;
	spin_lock_init(&entry->submitter_pid_lock);
	hash_init(entry->sdma_submitter_pid_ht);
	ret = sdma_add_authority_ht(entry, num, list);
	hash_add(g_authority->sdma_owner_pid_ht, &entry->node, entry->pid);
@@ -164,22 +184,20 @@ int sdma_auth_add(u32 pasid, u32 num, u32 *pid_list)
{
	struct hisi_sdma_own_pid_hte *owner;
	u32 pid = (u32)current->tgid;
	int ret = 0;
	int ret;

	write_lock(&g_authority->owner_pid_lock);
	owner = sdma_search_owner_pid_ht(pid);
	if (owner) {
		spin_lock(&owner->submitter_pid_lock);
		ret = sdma_add_authority_ht(owner, num, pid_list);
		spin_unlock(&owner->submitter_pid_lock);
		if (ret < 0)
			pr_err("add_pid_ht failed\n");
	} else {
		spin_lock(&g_authority->owner_pid_lock);
		ret = sdma_create_authority_ht(pid, pasid, num, pid_list);
		spin_unlock(&g_authority->owner_pid_lock);
		if (ret < 0)
			pr_err("create_pid_ht failed\n");
	}
	write_unlock(&g_authority->owner_pid_lock);

	return ret;
}
@@ -193,16 +211,20 @@ int sdma_check_authority(u32 pasid, u32 owner_pid, u32 submitter_pid, u32 *owner
		*owner_pasid = pasid;
		return 0;
	}
	read_lock(&g_authority->owner_pid_lock);
	entry = sdma_search_owner_pid_ht(owner_pid);
	if (!entry) {
		pr_err("the owner_pid_ht[%u] not exist\n", owner_pid);
		read_unlock(&g_authority->owner_pid_lock);
		return -ENODATA;
	}
	sub_entry = sdma_search_submitter_pid(entry, submitter_pid);
	if (!sub_entry) {
		pr_err("the submitter[%u] not authorithed\n", submitter_pid);
		read_unlock(&g_authority->owner_pid_lock);
		return -ENODATA;
	}
	read_unlock(&g_authority->owner_pid_lock);

	*owner_pasid = entry->pasid;
	return 0;
+173 −86
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/sort.h>
#include <linux/mm.h>
#include <linux/version.h>

#include "sdma_hal.h"
#include "sdma_umem.h"
@@ -14,7 +16,7 @@ static struct hisi_sdma_global_info g_info;

struct hisi_sdma_channel_list {
	struct list_head chn_list;
	int chn_idx;
	u32 chn_idx;
};

struct file_open_data {
@@ -103,13 +105,13 @@ static int ioctl_sdma_unpin_umem(struct file *file, unsigned long arg)
	int ret;
	int ida;

	if (copy_from_user(&cookie, (u64 __user *)(uintptr_t)arg, sizeof(u64)))
		return -EFAULT;

	ida = (int)(cookie >> COOKIE_IDA_SHIFT);
	if (ida != data->ida)
		return -EPERM;

	if (copy_from_user(&cookie, (u64 __user *)(uintptr_t)arg, sizeof(u64)))
		return -EFAULT;

	ret = sdma_umem_release(cookie);
	if (ret)
		dev_err(&data->psdma_dev->pdev->dev, "umem release fail!\n");
@@ -124,8 +126,10 @@ static int ioctl_sdma_pin_umem(struct file *file, unsigned long arg)
	int ret;

	if (copy_from_user(&umemInfo, (struct hisi_sdma_umem_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_umem_info)))
			   sizeof(struct hisi_sdma_umem_info))) {
		dev_dbg(&data->psdma_dev->pdev->dev, "umem_info copy from user failed!\n");
		return -EFAULT;
	}

	ret = sdma_umem_get((u64)umemInfo.vma, umemInfo.size, data->ida, &umemInfo.cookie);
	if (ret < 0)
@@ -134,6 +138,7 @@ static int ioctl_sdma_pin_umem(struct file *file, unsigned long arg)
	if (copy_to_user((struct hisi_sdma_umem_info __user *)(uintptr_t)arg, &umemInfo,
			 sizeof(struct hisi_sdma_umem_info))) {
		sdma_umem_release(umemInfo.cookie);
		dev_dbg(&data->psdma_dev->pdev->dev, "umem_info copy to user failed!\n");
		return -EFAULT;
	}

@@ -187,14 +192,14 @@ static int ioctl_sdma_get_chn(struct file *file, unsigned long arg)
	}

	idx += share_chns;
	list_node->chn_idx = (int)idx;
	list_node->chn_idx = idx;
	list_add(&list_node->chn_list, &data->non_share_chn_list);
	pchannel = pdev->channels + idx;
	pchannel->cnt_used++;
	pchannel->ida = (u32)data->ida;
	spin_unlock(&pdev->channel_lock);

	dev_dbg(&pdev->pdev->dev, "sdma get chn %u\n", idx);
	if (copy_to_user((int __user *)(uintptr_t)arg, &idx, sizeof(int))) {
	if (copy_to_user((u32 __user *)(uintptr_t)arg, &idx, sizeof(u32))) {
		ret = -EFAULT;
		goto put_chn;
	}
@@ -206,7 +211,7 @@ static int ioctl_sdma_get_chn(struct file *file, unsigned long arg)
	list_del(&list_node->chn_list);
	bitmap_set(pdev->channel_map, idx - share_chns, 1);
	pdev->nr_channel_used--;
	pchannel->cnt_used--;
	pchannel->ida = 0;
unlock:
	spin_unlock(&pdev->channel_lock);
	kfree(list_node);
@@ -221,31 +226,29 @@ static int ioctl_sdma_put_chn(struct file *file, unsigned long arg)
	struct device *dev = &pdev->pdev->dev;
	u32 share_chns = *(g_info.share_chns);
	struct hisi_sdma_channel_list *c, *n;
	int idx;
	u32 idx;

	if (copy_from_user(&idx, (int __user *)(uintptr_t)arg, sizeof(int))) {
	if (copy_from_user(&idx, (u32 __user *)(uintptr_t)arg, sizeof(u32))) {
		dev_err(dev, "put user chn failed\n");
		return -EFAULT;
	}

	if (idx < (int)share_chns || idx >= (int)pdev->nr_channel) {
		dev_err(dev, "put idx = %d is err\n", idx);
	if (idx < share_chns || idx >= pdev->nr_channel) {
		dev_err(dev, "put idx = %u is err\n", idx);
		return -EFAULT;
	}

	spin_lock(&pdev->channel_lock);
	bitmap_set(pdev->channel_map, idx - share_chns, 1);
	pdev->nr_channel_used--;

	list_for_each_entry_safe(c, n, &data->non_share_chn_list, chn_list) {
		if (c->chn_idx == idx) {
			dev_dbg(dev, "sdma put chn %d\n", idx);
			bitmap_set(pdev->channel_map, idx - share_chns, 1);
			pdev->nr_channel_used--;
			dev_dbg(dev, "sdma put chn %u\n", idx);
			list_del(&c->chn_list);
			kfree(c);
			break;
		}
	}

	spin_unlock(&pdev->channel_lock);

	return 0;
@@ -264,8 +267,8 @@ static int cmp(const void *a, const void *b)

static int ioctl_get_near_sdmaid(struct file *file, unsigned long arg)
{
	struct hisi_sdma_numa_domain sdma_numa[HISI_SDMA_MAX_DEVS];
	u32 num = g_info.core_dev->sdma_device_num;
	struct hisi_sdma_numa_domain *sdma_numa;
	struct hisi_sdma_device *sdma_dev;
	struct device *dev;
	int sdma_id = -1;
@@ -274,38 +277,40 @@ static int ioctl_get_near_sdmaid(struct file *file, unsigned long arg)

	nid = numa_node_id();
	if (nid < 0) {
		pr_err("numa_node not reported!\n");
		return 0;
		pr_err("sdma numa_node not reported!\n");
		return -EINVAL;
	}
	if (num <= 0 || num > HISI_SDMA_MAX_DEVS) {
		pr_err("device num wrong, cannot use sdma!\n");
		return -ENOENT;
	}

	sdma_numa = kcalloc(num, sizeof(struct hisi_sdma_numa_domain), GFP_KERNEL);
	if (!sdma_numa)
		return -ENOMEM;

	for (i = 0; i < num; i++) {
		spin_lock(&g_info.core_dev->device_lock);
		sdma_dev = g_info.core_dev->sdma_devices[i];
		dev = &sdma_dev->pdev->dev;
		sdma_numa[i].idx = sdma_dev->idx;
		sdma_numa[i].pxm = sdma_dev->node_idx;
		if (sdma_numa[i].pxm < 0) {
			spin_unlock(&g_info.core_dev->device_lock);
			dev_err(dev, "sdma%d PXM domain not reported!\n", sdma_numa[i].idx);
			goto ret_idx;
			return -ENODATA;
		}
		spin_unlock(&g_info.core_dev->device_lock);
		dev_dbg(dev, "sdma%d PXM = %d\n", sdma_numa[i].idx, sdma_numa[i].pxm);
	}

	sort(sdma_numa, num, sizeof(struct hisi_sdma_numa_domain), cmp, NULL);

	for (i = 0; i < num; i++) {
		if (nid >= sdma_numa[i].pxm) {
			sdma_id = sdma_numa[i].idx;
			goto ret_idx;
			break;
		}
	}
	pr_err("Nearest sdma not found! process nid = %d\n", nid);

ret_idx:
	kfree(sdma_numa);
	if (sdma_id < 0) {
		pr_err("Nearest sdma not found! process nid = %d, sdmaid = %d\n", nid, sdma_id);
		return -ENODATA;
	}
	if (copy_to_user((int __user *)(uintptr_t)arg, &sdma_id, sizeof(int)))
		return -EFAULT;

@@ -333,8 +338,7 @@ static int ioctl_sdma_mpamcfg(struct file *file, unsigned long arg)
	struct hisi_sdma_device *pdev = data->psdma_dev;
	struct hisi_sdma_mpamcfg cfg;

	if (copy_from_user(&cfg,
			   (struct hisi_sdma_mpamcfg __user *)(uintptr_t)arg,
	if (copy_from_user(&cfg, (struct hisi_sdma_mpamcfg __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_mpamcfg)))
		return -EFAULT;

@@ -378,16 +382,19 @@ static int ioctl_sdma_chn_used_refcount(struct file *file, unsigned long arg)
		list_add(&list_node->chn_list, &data->share_chn_list);
		pchannel->cnt_used++;
	}

	if (!share_chn.init_flag && pchannel->cnt_used > 0) {
		list_for_each_entry_safe(c, n, &data->share_chn_list, chn_list) {
			if (c->chn_idx == share_chn.chn_idx) {
				dev_dbg(dev, "release share_chn%d\n", c->chn_idx);
				pchannel->cnt_used--;
				if (pchannel->cnt_used == 0)
					pchannel->sync_info_base->err_cnt = 0;
				dev_dbg(dev, "release share_chn%u\n", c->chn_idx);
				list_del(&c->chn_list);
				kfree(c);
				break;
			}
		}
		pchannel->cnt_used--;
	}
	spin_unlock(&pdev->channel_lock);

@@ -413,7 +420,7 @@ static int ioctl_sdma_add_authority_ht(struct file *file, unsigned long arg)
		dev_err(&pdev->pdev->dev, "Invalid pid_list num:%u\n", list_num);
		return -EINVAL;
	}
	pid_list = kmalloc_node(list_num * sizeof(int), GFP_KERNEL, pdev->node_idx);
	pid_list = kmalloc_node(list_num * sizeof(u32), GFP_KERNEL, pdev->node_idx);
	if (!pid_list)
		return -ENOMEM;

@@ -435,7 +442,7 @@ static int sdma_verify_src_dst(struct file_open_data *data, struct pasid_info *p
{
	struct device *dev = &data->psdma_dev->pdev->dev;
	u32 pid = (u32)current->tgid;
	int ret = -1;
	int ret = -EPERM;

	if (task_list.opcode == HISI_SDMA_HBM_CACHE_PRELOAD_MODE) {
		pasid->src_pasid = data->pasid;
@@ -490,6 +497,20 @@ static void sdma_fill_sqe(struct hisi_sdma_sq_entry *sq_entry, struct hisi_sdma_
	sq_entry->qos             = task->qos;
}

static bool sdma_check_channel_permission(struct hisi_sdma_channel *pchannel, u32 ida, u32 chn)
{
	u32 share_chns = *(g_info.share_chns);

	if (chn < share_chns) {
		return true;
	} else if (chn < HISI_SDMA_DEFAULT_CHANNEL_NUM && pchannel->ida != ida) {
		pr_err("invalid process send task by sdma exclusive channel%u\n", chn);
		return false;
	}

	return true;
}

static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_task_info *task_info,
				 struct hisi_sdma_sqe_task *task_list)
{
@@ -502,9 +523,15 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
	u32 i;

	pchannel = pdev->channels + task_info->chn;
	spin_lock(&pchannel->owner_chn_lock);
	if (!sdma_check_channel_permission(pchannel, (u32)data->ida, task_info->chn)) {
		spin_unlock(&pchannel->owner_chn_lock);
		return -EPERM;
	}
	sq_tail = pchannel->sync_info_base->sq_tail;
	for (i = 0; i < task_info->task_cnt; i++) {
		if (task_info->req_cnt != 0) {
			/* not send/record tasks whose length == 0 */
			if (task_list[i].length == 0) {
				task_info->req_cnt--;
				continue;
@@ -512,6 +539,7 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
		}
		ret = sdma_verify_src_dst(data, &pasid, task_list[i]);
		if (ret < 0) {
			spin_unlock(&pchannel->owner_chn_lock);
			dev_err(&pdev->pdev->dev, "no correct pid\n");
			return ret;
		}
@@ -521,6 +549,43 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
	}
	sdma_channel_set_sq_tail(pchannel, sq_tail);
	pchannel->sync_info_base->sq_tail = sq_tail;
	spin_unlock(&pchannel->owner_chn_lock);

	return 0;
}

static u32 sdma_channel_free_sqe_cnt(struct hisi_sdma_channel *pchannel)
{
	u32 head = sdma_channel_get_sq_head(pchannel);
	u32 tail = sdma_channel_get_sq_tail(pchannel);
	u32 res_cnt;

	if (tail >= head)
		res_cnt = HISI_SDMA_SQ_LENGTH - (tail - head) - 1;
	else
		res_cnt = head - tail - 1;

	return res_cnt;
}

static int sdma_task_info_validate(struct file_open_data *data,
				    struct hisi_sdma_task_info *task_info)
{
	struct hisi_sdma_channel *pchannel;
	u32 free_sqe_cnt;

	if (task_info->chn >= data->psdma_dev->nr_channel) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid channel num:%u!\n", task_info->chn);
		return -EINVAL;
	}
	pchannel = data->psdma_dev->channels + task_info->chn;
	free_sqe_cnt = sdma_channel_free_sqe_cnt(pchannel);
	if (task_info->task_cnt > HISI_SDMA_MAX_ALLOC_SIZE / sizeof(struct hisi_sdma_sqe_task) ||
	    task_info->task_cnt > free_sqe_cnt || task_info->task_cnt == 0) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid send task cnt:%u!\n",
			task_info->task_cnt);
		return -EINVAL;
	}

	return 0;
}
@@ -528,25 +593,20 @@ static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_t
static int ioctl_sdma_send_task(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct device *dev = &data->psdma_dev->pdev->dev;
	struct hisi_sdma_task_info task_info;
	struct hisi_sdma_sqe_task *task_list;
	int ret;

	if (copy_from_user(&task_info, (struct hisi_sdma_task_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_task_info))) {
		dev_err(&data->psdma_dev->pdev->dev, "get hisi_sdma_task_info failed\n");
		dev_err(dev, "get hisi_sdma_task_info failed\n");
		return -EFAULT;
	}
	if (task_info.chn < 0 || (u32)task_info.chn >= data->psdma_dev->nr_channel) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid channel num:%d!\n", task_info.chn);
		return -EINVAL;
	}
	if (task_info.task_cnt > HISI_SDMA_MAX_ALLOC_SIZE / sizeof(struct hisi_sdma_sqe_task) ||
	    task_info.task_cnt == 0) {
		dev_err(&data->psdma_dev->pdev->dev, "Invalid send task cnt:%u!\n",
			task_info.task_cnt);
		return -EINVAL;
	}
	ret = sdma_task_info_validate(data, &task_info);
	if (ret != 0)
		return ret;

	task_list = kcalloc_node(task_info.task_cnt, sizeof(struct hisi_sdma_sqe_task), GFP_KERNEL,
				 data->psdma_dev->node_idx);
	if (!task_list)
@@ -554,17 +614,19 @@ static int ioctl_sdma_send_task(struct file *file, unsigned long arg)

	if (copy_from_user(task_list, (void __user *)task_info.task_addr,
			   task_info.task_cnt * sizeof(struct hisi_sdma_sqe_task))) {
		dev_err(&data->psdma_dev->pdev->dev, "get hisi_sdma_sqe_task failed\n");
		dev_err(dev, "get hisi_sdma_sqe_task failed\n");
		ret = -EFAULT;
		goto free_list;
	}
	ret = sdma_send_task_kernel(data, &task_info, task_list);
	if (ret < 0)
		dev_err(&data->psdma_dev->pdev->dev, "exec sdma_send_task_kernel failed\n");
	if (ret < 0) {
		dev_err(dev, "exec sdma_send_task_kernel failed\n");
		goto free_list;
	}

	if (copy_to_user((struct hisi_sdma_task_info __user *)(uintptr_t)arg, &task_info,
			 sizeof(struct hisi_sdma_task_info))) {
		dev_err(&data->psdma_dev->pdev->dev, "set hisi_sdma_task_info failed\n");
		dev_err(dev, "set hisi_sdma_task_info failed\n");
		ret = -EFAULT;
	}

@@ -587,8 +649,8 @@ static int sdma_operation_reg(struct hisi_sdma_device *pdev, unsigned long arg,
		return -EFAULT;
	}

	if (reg_info.chn < 0 || (u32)reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%d!\n", reg_info.chn);
	if (reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%u!\n", reg_info.chn);
		return -EINVAL;
	}
	pchannel = pdev->channels + reg_info.chn;
@@ -650,19 +712,21 @@ static int ioctl_sdma_dfx_reg(struct file *file, unsigned long arg)
	dev = &pdev->pdev->dev;
	if (copy_from_user(&reg_info, (struct hisi_sdma_reg_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "get hisi_sdma_dfx_reg failed\n");
		dev_err(dev, "dfx_reg copy from user failed\n");
		return -EFAULT;
	}

	if (reg_info.chn < 0 || (u32)reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%d!\n", reg_info.chn);
	if (reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%u!\n", reg_info.chn);
		return -EINVAL;
	}
	pchannel = pdev->channels + reg_info.chn;
	reg_info.reg_value = sdma_channel_get_dfx(pchannel);
	if (copy_to_user((struct hisi_sdma_reg_info __user *)(uintptr_t)arg, &reg_info,
			 sizeof(struct hisi_sdma_reg_info)))
			 sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "dfx_reg copy to user failed\n");
		return -EFAULT;
	}

	return 0;
}
@@ -675,16 +739,16 @@ static int ioctl_sdma_sqe_cnt_reg(struct file *file, unsigned long arg)
	struct hisi_sdma_channel *pchannel;
	struct device *dev;

	if (reg_info.chn < 0 || (u32)reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%d!\n", reg_info.chn);
		return -EINVAL;
	}
	dev = &pdev->pdev->dev;
	if (copy_from_user(&reg_info, (struct hisi_sdma_reg_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "get hisi_sdma_reg_info failed\n");
		return -EFAULT;
	}
	if (reg_info.chn >= pdev->nr_channel) {
		dev_err(dev, "Invalid channel num:%u!\n", reg_info.chn);
		return -EINVAL;
	}

	pchannel = pdev->channels + reg_info.chn;
	if (reg_info.type == HISI_SDMA_CLR_NORMAL_SQE_CNT)
@@ -722,7 +786,7 @@ static long sdma_dev_ioctl(struct file *file, unsigned int cmd, unsigned long ar
	int cmd_num;
	int i;

	cmd_num = sizeof(g_ioctl_funcs) / sizeof(struct hisi_sdma_ioctl_func_list);
	cmd_num = ARRAY_SIZE(g_ioctl_funcs);
	for (i = 0; i < cmd_num; i++) {
		if (g_ioctl_funcs[i].cmd == cmd)
			return g_ioctl_funcs[i].ioctl_func(file, arg);
@@ -736,6 +800,7 @@ static int sdma_core_open(struct inode *inode, struct file *file)
	struct hisi_sdma_device *psdma_dev;
	dev_t sdma_dev;
	u32 sdma_idx;
	int ret;

	if (g_info.core_dev->sdma_device_num == 0) {
		pr_err("cannot find a sdma device\n");
@@ -744,11 +809,24 @@ static int sdma_core_open(struct inode *inode, struct file *file)
	sdma_dev = inode->i_rdev;
	sdma_idx = MINOR(sdma_dev);
	if (sdma_idx >= HISI_SDMA_MAX_DEVS) {
		pr_err("secondary device number overflow\n");
		pr_err("wrong id of sdma device\n");
		return -ENODEV;
	}
	spin_lock(&g_info.core_dev->device_lock);
	psdma_dev = g_info.core_dev->sdma_devices[sdma_idx];
	return __do_sdma_open(psdma_dev, file);
	if (!psdma_dev) {
		spin_unlock(&g_info.core_dev->device_lock);
		pr_err("cannot find sdma%u\n", sdma_idx);
		return -ENODEV;
	}
	ret = __do_sdma_open(psdma_dev, file);
	spin_unlock(&g_info.core_dev->device_lock);
	if (ret != 0) {
		pr_err("open sdma failed\n");
		return ret;
	}

	return 0;
}

ssize_t sdma_read_info(struct file *file, char __user *buf, size_t size, loff_t *ppos)
@@ -779,7 +857,7 @@ static int sdma_dev_release(struct inode *inode, struct file *file)

	spin_lock(&pdev->channel_lock);
	list_for_each_entry_safe(c, n, &data->non_share_chn_list, chn_list) {
		dev_dbg(dev, "release non_share_chn%d\n", c->chn_idx);
		dev_dbg(dev, "release non_share_chn%u\n", c->chn_idx);
		bitmap_set(pdev->channel_map, c->chn_idx - share_chns, 1);
		list_del(&c->chn_list);
		kfree(c);
@@ -787,7 +865,7 @@ static int sdma_dev_release(struct inode *inode, struct file *file)
	}

	list_for_each_entry_safe(c, n, &data->share_chn_list, chn_list) {
		dev_dbg(dev, "release share_chn%d\n", c->chn_idx);
		dev_dbg(dev, "release share_chn%u\n", c->chn_idx);
		pchannel = pdev->channels + c->chn_idx;
		pchannel->cnt_used--;
		if (pchannel->sync_info_base->lock != 0 &&
@@ -796,15 +874,15 @@ static int sdma_dev_release(struct inode *inode, struct file *file)
			pchannel->sync_info_base->lock = 0;
			pchannel->sync_info_base->lock_pid = 0;
		}
		if (pchannel->cnt_used == 0)
			pchannel->sync_info_base->err_cnt = 0;
		list_del(&c->chn_list);
		kfree(c);
	}
	spin_unlock(&pdev->channel_lock);

	if (data->handle) {
	if (data->handle)
		iommu_sva_unbind_device(data->handle);
		data->handle = NULL;
	}

	sdma_hash_free_entry(data->ida);
	sdma_free_authority_ht_with_pid(pid);
@@ -814,28 +892,38 @@ static int sdma_dev_release(struct inode *inode, struct file *file)
	return 0;
}

static int remap_addr_range(struct hisi_sdma_device *psdma_dev, u32 chn_num, u64 offset, u64 size)
static int remap_addr_range(u32 chn_num, u64 offset, u64 size)
{
	u64 sync_size;

	sync_size = (u64)((sizeof(struct hisi_sdma_queue_info) + PAGE_SIZE - ALIGN_NUM) /
		    PAGE_SIZE * PAGE_SIZE);

	if (offset >= chn_num * (HISI_SDMA_MMAP_SHMEM + 1))
	if (offset >= chn_num * (HISI_SDMA_MMAP_SHMEM + 1)) {
		pr_err("sdma mmap offset exceed range\n");
		return -EINVAL;
	}

	if (offset < chn_num * HISI_SDMA_MMAP_CQE) {
		pr_err("sdma not support sqe mmap\n");
		return -EINVAL;
	} else if (offset < chn_num * HISI_SDMA_MMAP_IO) {
		if (size > HISI_SDMA_CQ_SIZE)
		if (size > HISI_SDMA_CQ_SIZE) {
			pr_err("sdma mmap size exceed cqe range\n");
			return -EINVAL;
		}
		return HISI_SDMA_MMAP_CQE;
	} else if (offset < chn_num * HISI_SDMA_MMAP_SHMEM)
	} else if (offset < chn_num * HISI_SDMA_MMAP_SHMEM) {
		pr_err("sdma not support io reg mmap\n");
		return -EINVAL;
	if (size > sync_size)
	} else {
		if (size > sync_size) {
			pr_err("sdma mmap size exceed share mem range\n");
			return -EINVAL;
		}
		return HISI_SDMA_MMAP_SHMEM;
	}
}

static int sdma_dev_mmap(struct file *file, struct vm_area_struct *vma)
{
@@ -852,10 +940,10 @@ static int sdma_dev_mmap(struct file *file, struct vm_area_struct *vma)
	io_base = data->psdma_dev->base_addr;
	size = vma->vm_end - vma->vm_start;
	offset = vma->vm_pgoff;
	vma->vm_flags |= VM_DONTEXPAND | VM_WIPEONFORK | VM_DONTCOPY;

	dev_dbg(dev, "sdma total channel num = %u, user mmap offset = 0x%llx", chn_num, offset);

	switch (remap_addr_range(data->psdma_dev, chn_num, offset, size)) {
	switch (remap_addr_range(chn_num, offset, size)) {
	case HISI_SDMA_MMAP_CQE:
		pchan = chn_base + offset - chn_num * HISI_SDMA_MMAP_CQE;
		pfn_start = virt_to_phys(pchan->cq_base) >> PAGE_SHIFT;
@@ -871,9 +959,8 @@ static int sdma_dev_mmap(struct file *file, struct vm_area_struct *vma)
	default:
		return -EINVAL;
	}

	if (ret)
		dev_err(&data->psdma_dev->pdev->dev, "sdma mmap failed!\n");
		dev_err(dev, "sdma mmap failed!\n");

	return ret;
}
@@ -893,9 +980,9 @@ void sdma_cdev_init(struct cdev *cdev)
	cdev->owner = THIS_MODULE;
}

void sdma_info_sync_cdev(struct hisi_sdma_global_info *g_info_input)
void sdma_info_sync_cdev(struct hisi_sdma_core_device *p, u32 *share_chns, struct ida *fd_ida)
{
	g_info.core_dev = g_info_input->core_dev;
	g_info.fd_ida = g_info_input->fd_ida;
	g_info.share_chns = g_info_input->share_chns;
	g_info.core_dev = p;
	g_info.fd_ida = fd_ida;
	g_info.share_chns = share_chns;
}
+5 −1
Original line number Diff line number Diff line
@@ -29,6 +29,9 @@ struct hisi_sdma_channel {
	u16 idx;
	struct hisi_sdma_device *pdev;

	u32 ida;
	spinlock_t owner_chn_lock;

	/* must be page-aligned and continuous physical memory */
	struct hisi_sdma_sq_entry *sq_base;
	struct hisi_sdma_cq_entry *cq_base;
@@ -79,6 +82,7 @@ struct hisi_sdma_device {
struct hisi_sdma_core_device {
	u32 sdma_major;
	u32 sdma_device_num;
	spinlock_t device_lock;
	struct hisi_sdma_device *sdma_devices[HISI_SDMA_MAX_DEVS];
};

@@ -89,7 +93,7 @@ struct hisi_sdma_global_info {
};

void sdma_cdev_init(struct cdev *cdev);
void sdma_info_sync_cdev(struct hisi_sdma_global_info *g_info);
void sdma_info_sync_cdev(struct hisi_sdma_core_device *p, u32 *share_chns, struct ida *fd_ida);

static inline void chn_set_val(struct hisi_sdma_channel *pchan, int reg, u32 val, u32 mask)
{
+8 −6
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ irqreturn_t sdma_chn_ioe_irq_handle(int irq, void *psdma_dev)
	return IRQ_HANDLED;
}

void sdma_irq_init(struct hisi_sdma_device *sdma)
int sdma_irq_init(struct hisi_sdma_device *sdma)
{
	struct platform_device *pdev;
	void __iomem *io_addr;
@@ -61,7 +61,7 @@ void sdma_irq_init(struct hisi_sdma_device *sdma)
	u32 i;

	pdev = sdma->pdev;
	for (i = 0; i < sdma->nr_channel + HISI_STARS_CHN_NUM; i++) {
	for (i = HISI_STARS_CHN_NUM; i < sdma->nr_channel + HISI_STARS_CHN_NUM; i++) {
		io_addr = sdma->io_orig_base + i * HISI_SDMA_CHANNEL_IOMEM_SIZE;
		sdma_channel_set_irq_mask(io_addr, SDMA_IOC_MASKED_STATUS);
	}
@@ -72,27 +72,29 @@ void sdma_irq_init(struct hisi_sdma_device *sdma)
	for (i = 0; i < irq_cnt; i++) {
		vir_irq = platform_get_irq(pdev, i);
		if (vir_irq < 0) {
			dev_err(&pdev->dev, "get vir_irq[idx:%d] failed:%d!\n", i, vir_irq);
			dev_err(&pdev->dev, "get vir_irq[idx:%u] failed:%d!\n", i, vir_irq);
			sdma->irq[i] = -1;
			continue;
		}
		sdma->irq[i] = vir_irq;
	}

	for (i = INT_CH_IOE_SDMAM_0 + HISI_STARS_CHN_NUM; i <= INT_CH_IOE_SDMAM_255; i++) {
		if (sdma->irq[i] == -1)
	for (i = INT_CH_IOE_SDMAM_0 + HISI_STARS_CHN_NUM; i <= INT_CH_IOE_SDMAM_191; i++) {
		if (sdma->irq[i] == -1 || sdma->irq[i] == 0)
			continue;

		ret = devm_request_irq(&sdma->pdev->dev, sdma->irq[i], sdma_chn_ioe_irq_handle,
				       IRQF_ONESHOT, HISI_SDMA_IRQ_FUNC_NAME, sdma);
		if (ret != 0) {
			dev_err(&pdev->dev, "request_irq failed, ret=%d", ret);
			continue;
			return ret;
		}
	}

	for (i = 0; i < SDMA_IOE_NUM_MAX; i++)
		spin_lock_init(&err_set_lock[i]);

	return 0;
}

void sdma_irq_deinit(struct hisi_sdma_device *sdma)
Loading