diff options
author | Karol Wachowski <karol.wachowski@intel.com> | 2020-11-24 16:59:43 +0300 |
---|---|---|
committer | Jae Hyun Yoo <jae.hyun.yoo@intel.com> | 2021-07-14 20:04:18 +0300 |
commit | b4c19b733995752a10d658482e337d31b9a76282 (patch) | |
tree | 0d8c2fbd76cbe197a217e26187e10916d61dedf0 | |
parent | b1b521b266fd66ef7c31f31e9301dfb16e2871c9 (diff) | |
download | linux-b4c19b733995752a10d658482e337d31b9a76282.tar.xz |
aspeed-mctp: Add EID information related ioctls
Implement two new ioctls for storing EID related information:
* ASPEED_MCTP_IOCTL_GET_EID_INFO
* ASPEED_MCTP_IOCTL_SET_EID_INFO
Driver stores EID mapping in a list which is traversed when
one tries to get information using ASPEED_MCTP_IOCTL_GET_EID_INFO
ioctl, when given EID mapping is not found in the list, next entry
is returned. When there are no entries with EIDs higher than specified
in the IOCTL call -ENODEV is returned.
Whenever new information about EID mapping is stored with
ASPEED_MCTP_IOCTL_SET_EID_INFO ioctl driver empties exsiting
list of mappings and creates new one based on user input.
After insertion list is sorted by EID. Invalid input
such as duplicated EIDs will cause driver to return -EINVAL.
Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
-rw-r--r-- | drivers/soc/aspeed/aspeed-mctp.c | 178 | ||||
-rw-r--r-- | include/uapi/linux/aspeed-mctp.h | 29 |
2 files changed, 207 insertions, 0 deletions
diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c index 87d76bf7c4fb..85ae1d842e5e 100644 --- a/drivers/soc/aspeed/aspeed-mctp.c +++ b/drivers/soc/aspeed/aspeed-mctp.c @@ -8,10 +8,12 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/list.h> +#include <linux/list_sort.h> #include <linux/mfd/syscon.h> #include <linux/miscdevice.h> #include <linux/mm.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of_platform.h> #include <linux/pci.h> #include <linux/poll.h> @@ -187,6 +189,12 @@ struct aspeed_mctp { * and default client */ spinlock_t clients_lock; + struct list_head endpoints; + size_t endpoints_count; + /* + * endpoints_lock protects list of endpoints + */ + struct mutex endpoints_lock; struct { struct regmap *map; struct delayed_work rst_dwork; @@ -214,6 +222,11 @@ struct mctp_type_handler { struct list_head link; }; +struct aspeed_mctp_endpoint { + struct aspeed_mctp_eid_info data; + struct list_head link; +}; + #define TX_CMD_BUF_SIZE \ PAGE_ALIGN(TX_CMD_COUNT * sizeof(struct aspeed_mctp_tx_cmd)) #define TX_DATA_BUF_SIZE \ @@ -868,6 +881,160 @@ aspeed_mctp_get_mtu(struct aspeed_mctp *priv, void __user *userbuf) return 0; } +static int +aspeed_mctp_get_eid_info(struct aspeed_mctp *priv, void __user *userbuf) +{ + int count = 0; + int ret = 0; + struct aspeed_mctp_get_eid_info get_eid; + struct aspeed_mctp_endpoint *endpoint; + struct aspeed_mctp_eid_info *user_ptr; + size_t count_to_copy; + + if (copy_from_user(&get_eid, userbuf, sizeof(get_eid))) { + dev_err(priv->dev, "copy from user failed\n"); + return -EFAULT; + } + + mutex_lock(&priv->endpoints_lock); + + if (get_eid.count == 0) { + count = priv->endpoints_count; + goto out_unlock; + } + + user_ptr = u64_to_user_ptr(get_eid.ptr); + count_to_copy = get_eid.count > priv->endpoints_count ? + priv->endpoints_count : get_eid.count; + list_for_each_entry(endpoint, &priv->endpoints, link) { + if (endpoint->data.eid < get_eid.start_eid) + continue; + if (count >= count_to_copy) + break; + if (copy_to_user(&user_ptr[count], &endpoint->data, sizeof(*user_ptr))) { + dev_err(priv->dev, "copy to user failed\n"); + ret = -EFAULT; + goto out_unlock; + } + count++; + } + +out_unlock: + get_eid.count = count; + if (copy_to_user(userbuf, &get_eid, sizeof(get_eid))) { + dev_err(priv->dev, "copy to user failed\n"); + ret = -EFAULT; + } + + mutex_unlock(&priv->endpoints_lock); + return ret; +} + +static int +eid_info_cmp(void *priv, struct list_head *a, struct list_head *b) +{ + struct aspeed_mctp_endpoint *endpoint_a; + struct aspeed_mctp_endpoint *endpoint_b; + + if (a == b) + return 0; + + endpoint_a = list_entry(a, typeof(*endpoint_a), link); + endpoint_b = list_entry(b, typeof(*endpoint_b), link); + + if (endpoint_a->data.eid < endpoint_b->data.eid) + return -1; + else if (endpoint_a->data.eid > endpoint_b->data.eid) + return 1; + + return 0; +} + +static void aspeed_mctp_eid_info_list_remove(struct list_head *list) +{ + struct aspeed_mctp_endpoint *endpoint; + struct aspeed_mctp_endpoint *tmp; + + list_for_each_entry_safe(endpoint, tmp, list, link) { + list_del(&endpoint->link); + kfree(endpoint); + } +} + +static bool +aspeed_mctp_eid_info_list_valid(struct list_head *list) +{ + struct aspeed_mctp_endpoint *endpoint; + struct aspeed_mctp_endpoint *next; + + list_for_each_entry(endpoint, list, link) { + next = list_next_entry(endpoint, link); + if (&next->link == list) + break; + + /* duplicted eids */ + if (next->data.eid == endpoint->data.eid) + return false; + } + + return true; +} + +static int +aspeed_mctp_set_eid_info(struct aspeed_mctp *priv, void __user *userbuf) +{ + struct list_head list = LIST_HEAD_INIT(list); + int ret = 0; + struct aspeed_mctp_set_eid_info set_eid; + struct aspeed_mctp_eid_info *user_ptr; + struct aspeed_mctp_endpoint *endpoint; + size_t i; + + if (copy_from_user(&set_eid, userbuf, sizeof(set_eid))) { + dev_err(priv->dev, "copy from user failed\n"); + return -EFAULT; + } + + if (set_eid.count > ASPEED_MCTP_EID_INFO_MAX) + return -EINVAL; + + user_ptr = u64_to_user_ptr(set_eid.ptr); + for (i = 0; i < set_eid.count; i++) { + endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); + if (!endpoint) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(&endpoint->data, &user_ptr[i], + sizeof(*user_ptr))) { + dev_err(priv->dev, "copy from user failed\n"); + kfree(endpoint); + ret = -EFAULT; + goto out; + } + + list_add_tail(&endpoint->link, &list); + } + + list_sort(NULL, &list, &eid_info_cmp); + if (!aspeed_mctp_eid_info_list_valid(&list)) { + ret = -EINVAL; + goto out; + } + + mutex_lock(&priv->endpoints_lock); + if (list_empty(&priv->endpoints)) + list_splice_init(&list, &priv->endpoints); + else + list_swap(&list, &priv->endpoints); + priv->endpoints_count = set_eid.count; + mutex_unlock(&priv->endpoints_lock); +out: + aspeed_mctp_eid_info_list_remove(&list); + return ret; +} + static long aspeed_mctp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -905,6 +1072,14 @@ aspeed_mctp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = aspeed_mctp_unregister_type_handler(client, userbuf); break; + case ASPEED_MCTP_IOCTL_GET_EID_INFO: + ret = aspeed_mctp_get_eid_info(priv, userbuf); + break; + + case ASPEED_MCTP_IOCTL_SET_EID_INFO: + ret = aspeed_mctp_set_eid_info(priv, userbuf); + break; + default: dev_err(priv->dev, "Command not found\n"); ret = -ENOTTY; @@ -1109,8 +1284,10 @@ static void aspeed_mctp_drv_init(struct aspeed_mctp *priv) { INIT_LIST_HEAD(&priv->clients); INIT_LIST_HEAD(&priv->mctp_type_handlers); + INIT_LIST_HEAD(&priv->endpoints); spin_lock_init(&priv->clients_lock); + mutex_init(&priv->endpoints_lock); INIT_DELAYED_WORK(&priv->pcie.rst_dwork, aspeed_mctp_reset_work); @@ -1122,6 +1299,7 @@ static void aspeed_mctp_drv_init(struct aspeed_mctp *priv) static void aspeed_mctp_drv_fini(struct aspeed_mctp *priv) { + aspeed_mctp_eid_info_list_remove(&priv->endpoints); tasklet_disable(&priv->tx.tasklet); tasklet_kill(&priv->tx.tasklet); tasklet_disable(&priv->rx.tasklet); diff --git a/include/uapi/linux/aspeed-mctp.h b/include/uapi/linux/aspeed-mctp.h index 2e8882133513..86ff04b71e92 100644 --- a/include/uapi/linux/aspeed-mctp.h +++ b/include/uapi/linux/aspeed-mctp.h @@ -27,6 +27,11 @@ #define ASPEED_MCTP_READY "PCIE_READY" /* + * maximum possible number of struct eid_info elements stored in list + */ +#define ASPEED_MCTP_EID_INFO_MAX 256 + +/* * MCTP operations * @ASPEED_MCTP_IOCTL_FILTER_EID: enable/disable filter incoming packets based * on Endpoint ID (BROKEN) @@ -41,6 +46,10 @@ * messages of specified MCTP type or PCI vendor defined type * @ASPEED_MCTP_IOCTL_UNREGISTER_TYPE_HANDLER Unregister client as handler * for specified MCTP type or PCI vendor defined message type + * @ASPEED_MCTP_GET_EID_INFO: read list of existing endpoint mappings + * returns count which is less of the two requested count and existing count + * @ASPEED_MCTP_SET_EID_INFO: write list of endpoint mappings + * overwrites already existing endpoint mappings */ struct aspeed_mctp_filter_eid { @@ -68,6 +77,22 @@ struct aspeed_mctp_type_handler_ioctl { __u16 vendor_type_mask; /* Mask applied to vendor type */ }; +struct aspeed_mctp_eid_info { + __u8 eid; + __u16 bdf; +}; + +struct aspeed_mctp_get_eid_info { + __u64 ptr; + __u16 count; + __u8 start_eid; +}; + +struct aspeed_mctp_set_eid_info { + __u64 ptr; + __u16 count; +}; + #define ASPEED_MCTP_IOCTL_BASE 0x4d #define ASPEED_MCTP_IOCTL_FILTER_EID \ @@ -84,5 +109,9 @@ struct aspeed_mctp_type_handler_ioctl { _IOW(ASPEED_MCTP_IOCTL_BASE, 6, struct aspeed_mctp_type_handler_ioctl) #define ASPEED_MCTP_IOCTL_UNREGISTER_TYPE_HANDLER \ _IOW(ASPEED_MCTP_IOCTL_BASE, 7, struct aspeed_mctp_type_handler_ioctl) +#define ASPEED_MCTP_IOCTL_GET_EID_INFO \ + _IOWR(ASPEED_MCTP_IOCTL_BASE, 8, struct aspeed_mctp_get_eid_info) +#define ASPEED_MCTP_IOCTL_SET_EID_INFO \ + _IOW(ASPEED_MCTP_IOCTL_BASE, 9, struct aspeed_mctp_set_eid_info) #endif /* _UAPI_LINUX_ASPEED_MCTP_H */ |