Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.15-rc3 239 lines 5.3 kB view raw
1/* 2 * iovec manipulation routines. 3 * 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 8 * 2 of the License, or (at your option) any later version. 9 * 10 * Fixes: 11 * Andrew Lunn : Errors in iovec copying. 12 * Pedro Roque : Added memcpy_fromiovecend and 13 * csum_..._fromiovecend. 14 * Andi Kleen : fixed error handling for 2.1 15 * Alexey Kuznetsov: 2.1 optimisations 16 * Andi Kleen : Fix csum*fromiovecend for IPv6. 17 */ 18 19#include <linux/errno.h> 20#include <linux/module.h> 21#include <linux/kernel.h> 22#include <linux/mm.h> 23#include <linux/net.h> 24#include <linux/in6.h> 25#include <asm/uaccess.h> 26#include <asm/byteorder.h> 27#include <net/checksum.h> 28#include <net/sock.h> 29 30/* 31 * Verify iovec. The caller must ensure that the iovec is big enough 32 * to hold the message iovec. 33 * 34 * Save time not doing access_ok. copy_*_user will make this work 35 * in any case. 36 */ 37 38int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode) 39{ 40 int size, ct, err; 41 42 if (m->msg_namelen) { 43 if (mode == VERIFY_READ) { 44 void __user *namep; 45 namep = (void __user __force *) m->msg_name; 46 err = move_addr_to_kernel(namep, m->msg_namelen, 47 address); 48 if (err < 0) 49 return err; 50 } 51 if (m->msg_name) 52 m->msg_name = address; 53 } else { 54 m->msg_name = NULL; 55 } 56 57 size = m->msg_iovlen * sizeof(struct iovec); 58 if (copy_from_user(iov, (void __user __force *) m->msg_iov, size)) 59 return -EFAULT; 60 61 m->msg_iov = iov; 62 err = 0; 63 64 for (ct = 0; ct < m->msg_iovlen; ct++) { 65 size_t len = iov[ct].iov_len; 66 67 if (len > INT_MAX - err) { 68 len = INT_MAX - err; 69 iov[ct].iov_len = len; 70 } 71 err += len; 72 } 73 74 return err; 75} 76 77/* 78 * Copy kernel to iovec. Returns -EFAULT on error. 79 */ 80 81int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, 82 int offset, int len) 83{ 84 int copy; 85 for (; len > 0; ++iov) { 86 /* Skip over the finished iovecs */ 87 if (unlikely(offset >= iov->iov_len)) { 88 offset -= iov->iov_len; 89 continue; 90 } 91 copy = min_t(unsigned int, iov->iov_len - offset, len); 92 if (copy_to_user(iov->iov_base + offset, kdata, copy)) 93 return -EFAULT; 94 offset = 0; 95 kdata += copy; 96 len -= copy; 97 } 98 99 return 0; 100} 101EXPORT_SYMBOL(memcpy_toiovecend); 102 103/* 104 * Copy iovec to kernel. Returns -EFAULT on error. 105 */ 106 107int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, 108 int offset, int len) 109{ 110 /* Skip over the finished iovecs */ 111 while (offset >= iov->iov_len) { 112 offset -= iov->iov_len; 113 iov++; 114 } 115 116 while (len > 0) { 117 u8 __user *base = iov->iov_base + offset; 118 int copy = min_t(unsigned int, len, iov->iov_len - offset); 119 120 offset = 0; 121 if (copy_from_user(kdata, base, copy)) 122 return -EFAULT; 123 len -= copy; 124 kdata += copy; 125 iov++; 126 } 127 128 return 0; 129} 130EXPORT_SYMBOL(memcpy_fromiovecend); 131 132/* 133 * And now for the all-in-one: copy and checksum from a user iovec 134 * directly to a datagram 135 * Calls to csum_partial but the last must be in 32 bit chunks 136 * 137 * ip_build_xmit must ensure that when fragmenting only the last 138 * call to this function will be unaligned also. 139 */ 140int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, 141 int offset, unsigned int len, __wsum *csump) 142{ 143 __wsum csum = *csump; 144 int partial_cnt = 0, err = 0; 145 146 /* Skip over the finished iovecs */ 147 while (offset >= iov->iov_len) { 148 offset -= iov->iov_len; 149 iov++; 150 } 151 152 while (len > 0) { 153 u8 __user *base = iov->iov_base + offset; 154 int copy = min_t(unsigned int, len, iov->iov_len - offset); 155 156 offset = 0; 157 158 /* There is a remnant from previous iov. */ 159 if (partial_cnt) { 160 int par_len = 4 - partial_cnt; 161 162 /* iov component is too short ... */ 163 if (par_len > copy) { 164 if (copy_from_user(kdata, base, copy)) 165 goto out_fault; 166 kdata += copy; 167 base += copy; 168 partial_cnt += copy; 169 len -= copy; 170 iov++; 171 if (len) 172 continue; 173 *csump = csum_partial(kdata - partial_cnt, 174 partial_cnt, csum); 175 goto out; 176 } 177 if (copy_from_user(kdata, base, par_len)) 178 goto out_fault; 179 csum = csum_partial(kdata - partial_cnt, 4, csum); 180 kdata += par_len; 181 base += par_len; 182 copy -= par_len; 183 len -= par_len; 184 partial_cnt = 0; 185 } 186 187 if (len > copy) { 188 partial_cnt = copy % 4; 189 if (partial_cnt) { 190 copy -= partial_cnt; 191 if (copy_from_user(kdata + copy, base + copy, 192 partial_cnt)) 193 goto out_fault; 194 } 195 } 196 197 if (copy) { 198 csum = csum_and_copy_from_user(base, kdata, copy, 199 csum, &err); 200 if (err) 201 goto out; 202 } 203 len -= copy + partial_cnt; 204 kdata += copy + partial_cnt; 205 iov++; 206 } 207 *csump = csum; 208out: 209 return err; 210 211out_fault: 212 err = -EFAULT; 213 goto out; 214} 215EXPORT_SYMBOL(csum_partial_copy_fromiovecend); 216 217unsigned long iov_pages(const struct iovec *iov, int offset, 218 unsigned long nr_segs) 219{ 220 unsigned long seg, base; 221 int pages = 0, len, size; 222 223 while (nr_segs && (offset >= iov->iov_len)) { 224 offset -= iov->iov_len; 225 ++iov; 226 --nr_segs; 227 } 228 229 for (seg = 0; seg < nr_segs; seg++) { 230 base = (unsigned long)iov[seg].iov_base + offset; 231 len = iov[seg].iov_len - offset; 232 size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; 233 pages += size; 234 offset = 0; 235 } 236 237 return pages; 238} 239EXPORT_SYMBOL(iov_pages);