Commit 1ce2c851 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'char-misc-5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver fixes from Greg KH:
 "Here are some small char and misc driver fixes for 5.2-rc4 to resolve
  a number of reported issues.

  The most "notable" one here is the kernel headers in proc^Wsysfs
  fixes. Those changes move the header file info into sysfs and fixes
  the build issues that you reported.

  Other than that, a bunch of small habanalabs driver fixes, some fpga
  driver fixes, and a few other tiny driver fixes.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'char-misc-5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc:
  habanalabs: Read upper bits of trace buffer from RWPHI
  habanalabs: Fix virtual address access via debugfs for 2MB pages
  fpga: zynqmp-fpga: Correctly handle error pointer
  habanalabs: fix bug in checking huge page optimization
  habanalabs: Avoid using a non-initialized MMU cache mutex
  habanalabs: fix debugfs code
  uapi/habanalabs: add opcode for enable/disable device debug mode
  habanalabs: halt debug engines on user process close
  test_firmware: Use correct snprintf() limit
  genwqe: Prevent an integer overflow in the ioctl
  parport: Fix mem leak in parport_register_dev_model
  fpga: dfl: expand minor range when registering chrdev region
  fpga: dfl: Add lockdep classes for pdata->lock
  fpga: dfl: afu: Pass the correct device to dma_mapping_error()
  fpga: stratix10-soc: fix use-after-free on s10_init()
  w1: ds2408: Fix typo after 49695ac4 (reset on output_write retry with readback)
  kheaders: Do not regenerate archive if config is not changed
  kheaders: Move from proc to sysfs
  lkdtm/bugs: Adjust recursion test to avoid elision
  lkdtm/usercopy: Moves the KERNEL_DS test to non-canonical
