| /* |
| * Copyright (c) 2016 Google Inc |
| * Author: Kanoj Sarcar <kanoj@google.com> |
| * See LICENSE for details. |
| */ |
| |
| #include <err.h> |
| #include <kmalloc.h> |
| #include <kref.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <error.h> |
| #include <pmap.h> |
| #include <smp.h> |
| #include <linux/rdma/ib_user_verbs.h> |
| #include "uverbs.h" |
| #include <ros/procinfo.h> |
| |
| static unsigned long pgprot_val(int vmprot) |
| { |
| unsigned long prot = PTE_P | PTE_U | PTE_A; |
| |
| if (vmprot & PROT_WRITE) |
| prot |= PTE_W | PTE_D; |
| return prot; |
| } |
| |
| unsigned long pgprot_noncached(int vmprot) |
| { |
| return pgprot_val(vmprot) | PTE_NOCACHE; |
| } |
| |
| unsigned long pgprot_writecombine(int vmprot) |
| { |
| return pgprot_val(vmprot) | PTE_WRITECOMB; |
| } |
| |
| /* |
| * Our version knocked off from kern/src/mm.c version + uncaching logic from |
| * vmap_pmem_nocache(). This routine is expected to be invoked as part of mmap() |
| * handler. |
| */ |
| int map_upage_at_addr(struct proc *p, physaddr_t paddr, uintptr_t addr, |
| int pteprot, int dolock) |
| { |
| pte_t pte; |
| int rv = -1; |
| struct page *pp; |
| |
| /* __vmr_free_pgs() assumes mapped pte is backed by "struct page" */ |
| if (paddr > max_paddr) { |
| printk("[akaros]: map_upage_at_addr(): paddr=0x%llx " |
| "max_paddr=0x%llx\n", paddr, max_paddr); |
| return -1; |
| } |
| |
| pp = pa2page(paddr); |
| |
| /* __vmr_free_pgs() refcnt's pagemap pages differently */ |
| if (atomic_read(&pp->pg_flags) & PG_PAGEMAP) { |
| printk("[akaros]: map_upage_at_addr(): mapPA=0x%llx\n", |
| paddr); |
| return -1; |
| } |
| |
| spin_lock(&p->pte_lock); |
| |
| /* |
| * Free any existing page backing uva, drop in this page, and |
| * acquire refcnt on page on behalf of user. Note though that we |
| * do not expect an existing page, since we are invoked in mmap |
| * path (page_insert() does not handle PG_PAGEMAP refcnt's). |
| */ |
| rv = page_insert(p->env_pgdir, pp, (void *)addr, pteprot); |
| spin_unlock(&p->pte_lock); |
| return rv; |
| } |
| |
| void set_page_dirty_lock(struct page *pagep) |
| { |
| atomic_or(&pagep->pg_flags, PG_DIRTY); |
| } |
| |
| void put_page(struct page *pagep) |
| { |
| if (atomic_read(&pagep->pg_flags) & PG_PAGEMAP) |
| printk("[akaros]: put_page() on pagemap page!!!\n"); |
| page_decref(pagep); |
| } |
| |
| int get_user_page(struct proc *p, unsigned long uvastart, int write, int force, |
| struct page **plist) |
| { |
| pte_t pte; |
| int ret = -1; |
| struct page *pp; |
| |
| spin_lock(&p->pte_lock); |
| |
| pte = pgdir_walk(p->env_pgdir, (void*)uvastart, TRUE); |
| |
| if (!pte_walk_okay(pte)) |
| goto err1; |
| |
| if (!pte_is_present(pte)) { |
| unsigned long prot = PTE_P | PTE_U | PTE_A | PTE_W | PTE_D; |
| #if 0 |
| printk("[akaros]: get_user_page() uva=0x%llx pte absent\n", |
| uvastart); |
| #endif |
| /* |
| * TODO: ok to allocate with pte_lock? "prot" needs to be |
| * based on VMR writability, refer to pgprot_noncached(). |
| */ |
| if (upage_alloc(p, &pp, 0)) |
| goto err1; |
| pte_write(pte, page2pa(pp), prot); |
| } else { |
| pp = pa2page(pte_get_paddr(pte)); |
| |
| /* __vmr_free_pgs() refcnt's pagemap pages differently */ |
| if (atomic_read(&pp->pg_flags) & PG_PAGEMAP) { |
| printk("[akaros]: get_user_page(): uva=0x%llx\n", |
| uvastart); |
| goto err1; |
| } |
| } |
| |
| if (write && (!pte_has_perm_urw(pte))) { |
| /* TODO: How is Linux using the "force" parameter */ |
| printk("[akaros]: get_user_page() uva=0x%llx pte ro\n", |
| uvastart); |
| goto err1; |
| } |
| |
| /* TODO (GUP): change the interface such that devices provide the memory |
| * and the user mmaps it, instead of trying to pin arbitrary user |
| * memory. */ |
| warn_once("Extremely unsafe, unpinned memory mapped! If your process dies, you might scribble on RAM!"); |
| |
| plist[0] = pp; |
| ret = 1; |
| err1: |
| spin_unlock(&p->pte_lock); |
| return ret; |
| } |
| |
| int sg_alloc_table(struct sg_table *ptr, unsigned int npages, gfp_t mask) |
| { |
| ptr->sgl = kmalloc((sizeof(struct scatterlist) * npages), mask); |
| ptr->nents = ptr->orig_nents = npages; |
| sg_init_table(ptr->sgl, npages); |
| return 0; |
| } |
| |
| void sg_free_table(struct sg_table *ptr) |
| { |
| kfree(ptr->sgl); |
| } |
| |
| void idr_remove(struct idr *idp, int id) |
| { |
| BUG_ON((id < 0) || (id >= MAXITEMS)); |
| idp->values[id] = NULL; |
| } |
| |
| void *idr_find(struct idr *idp, int id) |
| { |
| BUG_ON((id < 0) || (id >= MAXITEMS)); |
| BUG_ON(idp->values[id] == NULL); |
| return idp->values[id]; |
| } |
| |
| int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask) |
| { |
| int i; |
| |
| /* We use values[] == NULL as an indicator that slot is free */ |
| BUG_ON(ptr == NULL); |
| |
| spin_lock_irqsave(&idp->lock); |
| |
| for (i = 0; i < MAXITEMS; i++) { |
| if (idp->values[i] == NULL) { |
| idp->values[i] = ptr; |
| goto done; |
| } |
| } |
| |
| i = -1; /* error return */ |
| |
| done: |
| spin_unlock_irqsave(&idp->lock); |
| return i; |
| } |
| |
| /* START: Linux /sys support for lib/apps */ |
| |
| /* Callers must pass in null terminated strings */ |
| static ssize_t sysfs_read(char __user *buf, size_t ucount, loff_t *pos, |
| char *src) |
| { |
| int slen = strlen(src) + 1; /* + 1 for terminating null */ |
| unsigned long off = *pos, nb = slen - off; |
| |
| if (off >= slen) |
| return 0; |
| |
| if (copy_to_user(buf, (src + off), nb)) |
| return -EFAULT; |
| |
| *pos += nb; |
| return nb; |
| } |
| |
| static ssize_t ib_api_ver_read(struct file *filp, char __user *buf, |
| size_t count, loff_t *pos) |
| { |
| char src[4] = { 0, 0, 0, 0}; |
| |
| src[0] = '0' + IB_USER_VERBS_ABI_VERSION; |
| |
| return sysfs_read(buf, count, pos, src); |
| } |
| |
| #if 0 // AKAROS_PORT (9ns hooks) |
| static const struct file_operations ib_api_ver = { |
| .read = ib_api_ver_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| #endif |
| |
| static ssize_t mlx4_mgm_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| #if CONFIG_MLX4_DEFAULT_MGM_LOG_ENTRY_SIZE == -1 |
| char src[4] = { '-', '1', 0, 0 }; |
| #else |
| char src[4] = { '1', '0', 0, 0 }; |
| #endif |
| |
| return sysfs_read(buf, count, pos, src); |
| } |
| |
| #if 0 // AKAROS_PORT |
| static const struct file_operations mlx4_mgm = { |
| .read = mlx4_mgm_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| #endif |
| |
| #if 0 |
| static void stradd(char *dest, int val, int num) |
| { |
| int tval = val, i = 0, fac = 1; |
| |
| while (tval) { |
| tval /= 10; |
| fac *= 10; |
| i++; |
| } |
| fac /= 10; |
| tval = val; |
| while (tval && num) { |
| int dig = tval / fac; |
| *dest++ = dig + '0'; |
| tval -= (dig * fac); |
| fac /= 10; |
| num--; |
| } |
| } |
| |
| static ssize_t cpu_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| char cpu_info_str[128]; |
| long freq = __proc_global_info.tsc_freq, idx; |
| |
| strncpy(cpu_info_str, "cpu MHz\t\t: ", 16); |
| idx = strlen(cpu_info_str); |
| |
| stradd(cpu_info_str + idx, freq / 1000000, 4); |
| idx += 4; |
| |
| strncpy(cpu_info_str + idx, ".", 1); |
| idx++; |
| |
| stradd(cpu_info_str + idx, freq % 1000000, 3); |
| idx += 3; |
| |
| cpu_info_str[idx] = 0; |
| |
| return sysfs_read(buf, count, pos, cpu_info_str); |
| } |
| |
| static const struct file_operations cpuinfo = { |
| .read = cpu_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| #endif |
| |
| void sysfs_init(void) |
| { |
| #if 1 // AKAROS_PORT |
| warn("mlx4: udrvr stuff requires various files, implement for 9ns!"); |
| #else |
| do_mkdir("/dev_vfs/infiniband", S_IRWXU | S_IRWXG | S_IRWXO); |
| do_mkdir("/sys", S_IRWXU | S_IRWXG | S_IRWXO); |
| do_mkdir("/sys/class", S_IRWXU | S_IRWXG | S_IRWXO); |
| do_mkdir("/sys/class/infiniband_verbs", S_IRWXU | S_IRWXG | S_IRWXO); |
| do_mkdir("/sys/class/infiniband", S_IRWXU | S_IRWXG | S_IRWXO); |
| |
| make_device("/sys/class/infiniband_verbs/abi_version", |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&ib_api_ver); |
| |
| do_mkdir("/sys/module", S_IRWXU | S_IRWXG | S_IRWXO); |
| do_mkdir("/sys/module/mlx4_core", S_IRWXU | S_IRWXG | S_IRWXO); |
| do_mkdir("/sys/module/mlx4_core/parameters", S_IRWXU | S_IRWXG | |
| S_IRWXO); |
| make_device("/sys/module/mlx4_core/parameters/log_num_mgm_entry_size", |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&mlx4_mgm); |
| |
| #if 0 |
| /* Do this thru init scripts */ |
| do_mkdir("/proc", S_IRWXU | S_IRWXG | S_IRWXO); |
| make_device("/proc/cpuinfo", S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | |
| S_IRGRP | S_IROTH, __S_IFCHR, (struct file_operations *)&cpuinfo); |
| #endif |
| #endif |
| } |
| |
| static ssize_t dver_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| struct ib_uverbs_device *uvp; |
| char src[4] = { 0, 0, 0, 0}; |
| |
| uvp = (struct ib_uverbs_device *)get_fs_info(filp); |
| src[0] = '0' + uvp->ib_dev->uverbs_abi_ver; |
| |
| return sysfs_read(buf, count, pos, src); |
| } |
| |
| static ssize_t dname_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| struct ib_uverbs_device *uvp; |
| |
| uvp = (struct ib_uverbs_device *)get_fs_info(filp); |
| return sysfs_read(buf, count, pos, uvp->ib_dev->name); |
| } |
| |
| static ssize_t ntype_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| char src[] = "1"; |
| |
| return sysfs_read(buf, count, pos, src); |
| } |
| |
| static ssize_t ddev_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| char src[] = "0x1003"; |
| |
| return sysfs_read(buf, count, pos, src); |
| } |
| |
| static ssize_t dven_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| char src[] = "0x15b3"; |
| |
| return sysfs_read(buf, count, pos, src); |
| } |
| |
| static ssize_t vsd_read(struct file *filp, char __user *buf, size_t count, |
| loff_t *pos) |
| { |
| char *src = "puma20_A1-10.2.3.0"; |
| |
| return sysfs_read(buf, count, pos, src); |
| } |
| |
| #if 0 // AKAROS_PORT |
| static const struct file_operations dver_fops = { |
| .read = dver_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| |
| static const struct file_operations dname_fops = { |
| .read = dname_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| |
| static const struct file_operations ddev_fops = { |
| .read = ddev_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| |
| static const struct file_operations dven_fops = { |
| .read = dven_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| |
| static const struct file_operations ntype_fops = { |
| .read = ntype_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| |
| static const struct file_operations vsd_fops = { |
| .read = vsd_read, |
| .open = kfs_open, |
| .release= kfs_release, |
| }; |
| #endif |
| |
| void sysfs_create(int devnum, const struct file_operations *verb_fops, |
| void *ptr) |
| { |
| #if 1 // AKAROS_PORT |
| warn("mlx4: udrvr stuff requires various files, implement for 9ns!"); |
| #else |
| char sysname[256] = "/sys/class/infiniband_verbs/uverbs0"; |
| char devname[] = "/dev_vfs/infiniband/uverbs0"; |
| char drvname[64] = "/sys/class/infiniband/"; |
| int sysnameidx = strlen(sysname), drvidx; |
| struct file *fp; |
| struct ib_uverbs_device *uvp = (struct ib_uverbs_device *)ptr; |
| |
| /* Create correct name */ |
| if (devnum > 9) |
| panic("Too many devs"); |
| devname[strlen(devname) - 1] = '0' + devnum; |
| sysname[sysnameidx - 1] = '0' + devnum; |
| |
| /* Foll fops need to come from caller */ |
| fp = make_device(devname, |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)verb_fops); |
| set_fs_info(fp, ptr); |
| |
| /* /sys/class/infiniband/mlx4_0 */ |
| strncpy((drvname + strlen(drvname)), uvp->ib_dev->name, 12); |
| do_mkdir(drvname, S_IRWXU | S_IRWXG | S_IRWXO); |
| drvidx = strlen(drvname); |
| |
| /* /sys/class/infiniband/mlx4_0/node_type */ |
| strncpy(drvname + drvidx, "/node_type", 11); |
| make_device(drvname, |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&ntype_fops); |
| |
| /* /sys/class/infiniband/mlx4_0/vsd */ |
| strncpy(drvname + drvidx, "/vsd", 5); |
| fp = make_device(drvname, |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&vsd_fops); |
| set_fs_info(fp, ptr); |
| |
| /* /sys/class/infiniband_verbs/uverbs0 */ |
| do_mkdir(sysname, S_IRWXU | S_IRWXG | S_IRWXO); |
| |
| /* /sys/class/infiniband_verbs/uverbs0/device */ |
| strncpy(sysname + sysnameidx, "/device", 16); |
| do_mkdir(sysname, S_IRWXU | S_IRWXG | S_IRWXO); |
| |
| /* /sys/class/infiniband_verbs/uverbs0/device/device */ |
| strncpy(sysname + sysnameidx, "/device/device", 16); |
| fp = make_device(sysname, |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&ddev_fops); |
| set_fs_info(fp, ptr); |
| |
| /* /sys/class/infiniband_verbs/uverbs0/device/vendor */ |
| strncpy(sysname + sysnameidx, "/device/vendor", 16); |
| fp = make_device(sysname, |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&dven_fops); |
| set_fs_info(fp, ptr); |
| |
| /* /sys/class/infiniband_verbs/uverbs0/ibdev */ |
| strncpy(sysname + sysnameidx, "/ibdev", 16); |
| fp = make_device(sysname, |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&dname_fops); |
| set_fs_info(fp, ptr); |
| |
| /* /sys/class/infiniband_verbs/uverbs0/abi_version */ |
| strncpy(sysname + sysnameidx, "/abi_version", 16); |
| fp = make_device(sysname, |
| S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH, |
| __S_IFCHR, (struct file_operations *)&dver_fops); |
| set_fs_info(fp, ptr); |
| #endif |
| } |
| |
| /* END: Linux /sys support for lib/apps */ |
| |
| /* START: Support older version of libibverbs */ |
| |
| /* in_words and provider_in_words are in terms of 4-byte words, not 8-byte */ |
| struct ib_uverbs_ex_cmd_hdr_compat { |
| __u16 provider_in_words; |
| __u16 provider_out_words; |
| __u32 cmd_hdr_reserved; |
| __u32 comp_mask; |
| /* __u32 dummy; */ |
| __u64 response; |
| __u32 qp_handle; |
| }; |
| |
| static ssize_t compat_ex(struct ib_uverbs_file *file, size_t count, |
| const char __user *buf) |
| { |
| struct ib_uverbs_cmd_hdr hdr; |
| struct ib_uverbs_ex_cmd_hdr_compat ex_hdr; |
| struct ib_udata ucore; |
| struct ib_udata uhw; |
| __u32 command; |
| int err; |
| unsigned long tmpbuf[16]; |
| struct ib_uverbs_create_flow *ptr; |
| |
| if (copy_from_user(&hdr, buf, sizeof hdr)) |
| return -EFAULT; |
| |
| command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK; |
| command -= 2; |
| |
| if (command == IB_USER_VERBS_EX_CMD_DESTROY_FLOW) { |
| INIT_UDATA_BUF_OR_NULL(&ucore, buf + 8, 0, 8, 0); |
| err = ib_uverbs_ex_destroy_flow(file, &ucore, &uhw); |
| goto next; |
| } |
| |
| /* |
| * "struct ibv_create_flow" is 56 bytes, "struct ibv_kern_spec" is |
| * 48 bytes, so at a minimum we expect 56 + (n x 48), n >= 1. |
| */ |
| if (count < 104) |
| return -EINVAL; |
| |
| if (copy_from_user(&ex_hdr, buf + sizeof(hdr), sizeof(ex_hdr))) |
| return -EFAULT; |
| |
| if ((hdr.in_words + ex_hdr.provider_in_words) * 4 != count) |
| return -EINVAL; |
| |
| if (ex_hdr.cmd_hdr_reserved) |
| return -EINVAL; |
| |
| if (ex_hdr.comp_mask) |
| return -EINVAL; |
| |
| if (ex_hdr.response) { |
| if (!hdr.out_words && !ex_hdr.provider_out_words) |
| return -EINVAL; |
| |
| if (!access_ok(VERIFY_WRITE, |
| (void __user *) (unsigned long) ex_hdr.response, |
| (hdr.out_words + ex_hdr.provider_out_words) * 4)) |
| return -EFAULT; |
| } else { |
| if (hdr.out_words || ex_hdr.provider_out_words) |
| return -EINVAL; |
| } |
| |
| ptr = (struct ib_uverbs_create_flow *)tmpbuf; |
| ptr->comp_mask = 0; /* user input already validated above */ |
| ptr->qp_handle = ex_hdr.qp_handle; |
| |
| if ((count-36) > 120) |
| BUG(); |
| |
| /* Copy 16 bytes worth "struct ibv_kern_flow_attr" */ |
| copy_from_user(&tmpbuf[1], buf+36, sizeof(struct ib_uverbs_flow_attr)); |
| |
| ptr->flow_attr.size -= 56; /* Comes in as 96 = 56 + 40 */ |
| |
| /* Copy "struct ibv_kern_spec"s */ |
| copy_from_user(&tmpbuf[3], buf+56, count-56); |
| |
| /* |
| * Copy : count-56 "struct ibv_kern_spec"s, |
| * 16 bytes "struct ibv_kern_flow_attr", 16 bytes comp_mask/qp_handle. |
| */ |
| copy_to_user((char __user *)buf, tmpbuf, count-24); |
| |
| INIT_UDATA_BUF_OR_NULL(&ucore, buf, |
| (unsigned long) ex_hdr.response, count - 24, |
| hdr.out_words * 4); |
| |
| err = ib_uverbs_ex_create_flow(file, &ucore, &uhw); |
| |
| next: |
| if (err) |
| return err; |
| |
| return count; |
| } |
| |
| static ssize_t compat(struct ib_uverbs_file *file, size_t count, |
| const char __user *buf) |
| { |
| unsigned long tmpbuf[17]; |
| struct ib_uverbs_cmd_hdr *p = (struct ib_uverbs_cmd_hdr *)tmpbuf; |
| char __user *dst = (char __user *)buf; |
| int insz, outsz; |
| |
| /* |
| * User "struct ibv_qp_dest" is 40 bytes, passes in 136 bytes. |
| * Kernel "struct ib_uverbs_qp_dest" is 32 bytes, expects 120. |
| * Last 8 bytes of user "struct ibv_qp_dest" not used by kernel. |
| * Kernel expects this layout: |
| * struct ib_uverbs_cmd_hdr (8) |
| * struct ib_uverbs_qp_dest (32 <- 40) |
| * struct ib_uverbs_qp_dest (32 <- 40) |
| * Rest of qp_mod inputs (48) |
| */ |
| |
| if (count > 136) |
| BUG(); |
| |
| if (copy_from_user(tmpbuf, buf, count)) |
| return -EFAULT; |
| insz = p->in_words * 4; |
| outsz = p->out_words * 4; |
| |
| copy_to_user(dst, &tmpbuf[1], sizeof(struct ib_uverbs_qp_dest)); |
| dst += sizeof(struct ib_uverbs_qp_dest); |
| copy_to_user(dst, &tmpbuf[6], sizeof(struct ib_uverbs_qp_dest)); |
| dst += sizeof(struct ib_uverbs_qp_dest); |
| copy_to_user(dst, &tmpbuf[11], 48); |
| |
| |
| return ib_uverbs_modify_qp(file, buf, insz, outsz); |
| } |
| |
| /* |
| * Request structure is: |
| * ib_uverbs_cmd_hdr :: (almost) ib_uverbs_ex_cmd_hdr_compat. |
| * Response structure is: |
| * 8B comp_mask :: ib_uverbs_query_device_resp :: 8B timestamp_mask :: |
| * 8B hca_core_clock |
| */ |
| static ssize_t compat_query(struct ib_uverbs_file *file, size_t count, |
| const char __user *buf) |
| { |
| unsigned long tmpbuf[17], tval = 0; |
| struct ib_uverbs_cmd_hdr *p = (struct ib_uverbs_cmd_hdr *)tmpbuf; |
| char __user *dst = (char __user *)buf; |
| int insz, outsz; |
| |
| if (copy_from_user(tmpbuf, buf, count)) |
| return -EFAULT; |
| insz = p->in_words * 4; |
| outsz = p->out_words * 4; |
| |
| /* Zero out expected comp_mask field in response */ |
| copy_to_user((void *)tmpbuf[3], &tval, 8); |
| /* Kernel writes out after expected comp_mask field */ |
| tmpbuf[3] += 8; |
| /* Move "response" upwards to "buf" */ |
| copy_to_user(dst, &tmpbuf[3], sizeof(struct ib_uverbs_query_device)); |
| |
| return ib_uverbs_query_device(file, buf, insz, outsz); |
| } |
| |
| /* |
| * Compat hack for applications/libraries we care about. Retrofit Linux 3.12 |
| * style APIs. |
| */ |
| ssize_t check_old_abi(struct file *filp, const char __user *buf, size_t count) |
| { |
| struct ib_uverbs_cmd_hdr hdr; |
| int tmp; |
| struct ib_uverbs_file *file = filp->private_data; |
| |
| if (copy_from_user(&hdr, buf, sizeof hdr)) |
| return -EFAULT; |
| |
| tmp = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK; |
| if ((tmp >= 52) && (tmp <= 53)) { |
| return compat_ex(file, count, buf); |
| } else if (tmp == IB_USER_VERBS_CMD_MODIFY_QP) { |
| return compat(file, count, buf); |
| } else if (tmp == 56) { |
| return compat_query(file, count, buf); |
| } else if (tmp == IB_USER_VERBS_CMD_QUERY_QP) { |
| panic("query_qp API difference not handled\n"); |
| } |
| |
| /* Continue with processing this command */ |
| return 0; |
| } |
| |
| /* END: Support older version of libibverbs */ |