summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarol Wachowski <karol.wachowski@intel.com>2020-11-24 16:59:43 +0300
committerJae Hyun Yoo <jae.hyun.yoo@intel.com>2021-07-14 20:04:18 +0300
commitb4c19b733995752a10d658482e337d31b9a76282 (patch)
tree0d8c2fbd76cbe197a217e26187e10916d61dedf0
parentb1b521b266fd66ef7c31f31e9301dfb16e2871c9 (diff)
downloadlinux-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.c178
-rw-r--r--include/uapi/linux/aspeed-mctp.h29
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 */