436240feb22b91358f3cdaff66d843a24c3e666d
[akaros.git] / kern / drivers / net / udrvr / compat.c
1 /*
2  * Copyright (c) 2016 Google Inc
3  * Author: Kanoj Sarcar <kanoj@google.com>
4  * See LICENSE for details.
5  */
6
7 #include <err.h>
8 #include <kmalloc.h>
9 #include <kref.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <assert.h>
13 #include <error.h>
14 #include <pmap.h>
15 #include <smp.h>
16 #include <devfs.h>
17 #include <linux/rdma/ib_user_verbs.h>
18 #include "uverbs.h"
19
20 /*
21  * Our version knocked off from kern/src/mm.c version + uncaching logic from
22  * vmap_pmem_nocache().
23  */
24 int map_upage_at_addr(struct proc *p, physaddr_t paddr, uintptr_t addr, int pteprot, int dolock)
25 {
26         pte_t   pte;
27         int     rv = -1;
28
29         spin_lock(&p->pte_lock);
30
31         pte = pgdir_walk(p->env_pgdir, (void*)addr, TRUE);
32
33         if (!pte_walk_okay(pte))
34                 goto err1;
35         pte_write(pte, paddr, pteprot);
36         // tlbflush(); tlb_flush_global();
37         rv = 0;
38 err1:
39         spin_unlock(&p->pte_lock);
40
41         /*
42          * TODO: @mm tear down, unmap_and_destroy_vmrs():__vmr_free_pgs()
43          * decrefs page, which is a problem. 1st level workaround is to set
44          * PG_LOCKED/PG_PAGEMAP to avoid that. Not proud of myself.
45          */
46         if ((rv == 0) && (dolock == 1))
47                 atomic_set(&pa2page(paddr)->pg_flags, PG_LOCKED | PG_PAGEMAP);
48
49         return rv;
50 }
51
52 void set_page_dirty_lock(struct page *pagep)
53 {
54         atomic_or(&pagep->pg_flags, PG_DIRTY);
55 }
56
57 /*
58  * get_user_pages() does not grab a page ref count. Thus, put_page()
59  * can not release page ref count.
60  */
61 void put_page(struct page *pagep)
62 {
63         /* page_decref(pagep) / __put_page(pagep) */
64 }
65
66 int get_user_page(struct proc *p, unsigned long uvastart, int write, int force,
67     struct page **plist)
68 {
69         pte_t   pte;
70         int     ret = -1;
71
72         spin_lock(&p->pte_lock);
73
74         pte = pgdir_walk(p->env_pgdir, (void*)uvastart, TRUE);
75
76         if (!pte_walk_okay(pte))
77                 goto err1;
78
79         if (!pte_is_present(pte)) {
80                 printk("[akaros]: get_user_page() uva=0x%llx pte absent\n",
81                     uvastart);
82                 goto err1;
83         }
84
85         if (write && (!pte_has_perm_urw(pte))) {
86                 /* TODO: How is Linux using the "force" parameter */
87                 printk("[akaros]: get_user_page() uva=0x%llx pte ro\n",
88                     uvastart);
89                 goto err1;
90         }
91
92         plist[0] = pa2page(pte_get_paddr(pte));
93         ret = 1;
94 err1:
95         spin_unlock(&p->pte_lock);
96         return ret;
97 }
98
99 int sg_alloc_table(struct sg_table *ptr, unsigned int npages, gfp_t mask)
100 {
101         ptr->sgl = kmalloc((sizeof(struct scatterlist) * npages), mask);
102         ptr->nents = ptr->orig_nents = npages;
103         return 0;
104 }
105
106 void sg_free_table(struct sg_table *ptr)
107 {
108         kfree(ptr->sgl);
109 }
110
111 void idr_remove(struct idr *idp, int id)
112 {
113         BUG_ON((id < 0) || (id >= MAXITEMS));
114         idp->values[id] = NULL;
115 }
116
117 void *idr_find(struct idr *idp, int id)
118 {
119         BUG_ON((id < 0) || (id >= MAXITEMS));
120         BUG_ON(idp->values[id] == NULL);
121         return idp->values[id];
122 }
123
124 int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask)
125 {
126         int     i;
127
128         /* We use values[] == NULL as an indicator that slot is free */
129         BUG_ON(ptr == NULL);
130
131         spin_lock_irqsave(&idp->lock, f);
132
133         for (i = 0; i < MAXITEMS; i++) {
134                 if (idp->values[i] == NULL) {
135                         idp->values[i] = ptr;
136                         goto done;
137                 }
138         }
139
140         i = -1;                 /* error return */
141
142 done:
143         spin_unlock_irqsave(&idp->lock);
144         return i;
145 }
146
147 /* START: Linux /sys support for lib/apps */
148
149 /* Callers must pass in null terminated strings */
150 static ssize_t sysfs_read(char __user *buf, size_t ucount, loff_t *pos,
151     char *src)
152 {
153         int             slen = strlen(src) + 1; /* + 1 for terminating null */
154         unsigned long   off = *pos, nb = slen - off;
155
156         if (off >= slen)
157                 return 0;
158
159         if (copy_to_user(buf, (src + off), nb))
160                 return -EFAULT;
161
162         *pos += nb;
163         return nb;
164 }
165
166 static ssize_t ib_api_ver_read(struct file *filp, char __user *buf,
167     size_t count, loff_t *pos)
168 {
169         char            src[4] = { 0, 0, 0, 0};
170
171         src[0] = '0' + IB_USER_VERBS_ABI_VERSION;
172
173         return sysfs_read(buf, count, pos, src);
174 }
175
176 static const struct file_operations ib_api_ver = {
177         .read   = ib_api_ver_read,
178         .open   = kfs_open,
179         .release= kfs_release,
180 };
181
182 void sysfs_init(void)
183 {
184         do_mkdir("/dev/infiniband", S_IRWXU | S_IRWXG | S_IRWXO);
185         do_mkdir("/sys", S_IRWXU | S_IRWXG | S_IRWXO);
186         do_mkdir("/sys/class", S_IRWXU | S_IRWXG | S_IRWXO);
187         do_mkdir("/sys/class/infiniband_verbs", S_IRWXU | S_IRWXG | S_IRWXO);
188         do_mkdir("/sys/class/infiniband", S_IRWXU | S_IRWXG | S_IRWXO);
189
190         make_device("/sys/class/infiniband_verbs/abi_version",
191                     S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
192                     __S_IFCHR, (struct file_operations *)&ib_api_ver);
193 }
194
195 static ssize_t dver_read(struct file *filp, char __user *buf,
196     size_t count, loff_t *pos)
197 {
198         struct ib_uverbs_device *uvp;
199         char            src[4] = { 0, 0, 0, 0};
200
201         uvp = (struct ib_uverbs_device *)get_fs_info(filp);
202         src[0] = '0' + uvp->ib_dev->uverbs_abi_ver;
203
204         return sysfs_read(buf, count, pos, src);
205 }
206
207 static ssize_t dname_read(struct file *filp, char __user *buf,
208     size_t count, loff_t *pos)
209 {
210         struct ib_uverbs_device *uvp;
211
212         uvp = (struct ib_uverbs_device *)get_fs_info(filp);
213         return sysfs_read(buf, count, pos, uvp->ib_dev->name);
214 }
215
216 static ssize_t ntype_read(struct file *filp, char __user *buf,
217     size_t count, loff_t *pos)
218 {
219         char    src[] = "1";
220
221         return sysfs_read(buf, count, pos, src);
222 }
223
224 static ssize_t ddev_read(struct file *filp, char __user *buf,
225     size_t count, loff_t *pos)
226 {
227         char    src[] = "0x1003";
228
229         return sysfs_read(buf, count, pos, src);
230 }
231
232 static ssize_t dven_read(struct file *filp, char __user *buf,
233     size_t count, loff_t *pos)
234 {
235         char    src[] = "0x15b3";
236
237         return sysfs_read(buf, count, pos, src);
238 }
239
240 static ssize_t vsd_read(struct file *filp, char __user *buf,
241     size_t count, loff_t *pos)
242 {
243         char    *src = "puma20_A1-10.2.3.0";
244
245         return sysfs_read(buf, count, pos, src);
246 }
247
248 static const struct file_operations dver_fops = {
249         .read   = dver_read,
250         .open   = kfs_open,
251         .release= kfs_release,
252 };
253
254 static const struct file_operations dname_fops = {
255         .read   = dname_read,
256         .open   = kfs_open,
257         .release= kfs_release,
258 };
259
260 static const struct file_operations ddev_fops = {
261         .read   = ddev_read,
262         .open   = kfs_open,
263         .release= kfs_release,
264 };
265
266 static const struct file_operations dven_fops = {
267         .read   = dven_read,
268         .open   = kfs_open,
269         .release= kfs_release,
270 };
271
272 static const struct file_operations ntype_fops = {
273         .read   = ntype_read,
274         .open   = kfs_open,
275         .release= kfs_release,
276 };
277
278 static const struct file_operations vsd_fops = {
279         .read   = vsd_read,
280         .open   = kfs_open,
281         .release= kfs_release,
282 };
283
284 void sysfs_create(int devnum, const struct file_operations *verb_fops,
285     void *ptr)
286 {
287         char            sysname[256] = "/sys/class/infiniband_verbs/uverbs0";
288         char            devname[] = "/dev/infiniband/uverbs0";
289         char            drvname[64] = "/sys/class/infiniband/";
290         int             sysnameidx = strlen(sysname), drvidx;
291         struct file     *fp;
292         struct ib_uverbs_device *uvp = (struct ib_uverbs_device *)ptr;
293
294         /* Create correct name */
295         if (devnum > 9)
296                 panic("Too many devs");
297         devname[strlen(devname) - 1] = '0' + devnum;
298         sysname[sysnameidx - 1] = '0' + devnum;
299
300         /* Foll fops need to come from caller */
301         fp = make_device(devname,
302             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
303             __S_IFCHR, (struct file_operations *)verb_fops);
304         set_fs_info(fp, ptr);
305
306         /* /sys/class/infiniband/mlx4_0 */
307         strncpy((drvname + strlen(drvname)), uvp->ib_dev->name, 12);
308         do_mkdir(drvname, S_IRWXU | S_IRWXG | S_IRWXO);
309         drvidx = strlen(drvname);
310
311         /* /sys/class/infiniband/mlx4_0/node_type */
312         strncpy(drvname + drvidx, "/node_type", 11);
313         make_device(drvname,
314             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
315             __S_IFCHR, (struct file_operations *)&ntype_fops);
316
317         /* /sys/class/infiniband/mlx4_0/vsd */
318         strncpy(drvname + drvidx, "/vsd", 5);
319         fp = make_device(drvname,
320             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
321             __S_IFCHR, (struct file_operations *)&vsd_fops);
322         set_fs_info(fp, ptr);
323
324         /* /sys/class/infiniband_verbs/uverbs0 */
325         do_mkdir(sysname, S_IRWXU | S_IRWXG | S_IRWXO);
326
327         /* /sys/class/infiniband_verbs/uverbs0/device */
328         strncpy(sysname + sysnameidx, "/device", 16);
329         do_mkdir(sysname, S_IRWXU | S_IRWXG | S_IRWXO);
330
331         /* /sys/class/infiniband_verbs/uverbs0/device/device */
332         strncpy(sysname + sysnameidx, "/device/device", 16);
333         fp = make_device(sysname,
334             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
335             __S_IFCHR, (struct file_operations *)&ddev_fops);
336         set_fs_info(fp, ptr);
337
338         /* /sys/class/infiniband_verbs/uverbs0/device/vendor */
339         strncpy(sysname + sysnameidx, "/device/vendor", 16);
340         fp = make_device(sysname,
341             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
342             __S_IFCHR, (struct file_operations *)&dven_fops);
343         set_fs_info(fp, ptr);
344
345         /* /sys/class/infiniband_verbs/uverbs0/ibdev */
346         strncpy(sysname + sysnameidx, "/ibdev", 16);
347         fp = make_device(sysname,
348             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
349             __S_IFCHR, (struct file_operations *)&dname_fops);
350         set_fs_info(fp, ptr);
351
352         /* /sys/class/infiniband_verbs/uverbs0/abi_version */
353         strncpy(sysname + sysnameidx, "/abi_version", 16);
354         fp = make_device(sysname,
355             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
356             __S_IFCHR, (struct file_operations *)&dver_fops);
357         set_fs_info(fp, ptr);
358 }
359
360 /* END: Linux /sys support for lib/apps */
361
362 /* START: Support older version of libibverbs */
363
364 /* in_words and provider_in_words are in terms of 4-byte words, not 8-byte */
365 struct ib_uverbs_ex_cmd_hdr_compat {
366         __u16 provider_in_words;
367         __u16 provider_out_words;
368         __u32 cmd_hdr_reserved;
369         __u32 comp_mask;
370         /* __u32 dummy; */
371         __u64 response;
372         __u32 qp_handle;
373 };
374
375 static ssize_t compat_ex(struct ib_uverbs_file *file, size_t count,
376     const char __user *buf)
377 {
378         struct ib_uverbs_cmd_hdr hdr;
379         struct ib_uverbs_ex_cmd_hdr_compat ex_hdr;
380         struct ib_udata ucore;
381         struct ib_udata uhw;
382         __u32 command;
383         int err;
384         unsigned long   tmpbuf[16];
385         struct ib_uverbs_create_flow *ptr;
386
387         if (copy_from_user(&hdr, buf, sizeof hdr))
388                 return -EFAULT;
389
390         command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
391         command -= 2;
392
393         if (command == IB_USER_VERBS_EX_CMD_DESTROY_FLOW) {
394                 INIT_UDATA_BUF_OR_NULL(&ucore, buf + 8, 0, 8, 0);
395                 err = ib_uverbs_ex_destroy_flow(file, &ucore, &uhw);
396                 goto next;
397         }
398
399         /*
400          * "struct ibv_create_flow" is 56 bytes, "struct ibv_kern_spec" is
401          * 48 bytes, so at a minimum we expect 56 + (n x 48), n >= 1.
402          */
403         if (count < 104)
404                 return -EINVAL;
405
406         if (copy_from_user(&ex_hdr, buf + sizeof(hdr), sizeof(ex_hdr)))
407                 return -EFAULT;
408
409         if ((hdr.in_words + ex_hdr.provider_in_words) * 4 != count)
410                 return -EINVAL;
411
412         if (ex_hdr.cmd_hdr_reserved)
413                 return -EINVAL;
414
415         if (ex_hdr.comp_mask)
416                 return -EINVAL;
417
418         if (ex_hdr.response) {
419                 if (!hdr.out_words && !ex_hdr.provider_out_words)
420                         return -EINVAL;
421
422                 if (!access_ok(VERIFY_WRITE,
423                                (void __user *) (unsigned long) ex_hdr.response,
424                                (hdr.out_words + ex_hdr.provider_out_words) * 4))
425                         return -EFAULT;
426         } else {
427                 if (hdr.out_words || ex_hdr.provider_out_words)
428                         return -EINVAL;
429         }
430
431         ptr = (struct ib_uverbs_create_flow *)tmpbuf;
432         ptr->comp_mask = 0;     /* user input already validated above */
433         ptr->qp_handle = ex_hdr.qp_handle;
434
435         if ((count-36) > 120)
436                 BUG();
437
438         /* Copy 16 bytes worth "struct ibv_kern_flow_attr" */
439         copy_from_user(&tmpbuf[1], buf+36, sizeof(struct ib_uverbs_flow_attr));
440
441         ptr->flow_attr.size -= 56;              /* Comes in as 96 = 56 + 40 */
442
443         /* Copy "struct ibv_kern_spec"s */
444         copy_from_user(&tmpbuf[3], buf+56, count-56);
445
446         /*
447          * Copy : count-56 "struct ibv_kern_spec"s,
448          * 16 bytes "struct ibv_kern_flow_attr", 16 bytes comp_mask/qp_handle.
449          */
450         copy_to_user((char __user *)buf, tmpbuf, count-24);
451
452         INIT_UDATA_BUF_OR_NULL(&ucore, buf,
453             (unsigned long) ex_hdr.response, count - 24,
454             hdr.out_words * 4);
455
456         err = ib_uverbs_ex_create_flow(file, &ucore, &uhw);
457
458 next:
459         if (err)
460                 return err;
461
462         return count;
463 }
464
465 static ssize_t compat(struct ib_uverbs_file *file, size_t count,
466     const char __user *buf)
467 {
468         unsigned long                   tmpbuf[17];
469         struct ib_uverbs_cmd_hdr        *p = (struct ib_uverbs_cmd_hdr *)tmpbuf;
470         char __user                     *dst = (char __user *)buf;
471         int                             insz, outsz;
472
473         /*
474          * User "struct ibv_qp_dest" is 40 bytes, passes in 136 bytes.
475          * Kernel "struct ib_uverbs_qp_dest" is 32 bytes, expects 120.
476          * Last 8 bytes of user "struct ibv_qp_dest" not used by kernel.
477          * Kernel expects this layout:
478          *      struct ib_uverbs_cmd_hdr (8)
479          *      struct ib_uverbs_qp_dest (32 <- 40)
480          *      struct ib_uverbs_qp_dest (32 <- 40)
481          *      Rest of qp_mod inputs    (48)
482          */
483
484         if (count > 136)
485                 BUG();
486
487         if (copy_from_user(tmpbuf, buf, count))
488                 return -EFAULT;
489         insz = p->in_words * 4;
490         outsz = p->out_words * 4;
491
492         copy_to_user(dst, &tmpbuf[1], sizeof(struct ib_uverbs_qp_dest));
493         dst += sizeof(struct ib_uverbs_qp_dest);
494         copy_to_user(dst, &tmpbuf[6], sizeof(struct ib_uverbs_qp_dest));
495         dst += sizeof(struct ib_uverbs_qp_dest);
496         copy_to_user(dst, &tmpbuf[11], 48);
497         
498
499         return ib_uverbs_modify_qp(file, buf, insz, outsz);
500 }
501
502 /*
503  * Compat hack for applications/libraries we care about. Retrofit Linux 3.12
504  * style APIs.
505  */
506 ssize_t check_old_abi(struct file *filp, const char __user *buf, size_t count)
507 {
508         struct ib_uverbs_cmd_hdr hdr;
509         int                      tmp;
510         struct ib_uverbs_file *file = filp->private_data;
511
512         if (copy_from_user(&hdr, buf, sizeof hdr))
513                 return -EFAULT;
514
515         tmp = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
516         if ((tmp >= 52) && (tmp <= 53)) {
517                 return compat_ex(file, count, buf);
518         } else if (tmp == IB_USER_VERBS_CMD_MODIFY_QP) {
519                 return compat(file, count, buf);
520         } else if (tmp == IB_USER_VERBS_CMD_QUERY_QP) {
521                 panic("query_qp API difference not handled\n");
522         }
523
524         /* Continue with processing this command */
525         return 0;
526 }
527
528 /* END: Support older version of libibverbs */