Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
1
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
0db5753
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
counter
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
ide
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
altera-stapl
c2port
cardreader
cb710
cxl
echo
eeprom
genwqe
habanalabs
common
Makefile
asid.c
command_buffer.c
command_submission.c
context.c
debugfs.c
device.c
firmware_if.c
habanalabs.h
habanalabs_drv.c
habanalabs_ioctl.c
hw_queue.c
hwmon.c
irq.c
memory.c
mmu.c
pci.c
sysfs.c
gaudi
goya
include
Kconfig
Makefile
ibmasm
lis3lv02d
lkdtm
mei
mic
ocxl
sgi-gru
sgi-xp
ti-st
uacce
vmw_vmci
Kconfig
Makefile
ad525x_dpot-i2c.c
ad525x_dpot-spi.c
ad525x_dpot.c
ad525x_dpot.h
apds9802als.c
apds990x.c
atmel-ssc.c
atmel_tclib.c
bh1770glc.c
cs5535-mfgpt.c
ds1682.c
dummy-irq.c
enclosure.c
fastrpc.c
hisi_hikey_usb.c
hmc6352.c
hpilo.c
hpilo.h
ibmvmc.c
ibmvmc.h
ics932s401.c
isl29003.c
isl29020.c
kgdbts.c
lattice-ecp3-config.c
pch_phub.c
pci_endpoint_test.c
phantom.c
pti.c
pvpanic.c
qcom-coincell.c
sram-exec.c
sram.c
sram.h
tifm_7xx1.c
tifm_core.c
tsl2550.c
vmw_balloon.c
xilinx_sdfec.c
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
uio
usb
vdpa
vfio
vhost
video
virt
virtio
visorbus
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
misc
/
habanalabs
/
common
/
command_buffer.c
Copy path
Blame
Blame
Latest commit
History
History
496 lines (400 loc) · 10.2 KB
Breadcrumbs
linux
/
drivers
/
misc
/
habanalabs
/
common
/
command_buffer.c
Top
File metadata and controls
Code
Blame
496 lines (400 loc) · 10.2 KB
Raw
// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2016-2019 HabanaLabs, Ltd. * All Rights Reserved. */ #include <uapi/misc/habanalabs.h> #include "habanalabs.h" #include <linux/mm.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/genalloc.h> static void cb_fini(struct hl_device *hdev, struct hl_cb *cb) { if (cb->is_internal) gen_pool_free(hdev->internal_cb_pool, cb->kernel_address, cb->size); else hdev->asic_funcs->asic_dma_free_coherent(hdev, cb->size, (void *) (uintptr_t) cb->kernel_address, cb->bus_address); kfree(cb); } static void cb_do_release(struct hl_device *hdev, struct hl_cb *cb) { if (cb->is_pool) { spin_lock(&hdev->cb_pool_lock); list_add(&cb->pool_list, &hdev->cb_pool); spin_unlock(&hdev->cb_pool_lock); } else { cb_fini(hdev, cb); } } static void cb_release(struct kref *ref) { struct hl_device *hdev; struct hl_cb *cb; cb = container_of(ref, struct hl_cb, refcount); hdev = cb->hdev; hl_debugfs_remove_cb(cb); cb_do_release(hdev, cb); } static struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size, int ctx_id, bool internal_cb) { struct hl_cb *cb; u32 cb_offset; void *p; /* * We use of GFP_ATOMIC here because this function can be called from * the latency-sensitive code path for command submission. Due to H/W * limitations in some of the ASICs, the kernel must copy the user CB * that is designated for an external queue and actually enqueue * the kernel's copy. Hence, we must never sleep in this code section * and must use GFP_ATOMIC for all memory allocations. */ if (ctx_id == HL_KERNEL_ASID_ID) cb = kzalloc(sizeof(*cb), GFP_ATOMIC); else cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (!cb) return NULL; if (internal_cb) { p = (void *) gen_pool_alloc(hdev->internal_cb_pool, cb_size); if (!p) { kfree(cb); return NULL; } cb_offset = p - hdev->internal_cb_pool_virt_addr; cb->is_internal = true; cb->bus_address = hdev->internal_cb_va_base + cb_offset; } else if (ctx_id == HL_KERNEL_ASID_ID) { p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, &cb->bus_address, GFP_ATOMIC); } else { p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, &cb->bus_address, GFP_USER | __GFP_ZERO); } if (!p) { dev_err(hdev->dev, "failed to allocate %d of dma memory for CB\n", cb_size); kfree(cb); return NULL; } cb->kernel_address = (u64) (uintptr_t) p; cb->size = cb_size; return cb; } int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, u32 cb_size, u64 *handle, int ctx_id, bool internal_cb) { struct hl_cb *cb; bool alloc_new_cb = true; int rc; /* * Can't use generic function to check this because of special case * where we create a CB as part of the reset process */ if ((hdev->disabled) || ((atomic_read(&hdev->in_reset)) && (ctx_id != HL_KERNEL_ASID_ID))) { dev_warn_ratelimited(hdev->dev, "Device is disabled or in reset. Can't create new CBs\n"); rc = -EBUSY; goto out_err; } if (cb_size > SZ_2M) { dev_err(hdev->dev, "CB size %d must be less than %d\n", cb_size, SZ_2M); rc = -EINVAL; goto out_err; } if (!internal_cb) { /* Minimum allocation must be PAGE SIZE */ if (cb_size < PAGE_SIZE) cb_size = PAGE_SIZE; if (ctx_id == HL_KERNEL_ASID_ID && cb_size <= hdev->asic_prop.cb_pool_cb_size) { spin_lock(&hdev->cb_pool_lock); if (!list_empty(&hdev->cb_pool)) { cb = list_first_entry(&hdev->cb_pool, typeof(*cb), pool_list); list_del(&cb->pool_list); spin_unlock(&hdev->cb_pool_lock); alloc_new_cb = false; } else { spin_unlock(&hdev->cb_pool_lock); dev_dbg(hdev->dev, "CB pool is empty\n"); } } } if (alloc_new_cb) { cb = hl_cb_alloc(hdev, cb_size, ctx_id, internal_cb); if (!cb) { rc = -ENOMEM; goto out_err; } } cb->hdev = hdev; cb->ctx_id = ctx_id; spin_lock(&mgr->cb_lock); rc = idr_alloc(&mgr->cb_handles, cb, 1, 0, GFP_ATOMIC); spin_unlock(&mgr->cb_lock); if (rc < 0) { dev_err(hdev->dev, "Failed to allocate IDR for a new CB\n"); goto release_cb; } cb->id = (u64) rc; kref_init(&cb->refcount); spin_lock_init(&cb->lock); /* * idr is 32-bit so we can safely OR it with a mask that is above * 32 bit */ *handle = cb->id | HL_MMAP_TYPE_CB; *handle <<= PAGE_SHIFT; hl_debugfs_add_cb(cb); return 0; release_cb: cb_do_release(hdev, cb); out_err: *handle = 0; return rc; } int hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle) { struct hl_cb *cb; u32 handle; int rc = 0; /* * handle was given to user to do mmap, I need to shift it back to * how the idr module gave it to me */ cb_handle >>= PAGE_SHIFT; handle = (u32) cb_handle; spin_lock(&mgr->cb_lock); cb = idr_find(&mgr->cb_handles, handle); if (cb) { idr_remove(&mgr->cb_handles, handle); spin_unlock(&mgr->cb_lock); kref_put(&cb->refcount, cb_release); } else { spin_unlock(&mgr->cb_lock); dev_err(hdev->dev, "CB destroy failed, no match to handle 0x%x\n", handle); rc = -EINVAL; } return rc; } int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) { union hl_cb_args *args = data; struct hl_device *hdev = hpriv->hdev; u64 handle = 0; int rc; if (hl_device_disabled_or_in_reset(hdev)) { dev_warn_ratelimited(hdev->dev, "Device is %s. Can't execute CB IOCTL\n", atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); return -EBUSY; } switch (args->in.op) { case HL_CB_OP_CREATE: if (args->in.cb_size > HL_MAX_CB_SIZE) { dev_err(hdev->dev, "User requested CB size %d must be less than %d\n", args->in.cb_size, HL_MAX_CB_SIZE); rc = -EINVAL; } else { rc = hl_cb_create(hdev, &hpriv->cb_mgr, args->in.cb_size, &handle, hpriv->ctx->asid, false); } memset(args, 0, sizeof(*args)); args->out.cb_handle = handle; break; case HL_CB_OP_DESTROY: rc = hl_cb_destroy(hdev, &hpriv->cb_mgr, args->in.cb_handle); break; default: rc = -ENOTTY; break; } return rc; } static void cb_vm_close(struct vm_area_struct *vma) { struct hl_cb *cb = (struct hl_cb *) vma->vm_private_data; long new_mmap_size; new_mmap_size = cb->mmap_size - (vma->vm_end - vma->vm_start); if (new_mmap_size > 0) { cb->mmap_size = new_mmap_size; return; } spin_lock(&cb->lock); cb->mmap = false; spin_unlock(&cb->lock); hl_cb_put(cb); vma->vm_private_data = NULL; } static const struct vm_operations_struct cb_vm_ops = { .close = cb_vm_close }; int hl_cb_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) { struct hl_device *hdev = hpriv->hdev; struct hl_cb *cb; u32 handle, user_cb_size; int rc; /* We use the page offset to hold the idr and thus we need to clear * it before doing the mmap itself */ handle = vma->vm_pgoff; vma->vm_pgoff = 0; /* reference was taken here */ cb = hl_cb_get(hdev, &hpriv->cb_mgr, handle); if (!cb) { dev_err(hdev->dev, "CB mmap failed, no match to handle 0x%x\n", handle); return -EINVAL; } /* Validation check */ user_cb_size = vma->vm_end - vma->vm_start; if (user_cb_size != ALIGN(cb->size, PAGE_SIZE)) { dev_err(hdev->dev, "CB mmap failed, mmap size 0x%lx != 0x%x cb size\n", vma->vm_end - vma->vm_start, cb->size); rc = -EINVAL; goto put_cb; } if (!access_ok((void __user *) (uintptr_t) vma->vm_start, user_cb_size)) { dev_err(hdev->dev, "user pointer is invalid - 0x%lx\n", vma->vm_start); rc = -EINVAL; goto put_cb; } spin_lock(&cb->lock); if (cb->mmap) { dev_err(hdev->dev, "CB mmap failed, CB already mmaped to user\n"); rc = -EINVAL; goto release_lock; } cb->mmap = true; spin_unlock(&cb->lock); vma->vm_ops = &cb_vm_ops; /* * Note: We're transferring the cb reference to * vma->vm_private_data here. */ vma->vm_private_data = cb; rc = hdev->asic_funcs->cb_mmap(hdev, vma, (void *) cb->kernel_address, cb->bus_address, cb->size); if (rc) { spin_lock(&cb->lock); cb->mmap = false; goto release_lock; } cb->mmap_size = cb->size; return 0; release_lock: spin_unlock(&cb->lock); put_cb: hl_cb_put(cb); return rc; } struct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, u32 handle) { struct hl_cb *cb; spin_lock(&mgr->cb_lock); cb = idr_find(&mgr->cb_handles, handle); if (!cb) { spin_unlock(&mgr->cb_lock); dev_warn(hdev->dev, "CB get failed, no match to handle 0x%x\n", handle); return NULL; } kref_get(&cb->refcount); spin_unlock(&mgr->cb_lock); return cb; } void hl_cb_put(struct hl_cb *cb) { kref_put(&cb->refcount, cb_release); } void hl_cb_mgr_init(struct hl_cb_mgr *mgr) { spin_lock_init(&mgr->cb_lock); idr_init(&mgr->cb_handles); } void hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr) { struct hl_cb *cb; struct idr *idp; u32 id; idp = &mgr->cb_handles; idr_for_each_entry(idp, cb, id) { if (kref_put(&cb->refcount, cb_release) != 1) dev_err(hdev->dev, "CB %d for CTX ID %d is still alive\n", id, cb->ctx_id); } idr_destroy(&mgr->cb_handles); } struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size, bool internal_cb) { u64 cb_handle; struct hl_cb *cb; int rc; rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, cb_size, &cb_handle, HL_KERNEL_ASID_ID, internal_cb); if (rc) { dev_err(hdev->dev, "Failed to allocate CB for the kernel driver %d\n", rc); return NULL; } cb_handle >>= PAGE_SHIFT; cb = hl_cb_get(hdev, &hdev->kernel_cb_mgr, (u32) cb_handle); /* hl_cb_get should never fail here so use kernel WARN */ WARN(!cb, "Kernel CB handle invalid 0x%x\n", (u32) cb_handle); if (!cb) goto destroy_cb; return cb; destroy_cb: hl_cb_destroy(hdev, &hdev->kernel_cb_mgr, cb_handle << PAGE_SHIFT); return NULL; } int hl_cb_pool_init(struct hl_device *hdev) { struct hl_cb *cb; int i; INIT_LIST_HEAD(&hdev->cb_pool); spin_lock_init(&hdev->cb_pool_lock); for (i = 0 ; i < hdev->asic_prop.cb_pool_cb_cnt ; i++) { cb = hl_cb_alloc(hdev, hdev->asic_prop.cb_pool_cb_size, HL_KERNEL_ASID_ID, false); if (cb) { cb->is_pool = true; list_add(&cb->pool_list, &hdev->cb_pool); } else { hl_cb_pool_fini(hdev); return -ENOMEM; } } return 0; } int hl_cb_pool_fini(struct hl_device *hdev) { struct hl_cb *cb, *tmp; list_for_each_entry_safe(cb, tmp, &hdev->cb_pool, pool_list) { list_del(&cb->pool_list); cb_fini(hdev, cb); } return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
You can’t perform that action at this time.