Message ID | 1575945755-27380-3-git-send-email-zhangfei.gao@linaro.org (mailing list archive) |
---|---|
State | Not Applicable |
Delegated to: | Herbert Xu |
Headers | show |
Series | Add uacce module for Accelerator | expand |
Hi Zhangfei, Thank you for the patch! Yet something to improve: [auto build test ERROR on cryptodev/master] [also build test ERROR on crypto/master char-misc/char-misc-testing v5.5-rc1 next-20191209] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Zhangfei-Gao/Add-uacce-module-for-Accelerator/20191210-160210 base: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master config: s390-allmodconfig (attached as .config) compiler: s390-linux-gcc (GCC) 7.5.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.5.0 make.cross ARCH=s390 If you fix the issue, kindly add following tag Reported-by: kbuild test robot <lkp@intel.com> All error/warnings (new ones prefixed by >>): >> drivers/misc/uacce/uacce.c:112:15: error: variable 'uacce_sva_ops' has initializer but incomplete type static struct iommu_sva_ops uacce_sva_ops = { ^~~~~~~~~~~~~ >> drivers/misc/uacce/uacce.c:113:3: error: 'struct iommu_sva_ops' has no member named 'mm_exit' .mm_exit = uacce_sva_exit, ^~~~~~~ >> drivers/misc/uacce/uacce.c:113:13: warning: excess elements in struct initializer .mm_exit = uacce_sva_exit, ^~~~~~~~~~~~~~ drivers/misc/uacce/uacce.c:113:13: note: (near initialization for 'uacce_sva_ops') drivers/misc/uacce/uacce.c: In function 'uacce_mm_get': >> drivers/misc/uacce/uacce.c:144:12: error: implicit declaration of function 'iommu_sva_bind_device'; did you mean 'bus_find_device'? [-Werror=implicit-function-declaration] handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); ^~~~~~~~~~~~~~~~~~~~~ bus_find_device >> drivers/misc/uacce/uacce.c:144:10: warning: assignment makes pointer from integer without a cast [-Wint-conversion] handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); ^ >> drivers/misc/uacce/uacce.c:148:9: error: implicit declaration of function 'iommu_sva_set_ops'; did you mean 'iommu_setup_dma_ops'? [-Werror=implicit-function-declaration] ret = iommu_sva_set_ops(handle, &uacce_sva_ops); ^~~~~~~~~~~~~~~~~ iommu_setup_dma_ops >> drivers/misc/uacce/uacce.c:152:21: error: implicit declaration of function 'iommu_sva_get_pasid' [-Werror=implicit-function-declaration] uacce_mm->pasid = iommu_sva_get_pasid(handle); ^~~~~~~~~~~~~~~~~~~ >> drivers/misc/uacce/uacce.c:153:26: error: 'IOMMU_PASID_INVALID' undeclared (first use in this function); did you mean '_PAGE_INVALID'? if (uacce_mm->pasid == IOMMU_PASID_INVALID) ^~~~~~~~~~~~~~~~~~~ _PAGE_INVALID drivers/misc/uacce/uacce.c:153:26: note: each undeclared identifier is reported only once for each function it appears in >> drivers/misc/uacce/uacce.c:168:3: error: implicit declaration of function 'iommu_sva_unbind_device'; did you mean 'bus_find_device'? [-Werror=implicit-function-declaration] iommu_sva_unbind_device(handle); ^~~~~~~~~~~~~~~~~~~~~~~ bus_find_device drivers/misc/uacce/uacce.c: In function 'uacce_alloc': >> drivers/misc/uacce/uacce.c:502:9: error: implicit declaration of function 'iommu_dev_enable_feature'; did you mean 'module_enable_ro'? [-Werror=implicit-function-declaration] ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~~~~~~~ module_enable_ro >> drivers/misc/uacce/uacce.c:502:42: error: 'IOMMU_DEV_FEAT_SVA' undeclared (first use in this function); did you mean 'NOMMU_VMFLAGS'? ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~ NOMMU_VMFLAGS >> drivers/misc/uacce/uacce.c:530:3: error: implicit declaration of function 'iommu_dev_disable_feature'; did you mean 'module_disable_ro'? [-Werror=implicit-function-declaration] iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~~~~~~~~ module_disable_ro drivers/misc/uacce/uacce.c: In function 'uacce_remove': drivers/misc/uacce/uacce.c:592:44: error: 'IOMMU_DEV_FEAT_SVA' undeclared (first use in this function); did you mean 'NOMMU_VMFLAGS'? iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~ NOMMU_VMFLAGS drivers/misc/uacce/uacce.c: At top level: >> drivers/misc/uacce/uacce.c:112:29: error: storage size of 'uacce_sva_ops' isn't known static struct iommu_sva_ops uacce_sva_ops = { ^~~~~~~~~~~~~ cc1: some warnings being treated as errors vim +/uacce_sva_ops +112 drivers/misc/uacce/uacce.c 111 > 112 static struct iommu_sva_ops uacce_sva_ops = { > 113 .mm_exit = uacce_sva_exit, 114 }; 115 116 static struct uacce_mm *uacce_mm_get(struct uacce_device *uacce, 117 struct uacce_queue *q, 118 struct mm_struct *mm) 119 { 120 struct uacce_mm *uacce_mm = NULL; 121 struct iommu_sva *handle = NULL; 122 int ret; 123 124 lockdep_assert_held(&uacce->mm_lock); 125 126 list_for_each_entry(uacce_mm, &uacce->mm_list, list) { 127 if (uacce_mm->mm == mm) { 128 mutex_lock(&uacce_mm->lock); 129 list_add(&q->list, &uacce_mm->queues); 130 mutex_unlock(&uacce_mm->lock); 131 return uacce_mm; 132 } 133 } 134 135 uacce_mm = kzalloc(sizeof(*uacce_mm), GFP_KERNEL); 136 if (!uacce_mm) 137 return NULL; 138 139 if (uacce->flags & UACCE_DEV_SVA) { 140 /* 141 * Safe to pass an incomplete uacce_mm, since mm_exit cannot 142 * fire while we hold a reference to the mm. 143 */ > 144 handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); 145 if (IS_ERR(handle)) 146 goto err_free; 147 > 148 ret = iommu_sva_set_ops(handle, &uacce_sva_ops); 149 if (ret) 150 goto err_unbind; 151 > 152 uacce_mm->pasid = iommu_sva_get_pasid(handle); > 153 if (uacce_mm->pasid == IOMMU_PASID_INVALID) 154 goto err_unbind; 155 } 156 157 uacce_mm->mm = mm; 158 uacce_mm->handle = handle; 159 INIT_LIST_HEAD(&uacce_mm->queues); 160 mutex_init(&uacce_mm->lock); 161 list_add(&q->list, &uacce_mm->queues); 162 list_add(&uacce_mm->list, &uacce->mm_list); 163 164 return uacce_mm; 165 166 err_unbind: 167 if (handle) > 168 iommu_sva_unbind_device(handle); 169 err_free: 170 kfree(uacce_mm); 171 return NULL; 172 } 173 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org Intel Corporation
Hi Zhangfei, Thank you for the patch! Yet something to improve: [auto build test ERROR on cryptodev/master] [also build test ERROR on crypto/master char-misc/char-misc-testing v5.5-rc1 next-20191210] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Zhangfei-Gao/Add-uacce-module-for-Accelerator/20191210-160210 base: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master config: sparc64-allmodconfig (attached as .config) compiler: sparc64-linux-gcc (GCC) 7.5.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.5.0 make.cross ARCH=sparc64 If you fix the issue, kindly add following tag Reported-by: kbuild test robot <lkp@intel.com> All errors (new ones prefixed by >>): drivers/misc/uacce/uacce.c:112:15: error: variable 'uacce_sva_ops' has initializer but incomplete type static struct iommu_sva_ops uacce_sva_ops = { ^~~~~~~~~~~~~ drivers/misc/uacce/uacce.c:113:3: error: 'struct iommu_sva_ops' has no member named 'mm_exit' .mm_exit = uacce_sva_exit, ^~~~~~~ drivers/misc/uacce/uacce.c:113:13: warning: excess elements in struct initializer .mm_exit = uacce_sva_exit, ^~~~~~~~~~~~~~ drivers/misc/uacce/uacce.c:113:13: note: (near initialization for 'uacce_sva_ops') drivers/misc/uacce/uacce.c: In function 'uacce_mm_get': drivers/misc/uacce/uacce.c:144:12: error: implicit declaration of function 'iommu_sva_bind_device'; did you mean 'bus_find_device'? [-Werror=implicit-function-declaration] handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); ^~~~~~~~~~~~~~~~~~~~~ bus_find_device drivers/misc/uacce/uacce.c:144:10: warning: assignment makes pointer from integer without a cast [-Wint-conversion] handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); ^ drivers/misc/uacce/uacce.c:148:9: error: implicit declaration of function 'iommu_sva_set_ops'; did you mean 'iommu_setup_dma_ops'? [-Werror=implicit-function-declaration] ret = iommu_sva_set_ops(handle, &uacce_sva_ops); ^~~~~~~~~~~~~~~~~ iommu_setup_dma_ops drivers/misc/uacce/uacce.c:152:21: error: implicit declaration of function 'iommu_sva_get_pasid' [-Werror=implicit-function-declaration] uacce_mm->pasid = iommu_sva_get_pasid(handle); ^~~~~~~~~~~~~~~~~~~ >> drivers/misc/uacce/uacce.c:153:26: error: 'IOMMU_PASID_INVALID' undeclared (first use in this function); did you mean 'HV_MSIVALID_INVALID'? if (uacce_mm->pasid == IOMMU_PASID_INVALID) ^~~~~~~~~~~~~~~~~~~ HV_MSIVALID_INVALID drivers/misc/uacce/uacce.c:153:26: note: each undeclared identifier is reported only once for each function it appears in drivers/misc/uacce/uacce.c:168:3: error: implicit declaration of function 'iommu_sva_unbind_device'; did you mean 'bus_find_device'? [-Werror=implicit-function-declaration] iommu_sva_unbind_device(handle); ^~~~~~~~~~~~~~~~~~~~~~~ bus_find_device drivers/misc/uacce/uacce.c: At top level: >> drivers/misc/uacce/uacce.c:274:21: error: variable 'uacce_vm_ops' has initializer but incomplete type static const struct vm_operations_struct uacce_vm_ops = { ^~~~~~~~~~~~~~~~~~~~ >> drivers/misc/uacce/uacce.c:275:3: error: 'const struct vm_operations_struct' has no member named 'close' .close = uacce_vma_close, ^~~~~ drivers/misc/uacce/uacce.c:275:11: warning: excess elements in struct initializer .close = uacce_vma_close, ^~~~~~~~~~~~~~~ drivers/misc/uacce/uacce.c:275:11: note: (near initialization for 'uacce_vm_ops') drivers/misc/uacce/uacce.c: In function 'uacce_fops_mmap': >> drivers/misc/uacce/uacce.c:295:19: error: 'VM_DONTCOPY' undeclared (first use in this function) vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; ^~~~~~~~~~~ >> drivers/misc/uacce/uacce.c:295:33: error: 'VM_DONTEXPAND' undeclared (first use in this function); did you mean 'VM_DONTCOPY'? vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; ^~~~~~~~~~~~~ VM_DONTCOPY >> drivers/misc/uacce/uacce.c:295:49: error: 'VM_WIPEONFORK' undeclared (first use in this function) vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; ^~~~~~~~~~~~~ drivers/misc/uacce/uacce.c: In function 'uacce_alloc': drivers/misc/uacce/uacce.c:502:9: error: implicit declaration of function 'iommu_dev_enable_feature'; did you mean 'module_enable_ro'? [-Werror=implicit-function-declaration] ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~~~~~~~ module_enable_ro drivers/misc/uacce/uacce.c:502:42: error: 'IOMMU_DEV_FEAT_SVA' undeclared (first use in this function); did you mean 'NOMMU_VMFLAGS'? ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~ NOMMU_VMFLAGS drivers/misc/uacce/uacce.c:530:3: error: implicit declaration of function 'iommu_dev_disable_feature'; did you mean 'module_disable_ro'? [-Werror=implicit-function-declaration] iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~~~~~~~~ module_disable_ro drivers/misc/uacce/uacce.c: In function 'uacce_remove': drivers/misc/uacce/uacce.c:592:44: error: 'IOMMU_DEV_FEAT_SVA' undeclared (first use in this function); did you mean 'NOMMU_VMFLAGS'? iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); ^~~~~~~~~~~~~~~~~~ NOMMU_VMFLAGS drivers/misc/uacce/uacce.c: At top level: drivers/misc/uacce/uacce.c:112:29: error: storage size of 'uacce_sva_ops' isn't known static struct iommu_sva_ops uacce_sva_ops = { ^~~~~~~~~~~~~ >> drivers/misc/uacce/uacce.c:274:42: error: storage size of 'uacce_vm_ops' isn't known static const struct vm_operations_struct uacce_vm_ops = { ^~~~~~~~~~~~ cc1: some warnings being treated as errors vim +153 drivers/misc/uacce/uacce.c 115 116 static struct uacce_mm *uacce_mm_get(struct uacce_device *uacce, 117 struct uacce_queue *q, 118 struct mm_struct *mm) 119 { 120 struct uacce_mm *uacce_mm = NULL; 121 struct iommu_sva *handle = NULL; 122 int ret; 123 124 lockdep_assert_held(&uacce->mm_lock); 125 126 list_for_each_entry(uacce_mm, &uacce->mm_list, list) { 127 if (uacce_mm->mm == mm) { 128 mutex_lock(&uacce_mm->lock); 129 list_add(&q->list, &uacce_mm->queues); 130 mutex_unlock(&uacce_mm->lock); 131 return uacce_mm; 132 } 133 } 134 135 uacce_mm = kzalloc(sizeof(*uacce_mm), GFP_KERNEL); 136 if (!uacce_mm) 137 return NULL; 138 139 if (uacce->flags & UACCE_DEV_SVA) { 140 /* 141 * Safe to pass an incomplete uacce_mm, since mm_exit cannot 142 * fire while we hold a reference to the mm. 143 */ 144 handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); 145 if (IS_ERR(handle)) 146 goto err_free; 147 > 148 ret = iommu_sva_set_ops(handle, &uacce_sva_ops); 149 if (ret) 150 goto err_unbind; 151 152 uacce_mm->pasid = iommu_sva_get_pasid(handle); > 153 if (uacce_mm->pasid == IOMMU_PASID_INVALID) 154 goto err_unbind; 155 } 156 157 uacce_mm->mm = mm; 158 uacce_mm->handle = handle; 159 INIT_LIST_HEAD(&uacce_mm->queues); 160 mutex_init(&uacce_mm->lock); 161 list_add(&q->list, &uacce_mm->queues); 162 list_add(&uacce_mm->list, &uacce->mm_list); 163 164 return uacce_mm; 165 166 err_unbind: 167 if (handle) 168 iommu_sva_unbind_device(handle); 169 err_free: 170 kfree(uacce_mm); 171 return NULL; 172 } 173 174 static void uacce_mm_put(struct uacce_queue *q) 175 { 176 struct uacce_mm *uacce_mm = q->uacce_mm; 177 178 lockdep_assert_held(&q->uacce->mm_lock); 179 180 mutex_lock(&uacce_mm->lock); 181 list_del(&q->list); 182 mutex_unlock(&uacce_mm->lock); 183 184 if (list_empty(&uacce_mm->queues)) { 185 if (uacce_mm->handle) 186 iommu_sva_unbind_device(uacce_mm->handle); 187 list_del(&uacce_mm->list); 188 kfree(uacce_mm); 189 } 190 } 191 192 static int uacce_fops_open(struct inode *inode, struct file *filep) 193 { 194 struct uacce_mm *uacce_mm = NULL; 195 struct uacce_device *uacce; 196 struct uacce_queue *q; 197 int ret = 0; 198 199 uacce = xa_load(&uacce_xa, iminor(inode)); 200 if (!uacce) 201 return -ENODEV; 202 203 if (!try_module_get(uacce->parent->driver->owner)) 204 return -ENODEV; 205 206 q = kzalloc(sizeof(struct uacce_queue), GFP_KERNEL); 207 if (!q) { 208 ret = -ENOMEM; 209 goto out_with_module; 210 } 211 212 mutex_lock(&uacce->mm_lock); 213 uacce_mm = uacce_mm_get(uacce, q, current->mm); 214 mutex_unlock(&uacce->mm_lock); 215 if (!uacce_mm) { 216 ret = -ENOMEM; 217 goto out_with_mem; 218 } 219 220 q->uacce = uacce; 221 q->uacce_mm = uacce_mm; 222 223 if (uacce->ops->get_queue) { 224 ret = uacce->ops->get_queue(uacce, uacce_mm->pasid, q); 225 if (ret < 0) 226 goto out_with_mm; 227 } 228 229 init_waitqueue_head(&q->wait); 230 filep->private_data = q; 231 q->state = UACCE_Q_INIT; 232 233 return 0; 234 235 out_with_mm: 236 mutex_lock(&uacce->mm_lock); 237 uacce_mm_put(q); 238 mutex_unlock(&uacce->mm_lock); 239 out_with_mem: 240 kfree(q); 241 out_with_module: 242 module_put(uacce->parent->driver->owner); 243 return ret; 244 } 245 246 static int uacce_fops_release(struct inode *inode, struct file *filep) 247 { 248 struct uacce_queue *q = filep->private_data; 249 struct uacce_device *uacce = q->uacce; 250 251 uacce_put_queue(q); 252 253 mutex_lock(&uacce->mm_lock); 254 uacce_mm_put(q); 255 mutex_unlock(&uacce->mm_lock); 256 257 kfree(q); 258 module_put(uacce->parent->driver->owner); 259 260 return 0; 261 } 262 263 static void uacce_vma_close(struct vm_area_struct *vma) 264 { 265 struct uacce_queue *q = vma->vm_private_data; 266 struct uacce_qfile_region *qfr = NULL; 267 268 if (vma->vm_pgoff < UACCE_MAX_REGION) 269 qfr = q->qfrs[vma->vm_pgoff]; 270 271 kfree(qfr); 272 } 273 > 274 static const struct vm_operations_struct uacce_vm_ops = { > 275 .close = uacce_vma_close, 276 }; 277 278 static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) 279 { 280 struct uacce_queue *q = filep->private_data; 281 struct uacce_device *uacce = q->uacce; 282 struct uacce_qfile_region *qfr; 283 enum uacce_qfrt type = UACCE_MAX_REGION; 284 int ret = 0; 285 286 if (vma->vm_pgoff < UACCE_MAX_REGION) 287 type = vma->vm_pgoff; 288 else 289 return -EINVAL; 290 291 qfr = kzalloc(sizeof(*qfr), GFP_KERNEL); 292 if (!qfr) 293 return -ENOMEM; 294 > 295 vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; 296 vma->vm_ops = &uacce_vm_ops; 297 vma->vm_private_data = q; 298 qfr->type = type; 299 300 mutex_lock(&uacce_mutex); 301 302 if (q->state != UACCE_Q_INIT && q->state != UACCE_Q_STARTED) { 303 ret = -EINVAL; 304 goto out_with_lock; 305 } 306 307 if (q->qfrs[type]) { 308 ret = -EEXIST; 309 goto out_with_lock; 310 } 311 312 switch (type) { 313 case UACCE_QFRT_MMIO: 314 if (!uacce->ops->mmap) { 315 ret = -EINVAL; 316 goto out_with_lock; 317 } 318 319 ret = uacce->ops->mmap(q, vma, qfr); 320 if (ret) 321 goto out_with_lock; 322 323 break; 324 325 case UACCE_QFRT_DUS: 326 if (uacce->flags & UACCE_DEV_SVA) { 327 if (!uacce->ops->mmap) { 328 ret = -EINVAL; 329 goto out_with_lock; 330 } 331 332 ret = uacce->ops->mmap(q, vma, qfr); 333 if (ret) 334 goto out_with_lock; 335 } 336 break; 337 338 default: 339 ret = -EINVAL; 340 goto out_with_lock; 341 } 342 343 q->qfrs[type] = qfr; 344 mutex_unlock(&uacce_mutex); 345 346 return ret; 347 348 out_with_lock: 349 mutex_unlock(&uacce_mutex); 350 kfree(qfr); 351 return ret; 352 } 353 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org Intel Corporation
On 2019/12/11 上午8:09, kbuild test robot wrote: > Hi Zhangfei, > > Thank you for the patch! Yet something to improve: > > [auto build test ERROR on cryptodev/master] > [also build test ERROR on crypto/master char-misc/char-misc-testing v5.5-rc1 next-20191210] > [if your patch is applied to the wrong git tree, please drop us a note to help > improve the system. BTW, we also suggest to use '--base' option to specify the > base tree in git format-patch, please see https://stackoverflow.com/a/37406982] > > url: https://github.com/0day-ci/linux/commits/Zhangfei-Gao/Add-uacce-module-for-Accelerator/20191210-160210 > base: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master > config: sparc64-allmodconfig (attached as .config) > compiler: sparc64-linux-gcc (GCC) 7.5.0 > reproduce: > wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross > chmod +x ~/bin/make.cross > # save the attached .config to linux build tree > GCC_VERSION=7.5.0 make.cross ARCH=sparc64 > > If you fix the issue, kindly add following tag > Reported-by: kbuild test robot <lkp@intel.com> > > All errors (new ones prefixed by >>): > > drivers/misc/uacce/uacce.c:112:15: error: variable 'uacce_sva_ops' has initializer but incomplete type > static struct iommu_sva_ops uacce_sva_ops = { > ^~~~~~~~~~~~~ > drivers/misc/uacce/uacce.c:113:3: error: 'struct iommu_sva_ops' has no member named 'mm_exit' > .mm_exit = uacce_sva_exit, > ^~~~~~~ > drivers/misc/uacce/uacce.c:113:13: warning: excess elements in struct initializer > .mm_exit = uacce_sva_exit, > ^~~~~~~~~~~~~~ > drivers/misc/uacce/uacce.c:113:13: note: (near initialization for 'uacce_sva_ops') > drivers/misc/uacce/uacce.c: In function 'uacce_mm_get': > drivers/misc/uacce/uacce.c:144:12: error: implicit declaration of function 'iommu_sva_bind_device'; did you mean 'bus_find_device'? [-Werror=implicit-function-declaration] > handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); > ^~~~~~~~~~~~~~~~~~~~~ > bus_find_device > drivers/misc/uacce/uacce.c:144:10: warning: assignment makes pointer from integer without a cast [-Wint-conversion] > handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); > ^ > drivers/misc/uacce/uacce.c:148:9: error: implicit declaration of function 'iommu_sva_set_ops'; did you mean 'iommu_setup_dma_ops'? [-Werror=implicit-function-declaration] > ret = iommu_sva_set_ops(handle, &uacce_sva_ops); > ^~~~~~~~~~~~~~~~~ > iommu_setup_dma_ops > drivers/misc/uacce/uacce.c:152:21: error: implicit declaration of function 'iommu_sva_get_pasid' [-Werror=implicit-function-declaration] > uacce_mm->pasid = iommu_sva_get_pasid(handle); > ^~~~~~~~~~~~~~~~~~~ >>> drivers/misc/uacce/uacce.c:153:26: error: 'IOMMU_PASID_INVALID' undeclared (first use in this function); did you mean 'HV_MSIVALID_INVALID'? > if (uacce_mm->pasid == IOMMU_PASID_INVALID) > ^~~~~~~~~~~~~~~~~~~ > HV_MSIVALID_INVALID > drivers/misc/uacce/uacce.c:153:26: note: each undeclared identifier is reported only once for each function it appears in > drivers/misc/uacce/uacce.c:168:3: error: implicit declaration of function 'iommu_sva_unbind_device'; did you mean 'bus_find_device'? [-Werror=implicit-function-declaration] > iommu_sva_unbind_device(handle); > ^~~~~~~~~~~~~~~~~~~~~~~ > bus_find_device > drivers/misc/uacce/uacce.c: At top level: >>> drivers/misc/uacce/uacce.c:274:21: error: variable 'uacce_vm_ops' has initializer but incomplete type > static const struct vm_operations_struct uacce_vm_ops = { > ^~~~~~~~~~~~~~~~~~~~ >>> drivers/misc/uacce/uacce.c:275:3: error: 'const struct vm_operations_struct' has no member named 'close' > .close = uacce_vma_close, > ^~~~~ > drivers/misc/uacce/uacce.c:275:11: warning: excess elements in struct initializer > .close = uacce_vma_close, > ^~~~~~~~~~~~~~~ > drivers/misc/uacce/uacce.c:275:11: note: (near initialization for 'uacce_vm_ops') > drivers/misc/uacce/uacce.c: In function 'uacce_fops_mmap': >>> drivers/misc/uacce/uacce.c:295:19: error: 'VM_DONTCOPY' undeclared (first use in this function) > vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; > ^~~~~~~~~~~ >>> drivers/misc/uacce/uacce.c:295:33: error: 'VM_DONTEXPAND' undeclared (first use in this function); did you mean 'VM_DONTCOPY'? > vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; > ^~~~~~~~~~~~~ > VM_DONTCOPY >>> drivers/misc/uacce/uacce.c:295:49: error: 'VM_WIPEONFORK' undeclared (first use in this function) > vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; > ^~~~~~~~~~~~~ > drivers/misc/uacce/uacce.c: In function 'uacce_alloc': > drivers/misc/uacce/uacce.c:502:9: error: implicit declaration of function 'iommu_dev_enable_feature'; did you mean 'module_enable_ro'? [-Werror=implicit-function-declaration] > ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); > ^~~~~~~~~~~~~~~~~~~~~~~~ > module_enable_ro > drivers/misc/uacce/uacce.c:502:42: error: 'IOMMU_DEV_FEAT_SVA' undeclared (first use in this function); did you mean 'NOMMU_VMFLAGS'? > ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); > ^~~~~~~~~~~~~~~~~~ > NOMMU_VMFLAGS > drivers/misc/uacce/uacce.c:530:3: error: implicit declaration of function 'iommu_dev_disable_feature'; did you mean 'module_disable_ro'? [-Werror=implicit-function-declaration] > iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); > ^~~~~~~~~~~~~~~~~~~~~~~~~ > module_disable_ro > drivers/misc/uacce/uacce.c: In function 'uacce_remove': > drivers/misc/uacce/uacce.c:592:44: error: 'IOMMU_DEV_FEAT_SVA' undeclared (first use in this function); did you mean 'NOMMU_VMFLAGS'? > iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); > ^~~~~~~~~~~~~~~~~~ > NOMMU_VMFLAGS > drivers/misc/uacce/uacce.c: At top level: > drivers/misc/uacce/uacce.c:112:29: error: storage size of 'uacce_sva_ops' isn't known > static struct iommu_sva_ops uacce_sva_ops = { > ^~~~~~~~~~~~~ >>> drivers/misc/uacce/uacce.c:274:42: error: storage size of 'uacce_vm_ops' isn't known > The build issue can be solved by -#include <linux/dma-iommu.h> +#include <linux/dma-mapping.h> +#include <linux/iommu.h> Since CONFIG_IOMMU_DMA is not defined in the arch. include/linux/dma-iommu.h #ifdef CONFIG_IOMMU_DMA #include <linux/dma-mapping.h> #include <linux/iommu.h> Will update later. Thanks
diff --git a/Documentation/ABI/testing/sysfs-driver-uacce b/Documentation/ABI/testing/sysfs-driver-uacce new file mode 100644 index 0000000..8520d68 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-uacce @@ -0,0 +1,37 @@ +What: /sys/class/uacce/<dev_name>/api +Date: Dec 2019 +KernelVersion: 5.6 +Contact: linux-accelerators@lists.ozlabs.org +Description: Api of the device, no requirement of the format + Application use the api to match the correct driver + +What: /sys/class/uacce/<dev_name>/flags +Date: Dec 2019 +KernelVersion: 5.6 +Contact: linux-accelerators@lists.ozlabs.org +Description: Attributes of the device, see UACCE_DEV_xxx flag defined in uacce.h + +What: /sys/class/uacce/<dev_name>/available_instances +Date: Dec 2019 +KernelVersion: 5.6 +Contact: linux-accelerators@lists.ozlabs.org +Description: Available instances left of the device + Return -ENODEV if uacce_ops get_available_instances is not provided + +What: /sys/class/uacce/<dev_name>/algorithms +Date: Dec 2019 +KernelVersion: 5.6 +Contact: linux-accelerators@lists.ozlabs.org +Description: Algorithms supported by this accelerator, separated by new line. + +What: /sys/class/uacce/<dev_name>/region_mmio_size +Date: Dec 2019 +KernelVersion: 5.6 +Contact: linux-accelerators@lists.ozlabs.org +Description: Size (bytes) of mmio region queue file + +What: /sys/class/uacce/<dev_name>/region_dus_size +Date: Dec 2019 +KernelVersion: 5.6 +Contact: linux-accelerators@lists.ozlabs.org +Description: Size (bytes) of dus region queue file diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7f0d48f..99e1514 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -480,4 +480,5 @@ source "drivers/misc/cxl/Kconfig" source "drivers/misc/ocxl/Kconfig" source "drivers/misc/cardreader/Kconfig" source "drivers/misc/habanalabs/Kconfig" +source "drivers/misc/uacce/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c1860d3..9abf292 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -56,4 +56,5 @@ obj-$(CONFIG_OCXL) += ocxl/ obj-y += cardreader/ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HABANA_AI) += habanalabs/ +obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o diff --git a/drivers/misc/uacce/Kconfig b/drivers/misc/uacce/Kconfig new file mode 100644 index 0000000..5e39b60 --- /dev/null +++ b/drivers/misc/uacce/Kconfig @@ -0,0 +1,13 @@ +config UACCE + tristate "Accelerator Framework for User Land" + depends on IOMMU_API + help + UACCE provides interface for the user process to access the hardware + without interaction with the kernel space in data path. + + The user-space interface is described in + include/uapi/misc/uacce/uacce.h + + See Documentation/misc-devices/uacce.rst for more details. + + If you don't know what to do here, say N. diff --git a/drivers/misc/uacce/Makefile b/drivers/misc/uacce/Makefile new file mode 100644 index 0000000..5b4374e --- /dev/null +++ b/drivers/misc/uacce/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +obj-$(CONFIG_UACCE) += uacce.o diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c new file mode 100644 index 0000000..d7452b1 --- /dev/null +++ b/drivers/misc/uacce/uacce.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <linux/compat.h> +#include <linux/dma-iommu.h> +#include <linux/module.h> +#include <linux/poll.h> +#include <linux/uacce.h> + +static struct class *uacce_class; +static dev_t uacce_devt; +static DEFINE_MUTEX(uacce_mutex); +static DEFINE_XARRAY_ALLOC(uacce_xa); + +static int uacce_start_queue(struct uacce_queue *q) +{ + int ret = 0; + + mutex_lock(&uacce_mutex); + + if (q->state != UACCE_Q_INIT) { + ret = -EINVAL; + goto out_with_lock; + } + + if (q->uacce->ops->start_queue) { + ret = q->uacce->ops->start_queue(q); + if (ret < 0) + goto out_with_lock; + } + + q->state = UACCE_Q_STARTED; + +out_with_lock: + mutex_unlock(&uacce_mutex); + + return ret; +} + +static int uacce_put_queue(struct uacce_queue *q) +{ + struct uacce_device *uacce = q->uacce; + + mutex_lock(&uacce_mutex); + + if (q->state == UACCE_Q_ZOMBIE) + goto out; + + if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue) + uacce->ops->stop_queue(q); + + if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) && + uacce->ops->put_queue) + uacce->ops->put_queue(q); + + q->state = UACCE_Q_ZOMBIE; +out: + mutex_unlock(&uacce_mutex); + + return 0; +} + +static long uacce_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct uacce_queue *q = filep->private_data; + struct uacce_device *uacce = q->uacce; + + switch (cmd) { + case UACCE_CMD_START_Q: + return uacce_start_queue(q); + + case UACCE_CMD_PUT_Q: + return uacce_put_queue(q); + + default: + if (!uacce->ops->ioctl) + return -EINVAL; + + return uacce->ops->ioctl(q, cmd, arg); + } +} + +#ifdef CONFIG_COMPAT +static long uacce_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + + return uacce_fops_unl_ioctl(filep, cmd, arg); +} +#endif + +static int uacce_sva_exit(struct device *dev, struct iommu_sva *handle, + void *data) +{ + struct uacce_mm *uacce_mm = data; + struct uacce_queue *q; + + /* + * No new queue can be added concurrently because no caller can have a + * reference to this mm. But there may be concurrent calls to + * uacce_mm_put(), so we need the lock. + */ + mutex_lock(&uacce_mm->lock); + list_for_each_entry(q, &uacce_mm->queues, list) + uacce_put_queue(q); + uacce_mm->mm = NULL; + mutex_unlock(&uacce_mm->lock); + + return 0; +} + +static struct iommu_sva_ops uacce_sva_ops = { + .mm_exit = uacce_sva_exit, +}; + +static struct uacce_mm *uacce_mm_get(struct uacce_device *uacce, + struct uacce_queue *q, + struct mm_struct *mm) +{ + struct uacce_mm *uacce_mm = NULL; + struct iommu_sva *handle = NULL; + int ret; + + lockdep_assert_held(&uacce->mm_lock); + + list_for_each_entry(uacce_mm, &uacce->mm_list, list) { + if (uacce_mm->mm == mm) { + mutex_lock(&uacce_mm->lock); + list_add(&q->list, &uacce_mm->queues); + mutex_unlock(&uacce_mm->lock); + return uacce_mm; + } + } + + uacce_mm = kzalloc(sizeof(*uacce_mm), GFP_KERNEL); + if (!uacce_mm) + return NULL; + + if (uacce->flags & UACCE_DEV_SVA) { + /* + * Safe to pass an incomplete uacce_mm, since mm_exit cannot + * fire while we hold a reference to the mm. + */ + handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); + if (IS_ERR(handle)) + goto err_free; + + ret = iommu_sva_set_ops(handle, &uacce_sva_ops); + if (ret) + goto err_unbind; + + uacce_mm->pasid = iommu_sva_get_pasid(handle); + if (uacce_mm->pasid == IOMMU_PASID_INVALID) + goto err_unbind; + } + + uacce_mm->mm = mm; + uacce_mm->handle = handle; + INIT_LIST_HEAD(&uacce_mm->queues); + mutex_init(&uacce_mm->lock); + list_add(&q->list, &uacce_mm->queues); + list_add(&uacce_mm->list, &uacce->mm_list); + + return uacce_mm; + +err_unbind: + if (handle) + iommu_sva_unbind_device(handle); +err_free: + kfree(uacce_mm); + return NULL; +} + +static void uacce_mm_put(struct uacce_queue *q) +{ + struct uacce_mm *uacce_mm = q->uacce_mm; + + lockdep_assert_held(&q->uacce->mm_lock); + + mutex_lock(&uacce_mm->lock); + list_del(&q->list); + mutex_unlock(&uacce_mm->lock); + + if (list_empty(&uacce_mm->queues)) { + if (uacce_mm->handle) + iommu_sva_unbind_device(uacce_mm->handle); + list_del(&uacce_mm->list); + kfree(uacce_mm); + } +} + +static int uacce_fops_open(struct inode *inode, struct file *filep) +{ + struct uacce_mm *uacce_mm = NULL; + struct uacce_device *uacce; + struct uacce_queue *q; + int ret = 0; + + uacce = xa_load(&uacce_xa, iminor(inode)); + if (!uacce) + return -ENODEV; + + if (!try_module_get(uacce->parent->driver->owner)) + return -ENODEV; + + q = kzalloc(sizeof(struct uacce_queue), GFP_KERNEL); + if (!q) { + ret = -ENOMEM; + goto out_with_module; + } + + mutex_lock(&uacce->mm_lock); + uacce_mm = uacce_mm_get(uacce, q, current->mm); + mutex_unlock(&uacce->mm_lock); + if (!uacce_mm) { + ret = -ENOMEM; + goto out_with_mem; + } + + q->uacce = uacce; + q->uacce_mm = uacce_mm; + + if (uacce->ops->get_queue) { + ret = uacce->ops->get_queue(uacce, uacce_mm->pasid, q); + if (ret < 0) + goto out_with_mm; + } + + init_waitqueue_head(&q->wait); + filep->private_data = q; + q->state = UACCE_Q_INIT; + + return 0; + +out_with_mm: + mutex_lock(&uacce->mm_lock); + uacce_mm_put(q); + mutex_unlock(&uacce->mm_lock); +out_with_mem: + kfree(q); +out_with_module: + module_put(uacce->parent->driver->owner); + return ret; +} + +static int uacce_fops_release(struct inode *inode, struct file *filep) +{ + struct uacce_queue *q = filep->private_data; + struct uacce_device *uacce = q->uacce; + + uacce_put_queue(q); + + mutex_lock(&uacce->mm_lock); + uacce_mm_put(q); + mutex_unlock(&uacce->mm_lock); + + kfree(q); + module_put(uacce->parent->driver->owner); + + return 0; +} + +static void uacce_vma_close(struct vm_area_struct *vma) +{ + struct uacce_queue *q = vma->vm_private_data; + struct uacce_qfile_region *qfr = NULL; + + if (vma->vm_pgoff < UACCE_MAX_REGION) + qfr = q->qfrs[vma->vm_pgoff]; + + kfree(qfr); +} + +static const struct vm_operations_struct uacce_vm_ops = { + .close = uacce_vma_close, +}; + +static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct uacce_queue *q = filep->private_data; + struct uacce_device *uacce = q->uacce; + struct uacce_qfile_region *qfr; + enum uacce_qfrt type = UACCE_MAX_REGION; + int ret = 0; + + if (vma->vm_pgoff < UACCE_MAX_REGION) + type = vma->vm_pgoff; + else + return -EINVAL; + + qfr = kzalloc(sizeof(*qfr), GFP_KERNEL); + if (!qfr) + return -ENOMEM; + + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; + vma->vm_ops = &uacce_vm_ops; + vma->vm_private_data = q; + qfr->type = type; + + mutex_lock(&uacce_mutex); + + if (q->state != UACCE_Q_INIT && q->state != UACCE_Q_STARTED) { + ret = -EINVAL; + goto out_with_lock; + } + + if (q->qfrs[type]) { + ret = -EEXIST; + goto out_with_lock; + } + + switch (type) { + case UACCE_QFRT_MMIO: + if (!uacce->ops->mmap) { + ret = -EINVAL; + goto out_with_lock; + } + + ret = uacce->ops->mmap(q, vma, qfr); + if (ret) + goto out_with_lock; + + break; + + case UACCE_QFRT_DUS: + if (uacce->flags & UACCE_DEV_SVA) { + if (!uacce->ops->mmap) { + ret = -EINVAL; + goto out_with_lock; + } + + ret = uacce->ops->mmap(q, vma, qfr); + if (ret) + goto out_with_lock; + } + break; + + default: + ret = -EINVAL; + goto out_with_lock; + } + + q->qfrs[type] = qfr; + mutex_unlock(&uacce_mutex); + + return ret; + +out_with_lock: + mutex_unlock(&uacce_mutex); + kfree(qfr); + return ret; +} + +static __poll_t uacce_fops_poll(struct file *file, poll_table *wait) +{ + struct uacce_queue *q = file->private_data; + struct uacce_device *uacce = q->uacce; + + poll_wait(file, &q->wait, wait); + if (uacce->ops->is_q_updated && uacce->ops->is_q_updated(q)) + return EPOLLIN | EPOLLRDNORM; + + return 0; +} + +static const struct file_operations uacce_fops = { + .owner = THIS_MODULE, + .open = uacce_fops_open, + .release = uacce_fops_release, + .unlocked_ioctl = uacce_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = uacce_fops_compat_ioctl, +#endif + .mmap = uacce_fops_mmap, + .poll = uacce_fops_poll, +}; + +#define to_uacce_device(dev) container_of(dev, struct uacce_device, dev) + +static ssize_t api_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%s\n", uacce->api_ver); +} + +static ssize_t flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%u\n", uacce->flags); +} + +static ssize_t available_instances_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + int val = -ENODEV; + + if (uacce->ops->get_available_instances) + val = uacce->ops->get_available_instances(uacce); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t algorithms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%s\n", uacce->algs); +} + +static ssize_t region_mmio_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%lu\n", + uacce->qf_pg_num[UACCE_QFRT_MMIO] << PAGE_SHIFT); +} + +static ssize_t region_dus_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%lu\n", + uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT); +} + +static DEVICE_ATTR_RO(api); +static DEVICE_ATTR_RO(flags); +static DEVICE_ATTR_RO(available_instances); +static DEVICE_ATTR_RO(algorithms); +static DEVICE_ATTR_RO(region_mmio_size); +static DEVICE_ATTR_RO(region_dus_size); + +static struct attribute *uacce_dev_attrs[] = { + &dev_attr_api.attr, + &dev_attr_flags.attr, + &dev_attr_available_instances.attr, + &dev_attr_algorithms.attr, + &dev_attr_region_mmio_size.attr, + &dev_attr_region_dus_size.attr, + NULL, +}; + +static umode_t uacce_dev_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct uacce_device *uacce = to_uacce_device(dev); + + if (((attr == &dev_attr_region_mmio_size.attr) && + (!uacce->qf_pg_num[UACCE_QFRT_MMIO])) || + ((attr == &dev_attr_region_dus_size.attr) && + (!uacce->qf_pg_num[UACCE_QFRT_DUS]))) + return 0; + + return attr->mode; +} + +static struct attribute_group uacce_dev_group = { + .is_visible = uacce_dev_is_visible, + .attrs = uacce_dev_attrs, +}; + +__ATTRIBUTE_GROUPS(uacce_dev); + +static void uacce_release(struct device *dev) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + kfree(uacce); + uacce = NULL; +} + +/** + * uacce_alloc() - alloc an accelerator + * @parent: pointer of uacce parent device + * @interface: pointer of uacce_interface for register + * + * Returns uacce pointer if success and ERR_PTR if not + * Need check returned negotiated uacce->flags + */ +struct uacce_device *uacce_alloc(struct device *parent, + struct uacce_interface *interface) +{ + unsigned int flags = interface->flags; + struct uacce_device *uacce; + int ret; + + uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL); + if (!uacce) + return ERR_PTR(-ENOMEM); + + if (flags & UACCE_DEV_SVA) { + ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); + if (ret) + flags &= ~UACCE_DEV_SVA; + } + + uacce->parent = parent; + uacce->flags = flags; + uacce->ops = interface->ops; + + ret = xa_alloc(&uacce_xa, &uacce->dev_id, uacce, xa_limit_32b, + GFP_KERNEL); + if (ret < 0) + goto err_with_uacce; + + INIT_LIST_HEAD(&uacce->mm_list); + mutex_init(&uacce->mm_lock); + device_initialize(&uacce->dev); + uacce->dev.devt = MKDEV(MAJOR(uacce_devt), uacce->dev_id); + uacce->dev.class = uacce_class; + uacce->dev.groups = uacce_dev_groups; + uacce->dev.parent = uacce->parent; + uacce->dev.release = uacce_release; + dev_set_name(&uacce->dev, "%s-%d", interface->name, uacce->dev_id); + + return uacce; + +err_with_uacce: + if (flags & UACCE_DEV_SVA) + iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); + kfree(uacce); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(uacce_alloc); + +/** + * uacce_register() - add the accelerator to cdev and export to user space + * @uacce: The initialized uacce device + * + * Return 0 if register succeeded, or an error. + */ +int uacce_register(struct uacce_device *uacce) +{ + if (!uacce) + return -ENODEV; + + uacce->cdev = cdev_alloc(); + if (!uacce->cdev) + return -ENOMEM; + + uacce->cdev->ops = &uacce_fops; + uacce->cdev->owner = THIS_MODULE; + + return cdev_device_add(uacce->cdev, &uacce->dev); +} +EXPORT_SYMBOL_GPL(uacce_register); + +/** + * uacce_remove() - remove the accelerator + * @uacce: the accelerator to remove + */ +void uacce_remove(struct uacce_device *uacce) +{ + struct uacce_mm *uacce_mm; + struct uacce_queue *q; + + if (!uacce) + return; + + /* ensure no open queue remains */ + mutex_lock(&uacce->mm_lock); + list_for_each_entry(uacce_mm, &uacce->mm_list, list) { + /* + * We don't take the uacce_mm->lock here. Since we hold the + * device's mm_lock, no queue can be added to or removed from + * this uacce_mm. We may run concurrently with mm_exit, but + * uacce_put_queue() is serialized and iommu_sva_unbind_device() + * waits for the lock that mm_exit is holding. + */ + list_for_each_entry(q, &uacce_mm->queues, list) + uacce_put_queue(q); + + if (uacce->flags & UACCE_DEV_SVA) { + iommu_sva_unbind_device(uacce_mm->handle); + uacce_mm->handle = NULL; + } + } + mutex_unlock(&uacce->mm_lock); + + /* disable sva now since no opened queues */ + if (uacce->flags & UACCE_DEV_SVA) + iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); + + if (uacce->cdev) + cdev_device_del(uacce->cdev, &uacce->dev); + xa_erase(&uacce_xa, uacce->dev_id); + put_device(&uacce->dev); +} +EXPORT_SYMBOL_GPL(uacce_remove); + +static int __init uacce_init(void) +{ + int ret; + + uacce_class = class_create(THIS_MODULE, UACCE_NAME); + if (IS_ERR(uacce_class)) + return PTR_ERR(uacce_class); + + ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME); + if (ret) + class_destroy(uacce_class); + + return ret; +} + +static __exit void uacce_exit(void) +{ + unregister_chrdev_region(uacce_devt, MINORMASK); + class_destroy(uacce_class); +} + +subsys_initcall(uacce_init); +module_exit(uacce_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hisilicon Tech. Co., Ltd."); +MODULE_DESCRIPTION("Accelerator interface for Userland applications"); diff --git a/include/linux/uacce.h b/include/linux/uacce.h new file mode 100644 index 0000000..904a461 --- /dev/null +++ b/include/linux/uacce.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _LINUX_UACCE_H +#define _LINUX_UACCE_H + +#include <linux/cdev.h> +#include <uapi/misc/uacce/uacce.h> + +#define UACCE_NAME "uacce" +#define UACCE_MAX_REGION 2 +#define UACCE_MAX_NAME_SIZE 64 + +struct uacce_queue; +struct uacce_device; + +/** + * struct uacce_qfile_region - structure of queue file region + * @type: type of the region + */ +struct uacce_qfile_region { + enum uacce_qfrt type; +}; + +/** + * struct uacce_ops - uacce device operations + * @get_available_instances: get available instances left of the device + * @get_queue: get a queue from the device + * @put_queue: free a queue to the device + * @start_queue: make the queue start work after get_queue + * @stop_queue: make the queue stop work before put_queue + * @is_q_updated: check whether the task is finished + * @mmap: mmap addresses of queue to user space + * @ioctl: ioctl for user space users of the queue + */ +struct uacce_ops { + int (*get_available_instances)(struct uacce_device *uacce); + int (*get_queue)(struct uacce_device *uacce, unsigned long arg, + 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); + int (*is_q_updated)(struct uacce_queue *q); + int (*mmap)(struct uacce_queue *q, struct vm_area_struct *vma, + struct uacce_qfile_region *qfr); + long (*ioctl)(struct uacce_queue *q, unsigned int cmd, + unsigned long arg); +}; + +/** + * struct uacce_interface - interface required for uacce_register() + * @name: the uacce device name. Will show up in sysfs + * @flags: uacce device attributes + * @ops: pointer to the struct uacce_ops + */ +struct uacce_interface { + char name[UACCE_MAX_NAME_SIZE]; + unsigned int flags; + const struct uacce_ops *ops; +}; + +enum uacce_q_state { + UACCE_Q_ZOMBIE = 0, + UACCE_Q_INIT, + UACCE_Q_STARTED, +}; + +/** + * struct uacce_queue + * @uacce: pointer to uacce + * @priv: private pointer + * @wait: wait queue head + * @list: index into uacce_mm + * @uacce_mm: the corresponding mm + * @qfrs: pointer of qfr regions + * @state: queue state machine + */ +struct uacce_queue { + struct uacce_device *uacce; + void *priv; + wait_queue_head_t wait; + struct list_head list; + struct uacce_mm *uacce_mm; + struct uacce_qfile_region *qfrs[UACCE_MAX_REGION]; + enum uacce_q_state state; +}; + +/** + * struct uacce_device + * @algs: supported algorithms + * @api_ver: api version + * @ops: pointer to the struct uacce_ops + * @qf_pg_num: page numbers of the queue file regions + * @parent: pointer to the parent device + * @is_vf: whether virtual function + * @flags: uacce attributes + * @dev_id: id of the uacce device + * @cdev: cdev of the uacce + * @dev: dev of the uacce + * @priv: private pointer of the uacce + * @mm_list: list head of uacce_mm->list + * @mm_lock: lock for mm_list + */ +struct uacce_device { + const char *algs; + const char *api_ver; + const struct uacce_ops *ops; + unsigned long qf_pg_num[UACCE_MAX_REGION]; + struct device *parent; + bool is_vf; + u32 flags; + u32 dev_id; + struct cdev *cdev; + struct device dev; + void *priv; + struct list_head mm_list; + struct mutex mm_lock; +}; + +/** + * struct uacce_mm - keep track of queues bound to a process + * @list: index into uacce_device + * @queues: list of queues + * @mm: the mm struct + * @lock: protects the list of queues + * @pasid: pasid of the uacce_mm + * @handle: iommu_sva handle return from iommu_sva_bind_device + */ +struct uacce_mm { + struct list_head list; + struct list_head queues; + struct mm_struct *mm; + struct mutex lock; + int pasid; + struct iommu_sva *handle; +}; + +#if IS_ENABLED(CONFIG_UACCE) + +struct uacce_device *uacce_alloc(struct device *parent, + struct uacce_interface *interface); +int uacce_register(struct uacce_device *uacce); +void uacce_remove(struct uacce_device *uacce); + +#else /* CONFIG_UACCE */ + +static inline +struct uacce_device *uacce_alloc(struct device *parent, + struct uacce_interface *interface) +{ + return ERR_PTR(-ENODEV); +} + +static inline int uacce_register(struct uacce_device *uacce) +{ + return -EINVAL; +} + +static inline void uacce_remove(struct uacce_device *uacce) {} + +#endif /* CONFIG_UACCE */ + +#endif /* _LINUX_UACCE_H */ diff --git a/include/uapi/misc/uacce/uacce.h b/include/uapi/misc/uacce/uacce.h new file mode 100644 index 0000000..cc71856 --- /dev/null +++ b/include/uapi/misc/uacce/uacce.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +#ifndef _UAPIUUACCE_H +#define _UAPIUUACCE_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +/* + * UACCE_CMD_START_Q: Start queue + */ +#define UACCE_CMD_START_Q _IO('W', 0) + +/* + * UACCE_CMD_PUT_Q: + * User actively stop queue and free queue resource immediately + * Optimization method since close fd may delay + */ +#define UACCE_CMD_PUT_Q _IO('W', 1) + +/* + * UACCE Device flags: + * UACCE_DEV_SVA: Shared Virtual Addresses + * Support PASID + * Support device page faults (PCI PRI or SMMU Stall) + */ +#define UACCE_DEV_SVA BIT(0) + +/** + * enum uacce_qfrt: queue file region type + * @UACCE_QFRT_MMIO: device mmio region + * @UACCE_QFRT_DUS: device user share region + */ +enum uacce_qfrt { + UACCE_QFRT_MMIO = 0, + UACCE_QFRT_DUS = 1, +}; + +#endif