Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
csum-wrappers_64.c
Go to the documentation of this file.
1 /*
2  * Copyright 2002, 2003 Andi Kleen, SuSE Labs.
3  * Subject to the GNU Public License v.2
4  *
5  * Wrappers of assembly checksum functions for x86-64.
6  */
7 #include <asm/checksum.h>
8 #include <linux/module.h>
9 
21 __wsum
22 csum_partial_copy_from_user(const void __user *src, void *dst,
23  int len, __wsum isum, int *errp)
24 {
25  might_sleep();
26  *errp = 0;
27 
28  if (!likely(access_ok(VERIFY_READ, src, len)))
29  goto out_err;
30 
31  /*
32  * Why 6, not 7? To handle odd addresses aligned we
33  * would need to do considerable complications to fix the
34  * checksum which is defined as an 16bit accumulator. The
35  * fix alignment code is primarily for performance
36  * compatibility with 32bit and that will handle odd
37  * addresses slowly too.
38  */
39  if (unlikely((unsigned long)src & 6)) {
40  while (((unsigned long)src & 6) && len >= 2) {
41  __u16 val16;
42 
43  *errp = __get_user(val16, (const __u16 __user *)src);
44  if (*errp)
45  return isum;
46 
47  *(__u16 *)dst = val16;
48  isum = (__force __wsum)add32_with_carry(
49  (__force unsigned)isum, val16);
50  src += 2;
51  dst += 2;
52  len -= 2;
53  }
54  }
55  isum = csum_partial_copy_generic((__force const void *)src,
56  dst, len, isum, errp, NULL);
57  if (unlikely(*errp))
58  goto out_err;
59 
60  return isum;
61 
62 out_err:
63  *errp = -EFAULT;
64  memset(dst, 0, len);
65 
66  return isum;
67 }
69 
81 __wsum
82 csum_partial_copy_to_user(const void *src, void __user *dst,
83  int len, __wsum isum, int *errp)
84 {
85  might_sleep();
86 
87  if (unlikely(!access_ok(VERIFY_WRITE, dst, len))) {
88  *errp = -EFAULT;
89  return 0;
90  }
91 
92  if (unlikely((unsigned long)dst & 6)) {
93  while (((unsigned long)dst & 6) && len >= 2) {
94  __u16 val16 = *(__u16 *)src;
95 
96  isum = (__force __wsum)add32_with_carry(
97  (__force unsigned)isum, val16);
98  *errp = __put_user(val16, (__u16 __user *)dst);
99  if (*errp)
100  return isum;
101  src += 2;
102  dst += 2;
103  len -= 2;
104  }
105  }
106 
107  *errp = 0;
108  return csum_partial_copy_generic(src, (void __force *)dst,
109  len, isum, NULL, errp);
110 }
111 EXPORT_SYMBOL(csum_partial_copy_to_user);
112 
122 __wsum
123 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
124 {
125  return csum_partial_copy_generic(src, dst, len, sum, NULL, NULL);
126 }
128 
130  const struct in6_addr *daddr,
131  __u32 len, unsigned short proto, __wsum sum)
132 {
133  __u64 rest, sum64;
134 
135  rest = (__force __u64)htonl(len) + (__force __u64)htons(proto) +
136  (__force __u64)sum;
137 
138  asm(" addq (%[saddr]),%[sum]\n"
139  " adcq 8(%[saddr]),%[sum]\n"
140  " adcq (%[daddr]),%[sum]\n"
141  " adcq 8(%[daddr]),%[sum]\n"
142  " adcq $0,%[sum]\n"
143 
144  : [sum] "=r" (sum64)
145  : "[sum]" (rest), [saddr] "r" (saddr), [daddr] "r" (daddr));
146 
147  return csum_fold(
148  (__force __wsum)add32_with_carry(sum64 & 0xffffffff, sum64>>32));
149 }