zero copy functions separated from rest.
[cryptodev-linux.git] / zc.c
blob906dd060402bf04535eb0a341c9b10c75836ddaf
1 /*
2 * Driver for /dev/crypto device (aka CryptoDev)
4 * Copyright (c) 2009-2011 Nikos Mavrogiannopoulos <nmav@gnutls.org>
5 * Copyright (c) 2010 Phil Sutter
7 * This file is part of linux cryptodev.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
25 #include <crypto/hash.h>
26 #include <linux/crypto.h>
27 #include <linux/mm.h>
28 #include <linux/highmem.h>
29 #include <linux/ioctl.h>
30 #include <linux/random.h>
31 #include <linux/syscalls.h>
32 #include <linux/pagemap.h>
33 #include <linux/uaccess.h>
34 #include <crypto/scatterwalk.h>
35 #include <linux/scatterlist.h>
36 #include "cryptodev_int.h"
37 #include "version.h"
39 /* Helper functions to assist zero copy.
40 * This needs to be redesigned and moved out of the session. --nmav
43 /* offset of buf in it's first page */
44 #define PAGEOFFSET(buf) ((unsigned long)buf & ~PAGE_MASK)
46 /* fetch the pages addr resides in into pg and initialise sg with them */
47 int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
48 int pgcount, struct page **pg, struct scatterlist *sg,
49 struct task_struct *task, struct mm_struct *mm)
51 int ret, pglen, i = 0;
52 struct scatterlist *sgp;
54 down_write(&mm->mmap_sem);
55 ret = get_user_pages(task, mm,
56 (unsigned long)addr, pgcount, write, 0, pg, NULL);
57 up_write(&mm->mmap_sem);
58 if (ret != pgcount)
59 return -EINVAL;
61 sg_init_table(sg, pgcount);
63 pglen = min((ptrdiff_t)(PAGE_SIZE - PAGEOFFSET(addr)), (ptrdiff_t)len);
64 sg_set_page(sg, pg[i++], pglen, PAGEOFFSET(addr));
66 len -= pglen;
67 for (sgp = sg_next(sg); len; sgp = sg_next(sgp)) {
68 pglen = min((uint32_t)PAGE_SIZE, len);
69 sg_set_page(sgp, pg[i++], pglen, 0);
70 len -= pglen;
72 sg_mark_end(sg_last(sg, pgcount));
73 return 0;
76 int adjust_sg_array(struct csession * ses, int pagecount)
78 struct scatterlist *sg;
79 struct page **pages;
80 int array_size;
82 for (array_size = ses->array_size; array_size < pagecount;
83 array_size *= 2)
86 dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n",
87 __func__, array_size);
88 pages = krealloc(ses->pages, array_size * sizeof(struct page *),
89 GFP_KERNEL);
90 if (unlikely(!pages))
91 return -ENOMEM;
92 ses->pages = pages;
93 sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist),
94 GFP_KERNEL);
95 if (unlikely(!sg))
96 return -ENOMEM;
97 ses->sg = sg;
98 ses->array_size = array_size;
100 return 0;
103 void release_user_pages(struct page **pg, int pagecount)
105 while (pagecount--) {
106 if (!PageReserved(pg[pagecount]))
107 SetPageDirty(pg[pagecount]);
108 page_cache_release(pg[pagecount]);