From cf79ddf5317dd09e5427c281c7ecf5d1f170ac30 Mon Sep 17 00:00:00 2001 From: lingmingqiang Date: Fri, 9 Aug 2019 21:07:39 +0800 Subject: [PATCH] UACCE: Support err device isolation of UACCE driver inclusion category: bugfix bugzilla: NA CVE: NA Feature or Bugfix:Feature Signed-off-by: tanghui20 Reviewed-by: wangzhou Signed-off-by: lingmingqiang Reviewed-by: lingmingqiang Reviewed-by: Yang Yingliang Signed-off-by: Yang Yingliang --- drivers/crypto/hisilicon/hpre/hpre_main.c | 10 ++ drivers/crypto/hisilicon/qm.c | 16 +++ drivers/crypto/hisilicon/zip/zip_main.c | 10 ++ drivers/uacce/uacce.c | 168 +++++++++++++++++++--- include/linux/uacce.h | 23 ++- 5 files changed, 201 insertions(+), 26 deletions(-) diff --git a/drivers/crypto/hisilicon/hpre/hpre_main.c b/drivers/crypto/hisilicon/hpre/hpre_main.c index 8d7095b26b92..3dde20991569 100644 --- a/drivers/crypto/hisilicon/hpre/hpre_main.c +++ b/drivers/crypto/hisilicon/hpre/hpre_main.c @@ -1170,6 +1170,16 @@ static int hpre_controller_reset_prepare(struct hpre *hpre) return ret; } +#ifdef CONFIG_CRYPTO_QM_UACCE + if (qm->use_uacce) { + ret = uacce_hw_err_isolate(&qm->uacce); + if (ret) { + dev_err(&pdev->dev, "Fails to isolate hw err!\n"); + return ret; + } + } +#endif + return 0; } diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index 524c90665678..2ba196f55627 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -1841,6 +1841,22 @@ static int qm_register_uacce(struct hisi_qm *qm) uacce->ops = &uacce_qm_ops; uacce->algs = qm->algs; + if (uacce->is_vf) { + struct uacce *pf_uacce; + struct device *pf_dev = &(pci_physfn(pdev)->dev); + + /* VF uses PF's isoalte data */ + pf_uacce = dev_to_uacce(pf_dev); + if (!pf_uacce) { + dev_err(&pdev->dev, "fail to PF device\n"); + return -ENODEV; + } + + uacce->isolate = &pf_uacce->isolate_data; + } else { + uacce->isolate = &uacce->isolate_data; + } + if (qm->ver == QM_HW_V1) uacce->api_ver = HISI_QM_API_VER_BASE; else diff --git a/drivers/crypto/hisilicon/zip/zip_main.c b/drivers/crypto/hisilicon/zip/zip_main.c index 341686f77cd7..ca0c403a73ce 100644 --- a/drivers/crypto/hisilicon/zip/zip_main.c +++ b/drivers/crypto/hisilicon/zip/zip_main.c @@ -1133,6 +1133,16 @@ static int hisi_zip_controller_reset_prepare(struct hisi_zip *hisi_zip) return ret; } +#ifdef CONFIG_CRYPTO_QM_UACCE + if (qm->use_uacce) { + ret = uacce_hw_err_isolate(&qm->uacce); + if (ret) { + dev_err(&pdev->dev, "Fails to isolate hw err!\n"); + return ret; + } + } +#endif + return 0; } diff --git a/drivers/uacce/uacce.c b/drivers/uacce/uacce.c index 4d97cccb1c43..f4250970196e 100644 --- a/drivers/uacce/uacce.c +++ b/drivers/uacce/uacce.c @@ -43,6 +43,7 @@ static DEFINE_RWLOCK(uacce_qs_lock); #endif #define UACCE_RESET_DELAY_MS 10 +#define UACCE_FROM_CDEV_ATTR(dev) container_of(dev, struct uacce, dev) static const struct file_operations uacce_fops; static int uacce_fops_fasync(int fd, struct file *file, int mode); @@ -69,6 +70,90 @@ void uacce_q_set_hw_reset(struct uacce_queue *q) } EXPORT_SYMBOL_GPL(uacce_q_set_hw_reset); +static int cdev_get(struct device *dev, void *data) +{ + struct uacce *uacce; + struct device **t_dev = data; + + uacce = UACCE_FROM_CDEV_ATTR(dev); + if (uacce->pdev == *t_dev) { + *t_dev = dev; + return 1; + } + + return 0; +} + +/** + * dev_to_uacce - Get structure uacce from its device + * @dev the device + */ +struct uacce *dev_to_uacce(struct device *dev) +{ + struct device **tdev = &dev; + int ret; + + ret = class_for_each_device(uacce_class, NULL, tdev, cdev_get); + if (ret) { + dev = *tdev; + return UACCE_FROM_CDEV_ATTR(dev); + } + return NULL; +} +EXPORT_SYMBOL_GPL(dev_to_uacce); + +/** + * uacce_hw_err_isolate - Try to isolate the uacce device with its VFs + * according to user's configuration of isolation strategy. Warning: this + * API should be called while there is no user on the device, or the users + * on this device are suspended by slot resetting preparation of PCI AER. + * @uacce the uacce device + */ +int uacce_hw_err_isolate(struct uacce *uacce) +{ + struct uacce_err_isolate *isolate = uacce->isolate; + struct uacce_hw_err *err, *tmp, *hw_err; + u32 count = 0; + +#define SECONDS_PER_HOUR 3600 + + /* all the hw errs are processed by PF driver */ + if (uacce->is_vf || atomic_read(&isolate->is_isolate) || + !isolate->hw_err_isolate_hz) + return 0; + + hw_err = kzalloc(sizeof(*hw_err), GFP_ATOMIC); + if (!hw_err) + return -ENOMEM; + hw_err->tick_stamp = jiffies; + list_for_each_entry_safe(err, tmp, &isolate->hw_errs, list) { + if ((hw_err->tick_stamp - err->tick_stamp) / HZ > + SECONDS_PER_HOUR) { + list_del(&err->list); + kfree(err); + } else { + count++; + } + } + list_add(&hw_err->list, &isolate->hw_errs); + + if (count >= isolate->hw_err_isolate_hz) + atomic_set(&isolate->is_isolate, 1); + + return 0; +} +EXPORT_SYMBOL_GPL(uacce_hw_err_isolate); + +void uacce_hw_err_destroy(struct uacce *uacce) +{ + struct uacce_hw_err *err, *tmp; + + list_for_each_entry_safe(err, tmp, &uacce->isolate_data.hw_errs, list) { + list_del(&err->list); + kfree(err); + } +} + const char *uacce_qfrt_str(struct uacce_qfile_region *qfr) { enum uacce_qfrt type = qfr->type; @@ -1029,27 +1114,25 @@ static const struct file_operations uacce_fops = { .fasync = uacce_fops_fasync, }; -#define UACCE_FROM_CDEV_ATTR(dev) container_of(dev, struct uacce, dev) - -static ssize_t uacce_dev_show_id(struct device *dev, +static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct uacce *uacce = UACCE_FROM_CDEV_ATTR(dev); return sprintf(buf, "%d\n", uacce->dev_id); } -static DEVICE_ATTR(id, S_IRUGO, uacce_dev_show_id, NULL); +static DEVICE_ATTR_RO(id); -static ssize_t uacce_dev_show_api(struct device *dev, +static ssize_t api_show(struct device *dev, struct device_attribute *attr, char *buf) { struct uacce *uacce = UACCE_FROM_CDEV_ATTR(dev); return sprintf(buf, "%s\n", uacce->api_ver); } -static DEVICE_ATTR(api, S_IRUGO, uacce_dev_show_api, NULL); +static DEVICE_ATTR_RO(api); -static ssize_t uacce_dev_show_numa_distance(struct device *dev, +static ssize_t numa_distance_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1061,9 +1144,9 @@ static ssize_t uacce_dev_show_numa_distance(struct device *dev, #endif return sprintf(buf, "%d\n", abs(distance)); } -static DEVICE_ATTR(numa_distance, S_IRUGO, uacce_dev_show_numa_distance, NULL); +static DEVICE_ATTR_RO(numa_distance); -static ssize_t uacce_dev_show_node_id(struct device *dev, +static ssize_t node_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1075,9 +1158,9 @@ static ssize_t uacce_dev_show_node_id(struct device *dev, #endif return sprintf(buf, "%d\n", node_id); } -static DEVICE_ATTR(node_id, S_IRUGO, uacce_dev_show_node_id, NULL); +static DEVICE_ATTR_RO(node_id); -static ssize_t uacce_dev_show_flags(struct device *dev, +static ssize_t flags_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1085,9 +1168,9 @@ static ssize_t uacce_dev_show_flags(struct device *dev, return sprintf(buf, "%d\n", uacce->flags); } -static DEVICE_ATTR(flags, S_IRUGO, uacce_dev_show_flags, NULL); +static DEVICE_ATTR_RO(flags); -static ssize_t uacce_dev_show_available_instances(struct device *dev, +static ssize_t available_instances_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1095,10 +1178,9 @@ static ssize_t uacce_dev_show_available_instances(struct device *dev, return sprintf(buf, "%d\n", uacce->ops->get_available_instances(uacce)); } -static DEVICE_ATTR(available_instances, S_IRUGO, - uacce_dev_show_available_instances, NULL); +static DEVICE_ATTR_RO(available_instances); -static ssize_t uacce_dev_show_algorithms(struct device *dev, +static ssize_t algorithms_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1106,9 +1188,9 @@ static ssize_t uacce_dev_show_algorithms(struct device *dev, return sprintf(buf, "%s", uacce->algs); } -static DEVICE_ATTR(algorithms, S_IRUGO, uacce_dev_show_algorithms, NULL); +static DEVICE_ATTR_RO(algorithms); -static ssize_t uacce_dev_show_qfrs_offset(struct device *dev, +static ssize_t qfrs_offset_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1128,7 +1210,50 @@ static ssize_t uacce_dev_show_qfrs_offset(struct device *dev, return ret; } -static DEVICE_ATTR(qfrs_offset, S_IRUGO, uacce_dev_show_qfrs_offset, NULL); +static DEVICE_ATTR_RO(qfrs_offset); + +static ssize_t isolate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uacce *uacce = UACCE_FROM_CDEV_ATTR(dev); + + return sprintf(buf, "%d\n", atomic_read(&uacce->isolate->is_isolate)); +} +static DEVICE_ATTR_RO(isolate); + +static ssize_t isolate_strategy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uacce *uacce = UACCE_FROM_CDEV_ATTR(dev); + + return sprintf(buf, "%u\n", uacce->isolate->hw_err_isolate_hz); +} + +static ssize_t isolate_strategy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uacce *uacce = UACCE_FROM_CDEV_ATTR(dev); + unsigned long val; + + /* must be set by PF */ + if (uacce->is_vf) + return -EINVAL; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (atomic_read(&uacce->ref)) + return -EBUSY; + + uacce->isolate->hw_err_isolate_hz = val; + + return count; +} +static DEVICE_ATTR_RW(isolate_strategy); + static ssize_t dev_state_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1148,6 +1273,8 @@ static struct attribute *uacce_dev_attrs[] = { &dev_attr_available_instances.attr, &dev_attr_algorithms.attr, &dev_attr_qfrs_offset.attr, + &dev_attr_isolate.attr, + &dev_attr_isolate_strategy.attr, &dev_attr_dev_state.attr, NULL, }; @@ -1411,6 +1538,7 @@ int uacce_register(struct uacce *uacce) dev_dbg(&uacce->dev, "register to uacce!\n"); atomic_set(&uacce->ref, 0); + INIT_LIST_HEAD(&uacce->isolate_data.hw_errs); return 0; } @@ -1435,7 +1563,7 @@ int uacce_unregister(struct uacce *uacce) #else uacce_unset_iommu_domain(uacce); #endif - + uacce_hw_err_destroy(uacce); uacce_destroy_chrdev(uacce); return 0; diff --git a/include/linux/uacce.h b/include/linux/uacce.h index a7d983731044..004b9ad8a59c 100644 --- a/include/linux/uacce.h +++ b/include/linux/uacce.h @@ -19,6 +19,17 @@ struct uacce; #define UACCE_QFRF_DMA BIT(3) /* use dma api for the region */ #define UACCE_QFRF_SELFMT BIT(4) /* self maintained qfr */ +struct uacce_hw_err { + struct list_head list; + unsigned long long tick_stamp; +}; + +struct uacce_err_isolate { + struct list_head hw_errs; + u32 hw_err_isolate_hz; /* user cfg freq which triggers isolation */ + atomic_t is_isolate; +}; + struct uacce_qfile_region { enum uacce_qfrt type; unsigned long iova; /* iova share between user and device space */ @@ -47,7 +58,7 @@ struct uacce_qfile_region { struct uacce_ops { int (*get_available_instances)(struct uacce *uacce); int (*get_queue)(struct uacce *uacce, unsigned long arg, - struct uacce_queue **q); + struct uacce_queue **q); void (*put_queue)(struct uacce_queue *q); int (*start_queue)(struct uacce_queue *q); void (*stop_queue)(struct uacce_queue *q); @@ -81,13 +92,9 @@ struct uacce_queue { void *priv; wait_queue_head_t wait; int pasid; - struct list_head list; /* as list for as->qs */ - struct mm_struct *mm; - struct uacce_qfile_region *qfrs[UACCE_QFRT_MAX]; - struct fasync_struct *async_queue; enum uacce_q_state state; }; @@ -97,9 +104,9 @@ struct uacce { const char *drv_name; const char *algs; const char *api_ver; - unsigned int flags; unsigned long qf_pg_start[UACCE_QFRT_MAX]; int status; + unsigned int flags; struct uacce_ops *ops; struct device *pdev; bool is_vf; @@ -109,6 +116,8 @@ struct uacce { void *priv; atomic_t ref; int prot; + struct uacce_err_isolate isolate_data; + struct uacce_err_isolate *isolate; }; int uacce_register(struct uacce *uacce); @@ -116,5 +125,7 @@ int uacce_unregister(struct uacce *uacce); void uacce_wake_up(struct uacce_queue *q); const char *uacce_qfrt_str(struct uacce_qfile_region *qfr); void uacce_q_set_hw_reset(struct uacce_queue *q); +struct uacce *dev_to_uacce(struct device *dev); +int uacce_hw_err_isolate(struct uacce *uacce); #endif -- GitLab