parents 902b2edf e7bf2ce8
...@@ -399,7 +399,7 @@ int afu_dma_map_region(struct dfl_feature_platform_data *pdata, ...@@ -399,7 +399,7 @@ int afu_dma_map_region(struct dfl_feature_platform_data *pdata,
region->pages[0], 0, region->pages[0], 0,
region->length, region->length,
DMA_BIDIRECTIONAL); DMA_BIDIRECTIONAL);
if (dma_mapping_error(&pdata->dev->dev, region->iova)) { if (dma_mapping_error(dfl_fpga_pdata_to_parent(pdata), region->iova)) {
dev_err(&pdata->dev->dev, "failed to map for dma\n"); dev_err(&pdata->dev->dev, "failed to map for dma\n");
ret = -EFAULT; ret = -EFAULT;
goto unpin_pages; goto unpin_pages;
......
...@@ -40,6 +40,13 @@ enum dfl_fpga_devt_type { ...@@ -40,6 +40,13 @@ enum dfl_fpga_devt_type {
DFL_FPGA_DEVT_MAX, DFL_FPGA_DEVT_MAX,
}; };
static struct lock_class_key dfl_pdata_keys[DFL_ID_MAX];
static const char *dfl_pdata_key_strings[DFL_ID_MAX] = {
"dfl-fme-pdata",
"dfl-port-pdata",
};
/** /**
* dfl_dev_info - dfl feature device information. * dfl_dev_info - dfl feature device information.
* @name: name string of the feature platform device. * @name: name string of the feature platform device.
...@@ -315,7 +322,7 @@ static void dfl_chardev_uinit(void) ...@@ -315,7 +322,7 @@ static void dfl_chardev_uinit(void)
for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) for (i = 0; i < DFL_FPGA_DEVT_MAX; i++)
if (MAJOR(dfl_chrdevs[i].devt)) { if (MAJOR(dfl_chrdevs[i].devt)) {
unregister_chrdev_region(dfl_chrdevs[i].devt, unregister_chrdev_region(dfl_chrdevs[i].devt,
MINORMASK); MINORMASK + 1);
dfl_chrdevs[i].devt = MKDEV(0, 0); dfl_chrdevs[i].devt = MKDEV(0, 0);
} }
} }
...@@ -325,8 +332,8 @@ static int dfl_chardev_init(void) ...@@ -325,8 +332,8 @@ static int dfl_chardev_init(void)
int i, ret; int i, ret;
for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) { for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) {
ret = alloc_chrdev_region(&dfl_chrdevs[i].devt, 0, MINORMASK, ret = alloc_chrdev_region(&dfl_chrdevs[i].devt, 0,
dfl_chrdevs[i].name); MINORMASK + 1, dfl_chrdevs[i].name);
if (ret) if (ret)
goto exit; goto exit;
} }
...@@ -443,11 +450,16 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) ...@@ -443,11 +450,16 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
struct platform_device *fdev = binfo->feature_dev; struct platform_device *fdev = binfo->feature_dev;
struct dfl_feature_platform_data *pdata; struct dfl_feature_platform_data *pdata;
struct dfl_feature_info *finfo, *p; struct dfl_feature_info *finfo, *p;
enum dfl_id_type type;
int ret, index = 0; int ret, index = 0;
if (!fdev) if (!fdev)
return 0; return 0;
type = feature_dev_id_type(fdev);
if (WARN_ON_ONCE(type >= DFL_ID_MAX))
return -EINVAL;
/* /*
* we do not need to care for the memory which is associated with * we do not need to care for the memory which is associated with
* the platform device. After calling platform_device_unregister(), * the platform device. After calling platform_device_unregister(),
...@@ -463,6 +475,8 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) ...@@ -463,6 +475,8 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
pdata->num = binfo->feature_num; pdata->num = binfo->feature_num;
pdata->dfl_cdev = binfo->cdev; pdata->dfl_cdev = binfo->cdev;
mutex_init(&pdata->lock); mutex_init(&pdata->lock);
lockdep_set_class_and_name(&pdata->lock, &dfl_pdata_keys[type],
dfl_pdata_key_strings[type]);
/* /*
* the count should be initialized to 0 to make sure * the count should be initialized to 0 to make sure
...@@ -497,7 +511,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) ...@@ -497,7 +511,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
ret = platform_device_add(binfo->feature_dev); ret = platform_device_add(binfo->feature_dev);
if (!ret) { if (!ret) {
if (feature_dev_id_type(binfo->feature_dev) == PORT_ID) if (type == PORT_ID)
dfl_fpga_cdev_add_port_dev(binfo->cdev, dfl_fpga_cdev_add_port_dev(binfo->cdev,
binfo->feature_dev); binfo->feature_dev);
else else
......
...@@ -507,12 +507,16 @@ static int __init s10_init(void) ...@@ -507,12 +507,16 @@ static int __init s10_init(void)
if (!fw_np) if (!fw_np)
return -ENODEV; return -ENODEV;
of_node_get(fw_np);
np = of_find_matching_node(fw_np, s10_of_match); np = of_find_matching_node(fw_np, s10_of_match);
if (!np) if (!np) {
of_node_put(fw_np);
return -ENODEV; return -ENODEV;
}
of_node_put(np); of_node_put(np);
ret = of_platform_populate(fw_np, s10_of_match, NULL, NULL); ret = of_platform_populate(fw_np, s10_of_match, NULL, NULL);
of_node_put(fw_np);
if (ret) if (ret)
return ret; return ret;
......
...@@ -47,7 +47,7 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr, ...@@ -47,7 +47,7 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr,
char *kbuf; char *kbuf;
int ret; int ret;
if (!eemi_ops || !eemi_ops->fpga_load) if (IS_ERR_OR_NULL(eemi_ops) || !eemi_ops->fpga_load)
return -ENXIO; return -ENXIO;
priv = mgr->priv; priv = mgr->priv;
...@@ -81,7 +81,7 @@ static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr) ...@@ -81,7 +81,7 @@ static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr)
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
u32 status; u32 status;
if (!eemi_ops || !eemi_ops->fpga_get_status) if (IS_ERR_OR_NULL(eemi_ops) || !eemi_ops->fpga_get_status)
return FPGA_MGR_STATE_UNKNOWN; return FPGA_MGR_STATE_UNKNOWN;
eemi_ops->fpga_get_status(&status); eemi_ops->fpga_get_status(&status);
......
...@@ -780,6 +780,8 @@ static int genwqe_pin_mem(struct genwqe_file *cfile, struct genwqe_mem *m) ...@@ -780,6 +780,8 @@ static int genwqe_pin_mem(struct genwqe_file *cfile, struct genwqe_mem *m)
if ((m->addr == 0x0) || (m->size == 0)) if ((m->addr == 0x0) || (m->size == 0))
return -EINVAL; return -EINVAL;
if (m->size > ULONG_MAX - PAGE_SIZE - (m->addr & ~PAGE_MASK))
return -EINVAL;
map_addr = (m->addr & PAGE_MASK); map_addr = (m->addr & PAGE_MASK);
map_size = round_up(m->size + (m->addr & ~PAGE_MASK), PAGE_SIZE); map_size = round_up(m->size + (m->addr & ~PAGE_MASK), PAGE_SIZE);
......
...@@ -586,6 +586,10 @@ int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m, void *uaddr, ...@@ -586,6 +586,10 @@ int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m, void *uaddr,
/* determine space needed for page_list. */ /* determine space needed for page_list. */
data = (unsigned long)uaddr; data = (unsigned long)uaddr;
offs = offset_in_page(data); offs = offset_in_page(data);
if (size > ULONG_MAX - PAGE_SIZE - offs) {
m->size = 0; /* mark unused and not added */
return -EINVAL;
}
m->nr_pages = DIV_ROUND_UP(offs + size, PAGE_SIZE); m->nr_pages = DIV_ROUND_UP(offs + size, PAGE_SIZE);
m->page_list = kcalloc(m->nr_pages, m->page_list = kcalloc(m->nr_pages,
......
...@@ -26,6 +26,12 @@ static void hl_ctx_fini(struct hl_ctx *ctx) ...@@ -26,6 +26,12 @@ static void hl_ctx_fini(struct hl_ctx *ctx)
dma_fence_put(ctx->cs_pending[i]); dma_fence_put(ctx->cs_pending[i]);
if (ctx->asid != HL_KERNEL_ASID_ID) { if (ctx->asid != HL_KERNEL_ASID_ID) {
/*
* The engines are stopped as there is no executing CS, but the
* Coresight might be still working by accessing addresses
* related to the stopped engines. Hence stop it explicitly.
*/
hdev->asic_funcs->halt_coresight(hdev);
hl_vm_ctx_fini(ctx); hl_vm_ctx_fini(ctx);
hl_asid_free(hdev, ctx->asid); hl_asid_free(hdev, ctx->asid);
} }
......
...@@ -459,41 +459,31 @@ static ssize_t mmu_write(struct file *file, const char __user *buf, ...@@ -459,41 +459,31 @@ static ssize_t mmu_write(struct file *file, const char __user *buf,
struct hl_debugfs_entry *entry = s->private; struct hl_debugfs_entry *entry = s->private;
struct hl_dbg_device_entry *dev_entry = entry->dev_entry; struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
struct hl_device *hdev = dev_entry->hdev; struct hl_device *hdev = dev_entry->hdev;
char kbuf[MMU_KBUF_SIZE], asid_kbuf[MMU_ASID_BUF_SIZE], char kbuf[MMU_KBUF_SIZE];
addr_kbuf[MMU_ADDR_BUF_SIZE];
char *c; char *c;
ssize_t rc; ssize_t rc;
if (!hdev->mmu_enable) if (!hdev->mmu_enable)
return count; return count;
memset(kbuf, 0, sizeof(kbuf)); if (count > sizeof(kbuf) - 1)
memset(asid_kbuf, 0, sizeof(asid_kbuf)); goto err;
memset(addr_kbuf, 0, sizeof(addr_kbuf));
if (copy_from_user(kbuf, buf, count)) if (copy_from_user(kbuf, buf, count))
goto err; goto err;
kbuf[count] = 0;
kbuf[MMU_KBUF_SIZE - 1] = 0;
c = strchr(kbuf, ' '); c = strchr(kbuf, ' ');
if (!c) if (!c)
goto err; goto err;
*c = '\0';
memcpy(asid_kbuf, kbuf, c - kbuf); rc = kstrtouint(kbuf, 10, &dev_entry->mmu_asid);
rc = kstrtouint(asid_kbuf, 10, &dev_entry->mmu_asid);
if (rc) if (rc)
goto err; goto err;
c = strstr(kbuf, " 0x"); if (strncmp(c+1, "0x", 2))
if (!c)
goto err; goto err;
rc = kstrtoull(c+3, 16, &dev_entry->mmu_addr);
c += 3;
memcpy(addr_kbuf, c, (kbuf + count) - c);
rc = kstrtoull(addr_kbuf, 16, &dev_entry->mmu_addr);
if (rc) if (rc)
goto err; goto err;
...@@ -510,6 +500,7 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, ...@@ -510,6 +500,7 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr,
{ {
struct hl_ctx *ctx = hdev->user_ctx; struct hl_ctx *ctx = hdev->user_ctx;
u64 hop_addr, hop_pte_addr, hop_pte; u64 hop_addr, hop_pte_addr, hop_pte;
u64 offset_mask = HOP4_MASK | OFFSET_MASK;
int rc = 0; int rc = 0;
if (!ctx) { if (!ctx) {
...@@ -552,12 +543,14 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, ...@@ -552,12 +543,14 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr,
goto not_mapped; goto not_mapped;
hop_pte_addr = get_hop4_pte_addr(ctx, hop_addr, virt_addr); hop_pte_addr = get_hop4_pte_addr(ctx, hop_addr, virt_addr);
hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr);
offset_mask = OFFSET_MASK;
} }
if (!(hop_pte & PAGE_PRESENT_MASK)) if (!(hop_pte & PAGE_PRESENT_MASK))
goto not_mapped; goto not_mapped;
*phys_addr = (hop_pte & PTE_PHYS_ADDR_MASK) | (virt_addr & OFFSET_MASK); *phys_addr = (hop_pte & ~offset_mask) | (virt_addr & offset_mask);
goto out; goto out;
...@@ -600,10 +593,8 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf, ...@@ -600,10 +593,8 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf,
} }
sprintf(tmp_buf, "0x%08x\n", val); sprintf(tmp_buf, "0x%08x\n", val);
rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, return simple_read_from_buffer(buf, count, ppos, tmp_buf,
strlen(tmp_buf) + 1); strlen(tmp_buf));
return rc;
} }
static ssize_t hl_data_write32(struct file *f, const char __user *buf, static ssize_t hl_data_write32(struct file *f, const char __user *buf,
...@@ -645,7 +636,6 @@ static ssize_t hl_get_power_state(struct file *f, char __user *buf, ...@@ -645,7 +636,6 @@ static ssize_t hl_get_power_state(struct file *f, char __user *buf,
struct hl_dbg_device_entry *entry = file_inode(f)->i_private; struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
struct hl_device *hdev = entry->hdev; struct hl_device *hdev = entry->hdev;
char tmp_buf[200]; char tmp_buf[200];
ssize_t rc;
int i; int i;
if (*ppos) if (*ppos)
...@@ -660,10 +650,8 @@ static ssize_t hl_get_power_state(struct file *f, char __user *buf, ...@@ -660,10 +650,8 @@ static ssize_t hl_get_power_state(struct file *f, char __user *buf,
sprintf(tmp_buf, sprintf(tmp_buf,
"current power state: %d\n1 - D0\n2 - D3hot\n3 - Unknown\n", i); "current power state: %d\n1 - D0\n2 - D3hot\n3 - Unknown\n", i);
rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, return simple_read_from_buffer(buf, count, ppos, tmp_buf,
strlen(tmp_buf) + 1); strlen(tmp_buf));
return rc;
} }
static ssize_t hl_set_power_state(struct file *f, const char __user *buf, static ssize_t hl_set_power_state(struct file *f, const char __user *buf,
...@@ -716,8 +704,8 @@ static ssize_t hl_i2c_data_read(struct file *f, char __user *buf, ...@@ -716,8 +704,8 @@ static ssize_t hl_i2c_data_read(struct file *f, char __user *buf,
} }
sprintf(tmp_buf, "0x%02x\n", val); sprintf(tmp_buf, "0x%02x\n", val);
rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, rc = simple_read_from_buffer(buf, count, ppos, tmp_buf,
strlen(tmp_buf) + 1); strlen(tmp_buf));
return rc; return rc;
} }
...@@ -806,18 +794,9 @@ static ssize_t hl_led2_write(struct file *f, const char __user *buf, ...@@ -806,18 +794,9 @@ static ssize_t hl_led2_write(struct file *f, const char __user *buf,
static ssize_t hl_device_read(struct file *f, char __user *buf, static ssize_t hl_device_read(struct file *f, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char tmp_buf[200]; static const char *help =
ssize_t rc; "Valid values: disable, enable, suspend, resume, cpu_timeout\n";
return simple_read_from_buffer(buf, count, ppos, help, strlen(help));
if (*ppos)
return 0;
sprintf(tmp_buf,
"Valid values: disable, enable, suspend, resume, cpu_timeout\n");
rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf,
strlen(tmp_buf) + 1);
return rc;
} }
static ssize_t hl_device_write(struct file *f, const char __user *buf, static ssize_t hl_device_write(struct file *f, const char __user *buf,
...@@ -825,7 +804,7 @@ static ssize_t hl_device_write(struct file *f, const char __user *buf, ...@@ -825,7 +804,7 @@ static ssize_t hl_device_write(struct file *f, const char __user *buf,
{ {
struct hl_dbg_device_entry *entry = file_inode(f)->i_private; struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
struct hl_device *hdev = entry->hdev; struct hl_device *hdev = entry->hdev;
char data[30]; char data[30] = {0};
/* don't allow partial writes */ /* don't allow partial writes */
if (*ppos != 0) if (*ppos != 0)
......
...@@ -231,6 +231,7 @@ static int device_early_init(struct hl_device *hdev) ...@@ -231,6 +231,7 @@ static int device_early_init(struct hl_device *hdev)
mutex_init(&hdev->fd_open_cnt_lock); mutex_init(&hdev->fd_open_cnt_lock);
mutex_init(&hdev->send_cpu_message_lock); mutex_init(&hdev->send_cpu_message_lock);
mutex_init(&hdev->mmu_cache_lock);
INIT_LIST_HEAD(&hdev->hw_queues_mirror_list); INIT_LIST_HEAD(&hdev->hw_queues_mirror_list);
spin_lock_init(&hdev->hw_queues_mirror_lock); spin_lock_init(&hdev->hw_queues_mirror_lock);
atomic_set(&hdev->in_reset, 0); atomic_set(&hdev->in_reset, 0);
...@@ -260,6 +261,7 @@ static int device_early_init(struct hl_device *hdev) ...@@ -260,6 +261,7 @@ static int device_early_init(struct hl_device *hdev)
*/ */
static void device_early_fini(struct hl_device *hdev) static void device_early_fini(struct hl_device *hdev)
{ {
mutex_destroy(&hdev->mmu_cache_lock);
mutex_destroy(&hdev->send_cpu_message_lock); mutex_destroy(&hdev->send_cpu_message_lock);
hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr);
......
...@@ -4819,7 +4819,8 @@ static const struct hl_asic_funcs goya_funcs = { ...@@ -4819,7 +4819,8 @@ static const struct hl_asic_funcs goya_funcs = {
.set_dram_bar_base = goya_set_ddr_bar_base, .set_dram_bar_base = goya_set_ddr_bar_base,
.init_iatu = goya_init_iatu, .init_iatu = goya_init_iatu,
.rreg = hl_rreg, .rreg = hl_rreg,
.wreg = hl_wreg .wreg = hl_wreg,
.halt_coresight = goya_halt_coresight
}; };
/* /*
......
...@@ -202,6 +202,7 @@ void goya_add_device_attr(struct hl_device *hdev, ...@@ -202,6 +202,7 @@ void goya_add_device_attr(struct hl_device *hdev,
struct attribute_group *dev_attr_grp); struct attribute_group *dev_attr_grp);
int goya_armcp_info_get(struct hl_device *hdev); int goya_armcp_info_get(struct hl_device *hdev);
int goya_debug_coresight(struct hl_device *hdev, void *data); int goya_debug_coresight(struct hl_device *hdev, void *data);
void goya_halt_coresight(struct hl_device *hdev);
void goya_mmu_prepare(struct hl_device *hdev, u32 asid); void goya_mmu_prepare(struct hl_device *hdev, u32 asid);
int goya_mmu_clear_pgt_range(struct hl_device *hdev); int goya_mmu_clear_pgt_range(struct hl_device *hdev);
......
...@@ -425,8 +425,18 @@ static int goya_config_etr(struct hl_device *hdev, ...@@ -425,8 +425,18 @@ static int goya_config_etr(struct hl_device *hdev,
WREG32(base_reg + 0x28, 0); WREG32(base_reg + 0x28, 0);
WREG32(base_reg + 0x304, 0); WREG32(base_reg + 0x304, 0);
if (params->output_size >= sizeof(u32)) if (params->output_size >= sizeof(u64)) {
*(u32 *) params->output = RREG32(base_reg + 0x18); u32 rwp, rwphi;
/*
* The trace buffer address is 40 bits wide. The end of
* the buffer is set in the RWP register (lower 32
* bits), and in the RWPHI register (upper 8 bits).
*/
rwp = RREG32(base_reg + 0x18);
rwphi = RREG32(base_reg + 0x3c) & 0xff;
*(u64 *) params->output = ((u64) rwphi << 32) | rwp;
}
} }
return 0; return 0;
...@@ -626,3 +636,20 @@ int goya_debug_coresight(struct hl_device *hdev, void *data) ...@@ -626,3 +636,20 @@ int goya_debug_coresight(struct hl_device *hdev, void *data)
return rc; return rc;
} }
void goya_halt_coresight(struct hl_device *hdev)
{
struct hl_debug_params params = {};
int i, rc;
for (i = GOYA_ETF_FIRST ; i <= GOYA_ETF_LAST ; i++) {
params.reg_idx = i;
rc = goya_config_etf(hdev, &params);
if (rc)
dev_err(hdev->dev, "halt ETF failed, %d/%d\n", rc, i);
}
rc = goya_config_etr(hdev, &params);
if (rc)
dev_err(hdev->dev, "halt ETR failed, %d\n", rc);
}
...@@ -501,6 +501,7 @@ enum hl_pll_frequency { ...@@ -501,6 +501,7 @@ enum hl_pll_frequency {
* @init_iatu: Initialize the iATU unit inside the PCI controller. * @init_iatu: Initialize the iATU unit inside the PCI controller.
* @rreg: Read a register. Needed for simulator support. * @rreg: Read a register. Needed for simulator support.
* @wreg: Write a register. Needed for simulator support. * @wreg: Write a register. Needed for simulator support.
* @halt_coresight: stop the ETF and ETR traces.
*/ */
struct hl_asic_funcs { struct hl_asic_funcs {
int (*early_init)(struct hl_device *hdev); int (*early_init)(struct hl_device *hdev);
...@@ -578,6 +579,7 @@ struct hl_asic_funcs { ...@@ -578,6 +579,7 @@ struct hl_asic_funcs {
int (*init_iatu)(struct hl_device *hdev); int (*init_iatu)(struct hl_device *hdev);
u32 (*rreg)(struct hl_device *hdev, u32 reg); u32 (*rreg)(struct hl_device *hdev, u32 reg);
void (*wreg)(struct hl_device *hdev, u32 reg, u32 val); void (*wreg)(struct hl_device *hdev, u32 reg, u32 val);
void (*halt_coresight)(struct hl_device *hdev);
}; };
......
...@@ -675,11 +675,6 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx, ...@@ -675,11 +675,6 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
total_npages += npages; total_npages += npages;
if (first) {
first = false;
dma_addr &= PAGE_MASK_2MB;
}
if ((npages % PGS_IN_2MB_PAGE) || if ((npages % PGS_IN_2MB_PAGE) ||
(dma_addr & (PAGE_SIZE_2MB - 1))) (dma_addr & (PAGE_SIZE_2MB - 1)))
is_huge_page_opt = false; is_huge_page_opt = false;
...@@ -704,7 +699,6 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx, ...@@ -704,7 +699,6 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
phys_pg_pack->total_size = total_npages * page_size; phys_pg_pack->total_size = total_npages * page_size;
j = 0; j = 0;
first = true;
for_each_sg(userptr->sgt->sgl, sg, userptr->sgt->nents, i) { for_each_sg(userptr->sgt->sgl, sg, userptr->sgt->nents, i) {
npages = get_sg_info(sg, &dma_addr); npages = get_sg_info(sg, &dma_addr);
......
...@@ -404,15 +404,12 @@ int hl_mmu_init(struct hl_device *hdev) ...@@ -404,15 +404,12 @@ int hl_mmu_init(struct hl_device *hdev)
/* MMU H/W init was already done in device hw_init() */ /* MMU H/W init was already done in device hw_init() */
mutex_init(&hdev->mmu_cache_lock);
hdev->mmu_pgt_pool = hdev->mmu_pgt_pool =
gen_pool_create(__ffs(prop->mmu_hop_table_size), -1); gen_pool_create(__ffs(prop->mmu_hop_table_size), -1);
if (!hdev->mmu_pgt_pool) { if (!hdev->mmu_pgt_pool) {
dev_err(hdev->dev, "Failed to create page gen pool\n"); dev_err(hdev->dev, "Failed to create page gen pool\n");
rc = -ENOMEM; return -ENOMEM;
goto err_pool_create;
} }
rc = gen_pool_add(hdev->mmu_pgt_pool, prop->mmu_pgt_addr + rc = gen_pool_add(hdev->mmu_pgt_pool, prop->mmu_pgt_addr +
...@@ -436,8 +433,6 @@ int hl_mmu_init(struct hl_device *hdev) ...@@ -436,8 +433,6 @@ int hl_mmu_init(struct hl_device *hdev)
err_pool_add: err_pool_add:
gen_pool_destroy(hdev->mmu_pgt_pool); gen_pool_destroy(hdev->mmu_pgt_pool);
err_pool_create:
mutex_destroy(&hdev->mmu_cache_lock);
return rc; return rc;
} }
...@@ -459,7 +454,6 @@ void hl_mmu_fini(struct hl_device *hdev) ...@@ -459,7 +454,6 @@ void hl_mmu_fini(struct hl_device *hdev)
kvfree(hdev->mmu_shadow_hop0); kvfree(hdev->mmu_shadow_hop0);
gen_pool_destroy(hdev->mmu_pgt_pool); gen_pool_destroy(hdev->mmu_pgt_pool);
mutex_destroy(&hdev->mmu_cache_lock);
/* MMU H/W fini will be done in device hw_fini() */ /* MMU H/W fini will be done in device hw_fini() */
} }
......
...@@ -32,12 +32,20 @@ static int recur_count = REC_NUM_DEFAULT; ...@@ -32,12 +32,20 @@ static int recur_count = REC_NUM_DEFAULT;
static DEFINE_SPINLOCK(lock_me_up); static DEFINE_SPINLOCK(lock_me_up);
static int recursive_loop(int remaining) /*
* Make sure compiler does not optimize this function or stack frame away:
* - function marked noinline
* - stack variables are marked volatile
* - stack variables are written (memset()) and read (pr_info())
* - function has external effects (pr_info())
* */
static int noinline recursive_loop(int remaining)
{ {
char buf[REC_STACK_SIZE]; volatile char buf[REC_STACK_SIZE];
/* Make sure compiler does not optimize this away. */ memset((void *)buf, remaining & 0xFF, sizeof(buf));
memset(buf, (remaining & 0xff) | 0x1, REC_STACK_SIZE); pr_info("loop %d/%d ...\n", (int)buf[remaining % sizeof(buf)],
recur_count);
if (!remaining) if (!remaining)
return 0; return 0;
else else
...@@ -81,9 +89,12 @@ void lkdtm_LOOP(void) ...@@ -81,9 +89,12 @@ void lkdtm_LOOP(void)
; ;
} }
void lkdtm_OVERFLOW(void) void lkdtm_EXHAUST_STACK(void)
{ {
(void) recursive_loop(recur_count); pr_info("Calling function with %d frame size to depth %d ...\n",
REC_STACK_SIZE, recur_count);
recursive_loop(recur_count);
pr_info("FAIL: survived without exhausting stack?!\n");
} }
static noinline void __lkdtm_CORRUPT_STACK(void *stack) static noinline void __lkdtm_CORRUPT_STACK(void *stack)
......
...@@ -106,12 +106,12 @@ static const struct crashtype crashtypes[] = { ...@@ -106,12 +106,12 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(WARNING), CRASHTYPE(WARNING),
CRASHTYPE(EXCEPTION), CRASHTYPE(EXCEPTION),
CRASHTYPE(LOOP), CRASHTYPE(LOOP),
CRASHTYPE(OVERFLOW), CRASHTYPE(EXHAUST_STACK),
CRASHTYPE(CORRUPT_STACK),
CRASHTYPE(CORRUPT_STACK_STRONG),
CRASHTYPE(CORRUPT_LIST_ADD), CRASHTYPE(CORRUPT_LIST_ADD),
CRASHTYPE(CORRUPT_LIST_DEL), CRASHTYPE(CORRUPT_LIST_DEL),
CRASHTYPE(CORRUPT_USER_DS), CRASHTYPE(CORRUPT_USER_DS),
CRASHTYPE(CORRUPT_STACK),
CRASHTYPE(CORRUPT_STACK_STRONG),
CRASHTYPE(STACK_GUARD_PAGE_LEADING), CRASHTYPE(STACK_GUARD_PAGE_LEADING),
CRASHTYPE(STACK_GUARD_PAGE_TRAILING), CRASHTYPE(STACK_GUARD_PAGE_TRAILING),
CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE),
......
...@@ -13,7 +13,7 @@ void lkdtm_BUG(void); ...@@ -13,7 +13,7 @@ void lkdtm_BUG(void);
void lkdtm_WARNING(void); void lkdtm_WARNING(void);
void lkdtm_EXCEPTION(void); void lkdtm_EXCEPTION(void);
void lkdtm_LOOP(void); void lkdtm_LOOP(void);
void lkdtm_OVERFLOW(void); void lkdtm_EXHAUST_STACK(void);
void lkdtm_CORRUPT_STACK(void); void lkdtm_CORRUPT_STACK(void);
void lkdtm_CORRUPT_STACK_STRONG(void); void lkdtm_CORRUPT_STACK_STRONG(void);
void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void); void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void);
......
...@@ -324,14 +324,16 @@ void lkdtm_USERCOPY_KERNEL(void) ...@@ -324,14 +324,16 @@ void lkdtm_USERCOPY_KERNEL(void)
void lkdtm_USERCOPY_KERNEL_DS(void) void lkdtm_USERCOPY_KERNEL_DS(void)
{ {
char __user *user_ptr = (char __user *)ERR_PTR(-EINVAL); char __user *user_ptr =
(char __user *)(0xFUL << (sizeof(unsigned long) * 8 - 4));
mm_segment_t old_fs = get_fs(); mm_segment_t old_fs = get_fs();
char buf[10] = {0}; char buf[10] = {0};
pr_info("attempting copy_to_user on unmapped kernel address\n"); pr_info("attempting copy_to_user() to noncanonical address: %px\n",
user_ptr);
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
if (copy_to_user(user_ptr, buf, sizeof(buf))) if (copy_to_user(user_ptr, buf, sizeof(buf)) == 0)
pr_info("copy_to_user un unmapped kernel address failed\n"); pr_err("copy_to_user() to noncanonical address succeeded!?\n");
set_fs(old_fs); set_fs(old_fs);
} }
......
...@@ -895,6 +895,7 @@ parport_register_dev_model(struct parport *port, const char *name, ...@@ -895,6 +895,7 @@ parport_register_dev_model(struct parport *port, const char *name,
par_dev->devmodel = true; par_dev->devmodel = true;
ret = device_register(&par_dev->dev); ret = device_register(&par_dev->dev);
if (ret) { if (ret) {
kfree(par_dev->state);
put_device(&par_dev->dev); put_device(&par_dev->dev);
goto err_put_port; goto err_put_port;
} }
...@@ -912,6 +913,7 @@ parport_register_dev_model(struct parport *port, const char *name, ...@@ -912,6 +913,7 @@ parport_register_dev_model(struct parport *port, const char *name,
spin_unlock(&port->physport->pardevice_lock); spin_unlock(&port->physport->pardevice_lock);
pr_debug("%s: cannot grant exclusive access for device %s\n", pr_debug("%s: cannot grant exclusive access for device %s\n",
port->name, name); port->name, name);
kfree(par_dev->state);
device_unregister(&par_dev->dev); device_unregister(&par_dev->dev);
goto err_put_port; goto err_put_port;
} }
......
...@@ -138,7 +138,7 @@ static ssize_t status_control_read(struct file *filp, struct kobject *kobj, ...@@ -138,7 +138,7 @@ static ssize_t status_control_read(struct file *filp, struct kobject *kobj,
W1_F29_REG_CONTROL_AND_STATUS, buf); W1_F29_REG_CONTROL_AND_STATUS, buf);
} }
#ifdef fCONFIG_W1_SLAVE_DS2408_READBACK #ifdef CONFIG_W1_SLAVE_DS2408_READBACK
static bool optional_read_back_valid(struct w1_slave *sl, u8 expected) static bool optional_read_back_valid(struct w1_slave *sl, u8 expected)
{ {
u8 w1_buf[3]; u8 w1_buf[3];
......
...@@ -413,6 +413,10 @@ struct hl_debug_params_spmu { ...@@ -413,6 +413,10 @@ struct hl_debug_params_spmu {
#define HL_DEBUG_OP_SPMU 5 #define HL_DEBUG_OP_SPMU 5
/* Opcode for timestamp */ /* Opcode for timestamp */
#define HL_DEBUG_OP_TIMESTAMP 6 #define HL_DEBUG_OP_TIMESTAMP 6
/* Opcode for setting the device into or out of debug mode. The enable
* variable should be 1 for enabling debug mode and 0 for disabling it
*/
#define HL_DEBUG_OP_SET_MODE 7
struct hl_debug_args { struct hl_debug_args {
/* /*
...@@ -574,8 +578,22 @@ struct hl_debug_args { ...@@ -574,8 +578,22 @@ struct hl_debug_args {
* *
* This IOCTL allows the user to get debug traces from the chip. * This IOCTL allows the user to get debug traces from the chip.
* *
* The user needs to provide the register index and essential data such as * Before the user can send configuration requests of the various
* buffer address and size. * debug/profile engines, it needs to set the device into debug mode.
* This is because the debug/profile infrastructure is shared component in the
* device and we can't allow multiple users to access it at the same time.
*
* Once a user set the device into debug mode, the driver won't allow other
* users to "work" with the device, i.e. open a FD. If there are multiple users
* opened on the device, the driver won't allow any user to debug the device.
*
* For each configuration request, the user needs to provide the register index
* and essential data such as buffer address and size.
*
* Once the user has finished using the debug/profile engines, he should
* set the device into non-debug mode, i.e. disable debug mode.
*
* The driver can decide to "kick out" the user if he abuses this interface.
* *
*/ */
#define HL_IOCTL_DEBUG \ #define HL_IOCTL_DEBUG \
......
...@@ -580,15 +580,14 @@ config IKCONFIG_PROC ...@@ -580,15 +580,14 @@ config IKCONFIG_PROC
This option enables access to the kernel configuration file This option enables access to the kernel configuration file
through /proc/config.gz. through /proc/config.gz.
config IKHEADERS_PROC config IKHEADERS
tristate "Enable kernel header artifacts through /proc/kheaders.tar.xz" tristate "Enable kernel headers through /sys/kernel/kheaders.tar.xz"
depends on PROC_FS depends on SYSFS
help help
This option enables access to the kernel header and other artifacts that This option enables access to the in-kernel headers that are generated during
are generated during the build process. These can be used to build eBPF the build process. These can be used to build eBPF tracing programs,
tracing programs, or similar programs. If you build the headers as a or similar programs. If you build the headers as a module, a module called
module, a module called kheaders.ko is built which can be loaded on-demand kheaders.ko is built which can be loaded on-demand to get access to headers.
to get access to the headers.
config LOG_BUF_SHIFT config LOG_BUF_SHIFT
int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
......
...@@ -71,7 +71,7 @@ obj-$(CONFIG_UTS_NS) += utsname.o ...@@ -71,7 +71,7 @@ obj-$(CONFIG_UTS_NS) += utsname.o
obj-$(CONFIG_USER_NS) += user_namespace.o obj-$(CONFIG_USER_NS) += user_namespace.o
obj-$(CONFIG_PID_NS) += pid_namespace.o obj-$(CONFIG_PID_NS) += pid_namespace.o
obj-$(CONFIG_IKCONFIG) += configs.o obj-$(CONFIG_IKCONFIG) += configs.o
obj-$(CONFIG_IKHEADERS_PROC) += kheaders.o obj-$(CONFIG_IKHEADERS) += kheaders.o
obj-$(CONFIG_SMP) += stop_machine.o obj-$(CONFIG_SMP) += stop_machine.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
...@@ -127,7 +127,7 @@ $(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE ...@@ -127,7 +127,7 @@ $(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE
$(obj)/kheaders.o: $(obj)/kheaders_data.tar.xz $(obj)/kheaders.o: $(obj)/kheaders_data.tar.xz
quiet_cmd_genikh = CHK $(obj)/kheaders_data.tar.xz quiet_cmd_genikh = CHK $(obj)/kheaders_data.tar.xz
cmd_genikh = $(CONFIG_SHELL) $(srctree)/kernel/gen_ikh_data.sh $@ cmd_genikh = $(CONFIG_SHELL) $(srctree)/kernel/gen_kheaders.sh $@
$(obj)/kheaders_data.tar.xz: FORCE $(obj)/kheaders_data.tar.xz: FORCE
$(call cmd,genikh) $(call cmd,genikh)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
# This script generates an archive consisting of kernel headers # This script generates an archive consisting of kernel headers
# for CONFIG_IKHEADERS_PROC. # for CONFIG_IKHEADERS.
set -e set -e
spath="$(dirname "$(readlink -f "$0")")" spath="$(dirname "$(readlink -f "$0")")"
kroot="$spath/.." kroot="$spath/.."
...@@ -31,9 +31,8 @@ arch/$SRCARCH/include/ ...@@ -31,9 +31,8 @@ arch/$SRCARCH/include/
# This block is useful for debugging the incremental builds. # This block is useful for debugging the incremental builds.
# Uncomment it for debugging. # Uncomment it for debugging.
# iter=1 # if [ ! -f /tmp/iter ]; then iter=1; echo 1 > /tmp/iter;
# if [ ! -f /tmp/iter ]; then echo 1 > /tmp/iter; # else iter=$(($(cat /tmp/iter) + 1)); echo $iter > /tmp/iter; fi
# else; iter=$(($(cat /tmp/iter) + 1)); fi
# find $src_file_list -type f | xargs ls -lR > /tmp/src-ls-$iter # find $src_file_list -type f | xargs ls -lR > /tmp/src-ls-$iter
# find $obj_file_list -type f | xargs ls -lR > /tmp/obj-ls-$iter # find $obj_file_list -type f | xargs ls -lR > /tmp/obj-ls-$iter
...@@ -43,10 +42,18 @@ arch/$SRCARCH/include/ ...@@ -43,10 +42,18 @@ arch/$SRCARCH/include/
pushd $kroot > /dev/null pushd $kroot > /dev/null
src_files_md5="$(find $src_file_list -type f | src_files_md5="$(find $src_file_list -type f |
grep -v "include/generated/compile.h" | grep -v "include/generated/compile.h" |
grep -v "include/generated/autoconf.h" |
grep -v "include/config/auto.conf" |
grep -v "include/config/auto.conf.cmd" |
grep -v "include/config/tristate.conf" |
xargs ls -lR | md5sum | cut -d ' ' -f1)" xargs ls -lR | md5sum | cut -d ' ' -f1)"
popd > /dev/null popd > /dev/null
obj_files_md5="$(find $obj_file_list -type f | obj_files_md5="$(find $obj_file_list -type f |
grep -v "include/generated/compile.h" | grep -v "include/generated/compile.h" |
grep -v "include/generated/autoconf.h" |
grep -v "include/config/auto.conf" |
grep -v "include/config/auto.conf.cmd" |
grep -v "include/config/tristate.conf" |
xargs ls -lR | md5sum | cut -d ' ' -f1)" xargs ls -lR | md5sum | cut -d ' ' -f1)"
if [ -f $tarfile ]; then tarfile_md5="$(md5sum $tarfile | cut -d ' ' -f1)"; fi if [ -f $tarfile ]; then tarfile_md5="$(md5sum $tarfile | cut -d ' ' -f1)"; fi
......
...@@ -8,9 +8,8 @@ ...@@ -8,9 +8,8 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/proc_fs.h> #include <linux/kobject.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/uaccess.h>
/* /*
* Define kernel_headers_data and kernel_headers_data_end, within which the * Define kernel_headers_data and kernel_headers_data_end, within which the
...@@ -31,39 +30,32 @@ extern char kernel_headers_data; ...@@ -31,39 +30,32 @@ extern char kernel_headers_data;
extern char kernel_headers_data_end; extern char kernel_headers_data_end;
static ssize_t static ssize_t
ikheaders_read_current(struct file *file, char __user *buf, ikheaders_read(struct file *file, struct kobject *kobj,
size_t len, loff_t *offset) struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t len)
{ {
return simple_read_from_buffer(buf, len, offset, memcpy(buf, &kernel_headers_data + off, len);
&kernel_headers_data, return len;
&kernel_headers_data_end -
&kernel_headers_data);
} }
static const struct file_operations ikheaders_file_ops = { static struct bin_attribute kheaders_attr __ro_after_init = {
.read = ikheaders_read_current, .attr = {
.llseek = default_llseek, .name = "kheaders.tar.xz",
.mode = 0444,
},
.read = &ikheaders_read,
}; };
static int __init ikheaders_init(void) static int __init ikheaders_init(void)
{ {
struct proc_dir_entry *entry; kheaders_attr.size = (&kernel_headers_data_end -
/* create the current headers file */
entry = proc_create("kheaders.tar.xz", S_IRUGO, NULL,
&ikheaders_file_ops);
if (!entry)
return -ENOMEM;
proc_set_size(entry,
&kernel_headers_data_end -
&kernel_headers_data); &kernel_headers_data);
return 0; return sysfs_create_bin_file(kernel_kobj, &kheaders_attr);
} }
static void __exit ikheaders_cleanup(void) static void __exit ikheaders_cleanup(void)
{ {
remove_proc_entry("kheaders.tar.xz", NULL); sysfs_remove_bin_file(kernel_kobj, &kheaders_attr);
} }
module_init(ikheaders_init); module_init(ikheaders_init);
......
...@@ -224,30 +224,30 @@ static ssize_t config_show(struct device *dev, ...@@ -224,30 +224,30 @@ static ssize_t config_show(struct device *dev,
mutex_lock(&test_fw_mutex); mutex_lock(&test_fw_mutex);
len += snprintf(buf, PAGE_SIZE, len += scnprintf(buf, PAGE_SIZE - len,
"Custom trigger configuration for: %s\n", "Custom trigger configuration for: %s\n",
dev_name(dev)); dev_name(dev));
if (test_fw_config->name) if (test_fw_config->name)
len += snprintf(buf+len, PAGE_SIZE, len += scnprintf(buf+len, PAGE_SIZE - len,
"name:\t%s\n", "name:\t%s\n",
test_fw_config->name); test_fw_config->name);
else else
len += snprintf(buf+len, PAGE_SIZE, len += scnprintf(buf+len, PAGE_SIZE - len,
"name:\tEMTPY\n"); "name:\tEMTPY\n");
len += snprintf(buf+len, PAGE_SIZE, len += scnprintf(buf+len, PAGE_SIZE - len,
"num_requests:\t%u\n", test_fw_config->num_requests); "num_requests:\t%u\n", test_fw_config->num_requests);
len += snprintf(buf+len, PAGE_SIZE, len += scnprintf(buf+len, PAGE_SIZE - len,
"send_uevent:\t\t%s\n", "send_uevent:\t\t%s\n",
test_fw_config->send_uevent ? test_fw_config->send_uevent ?
"FW_ACTION_HOTPLUG" : "FW_ACTION_HOTPLUG" :
"FW_ACTION_NOHOTPLUG"); "FW_ACTION_NOHOTPLUG");
len += snprintf(buf+len, PAGE_SIZE, len += scnprintf(buf+len, PAGE_SIZE - len,
"sync_direct:\t\t%s\n", "sync_direct:\t\t%s\n",
test_fw_config->sync_direct ? "true" : "false"); test_fw_config->sync_direct ? "true" : "false");
len += snprintf(buf+len, PAGE_SIZE, len += scnprintf(buf+len, PAGE_SIZE - len,
"read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
mutex_unlock(&test_fw_mutex); mutex_unlock(&test_fw_mutex);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment