diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-06-17 02:59:01 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-17 02:59:10 -0400 |
commit | cc4949e1fdade5d063e9f8783cf0e2cc92041ce5 (patch) | |
tree | 4023bd641bfe464efbde518fb504d6865c9df014 /lib | |
parent | 28b4868820a56de661f54742ff91b78e12f1e582 (diff) | |
parent | 300df7dc89cc276377fc020704e34875d5c473b6 (diff) |
Merge branch 'linus' into x86/urgent
Merge reason: pull in latest to fix a bug in it.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Kconfig | 6 | ||||
-rw-r--r-- | lib/Makefile | 4 | ||||
-rw-r--r-- | lib/atomic64.c | 175 | ||||
-rw-r--r-- | lib/checksum.c | 193 | ||||
-rw-r--r-- | lib/extable.c | 21 |
5 files changed, 398 insertions, 1 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index 9960be04cbbe..bb1326d3839c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig | |||
@@ -194,4 +194,10 @@ config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS | |||
194 | config NLATTR | 194 | config NLATTR |
195 | bool | 195 | bool |
196 | 196 | ||
197 | # | ||
198 | # Generic 64-bit atomic support is selected if needed | ||
199 | # | ||
200 | config GENERIC_ATOMIC64 | ||
201 | bool | ||
202 | |||
197 | endmenu | 203 | endmenu |
diff --git a/lib/Makefile b/lib/Makefile index 1f6edefebffe..8e9bcf9d3261 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -93,6 +93,10 @@ obj-$(CONFIG_NLATTR) += nlattr.o | |||
93 | 93 | ||
94 | obj-$(CONFIG_DMA_API_DEBUG) += dma-debug.o | 94 | obj-$(CONFIG_DMA_API_DEBUG) += dma-debug.o |
95 | 95 | ||
96 | obj-$(CONFIG_GENERIC_CSUM) += checksum.o | ||
97 | |||
98 | obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o | ||
99 | |||
96 | hostprogs-y := gen_crc32table | 100 | hostprogs-y := gen_crc32table |
97 | clean-files := crc32table.h | 101 | clean-files := crc32table.h |
98 | 102 | ||
diff --git a/lib/atomic64.c b/lib/atomic64.c new file mode 100644 index 000000000000..c5e725562416 --- /dev/null +++ b/lib/atomic64.c | |||
@@ -0,0 +1,175 @@ | |||
1 | /* | ||
2 | * Generic implementation of 64-bit atomics using spinlocks, | ||
3 | * useful on processors that don't have 64-bit atomic instructions. | ||
4 | * | ||
5 | * Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/cache.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <asm/atomic.h> | ||
17 | |||
18 | /* | ||
19 | * We use a hashed array of spinlocks to provide exclusive access | ||
20 | * to each atomic64_t variable. Since this is expected to used on | ||
21 | * systems with small numbers of CPUs (<= 4 or so), we use a | ||
22 | * relatively small array of 16 spinlocks to avoid wasting too much | ||
23 | * memory on the spinlock array. | ||
24 | */ | ||
25 | #define NR_LOCKS 16 | ||
26 | |||
27 | /* | ||
28 | * Ensure each lock is in a separate cacheline. | ||
29 | */ | ||
30 | static union { | ||
31 | spinlock_t lock; | ||
32 | char pad[L1_CACHE_BYTES]; | ||
33 | } atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp; | ||
34 | |||
35 | static inline spinlock_t *lock_addr(const atomic64_t *v) | ||
36 | { | ||
37 | unsigned long addr = (unsigned long) v; | ||
38 | |||
39 | addr >>= L1_CACHE_SHIFT; | ||
40 | addr ^= (addr >> 8) ^ (addr >> 16); | ||
41 | return &atomic64_lock[addr & (NR_LOCKS - 1)].lock; | ||
42 | } | ||
43 | |||
44 | long long atomic64_read(const atomic64_t *v) | ||
45 | { | ||
46 | unsigned long flags; | ||
47 | spinlock_t *lock = lock_addr(v); | ||
48 | long long val; | ||
49 | |||
50 | spin_lock_irqsave(lock, flags); | ||
51 | val = v->counter; | ||
52 | spin_unlock_irqrestore(lock, flags); | ||
53 | return val; | ||
54 | } | ||
55 | |||
56 | void atomic64_set(atomic64_t *v, long long i) | ||
57 | { | ||
58 | unsigned long flags; | ||
59 | spinlock_t *lock = lock_addr(v); | ||
60 | |||
61 | spin_lock_irqsave(lock, flags); | ||
62 | v->counter = i; | ||
63 | spin_unlock_irqrestore(lock, flags); | ||
64 | } | ||
65 | |||
66 | void atomic64_add(long long a, atomic64_t *v) | ||
67 | { | ||
68 | unsigned long flags; | ||
69 | spinlock_t *lock = lock_addr(v); | ||
70 | |||
71 | spin_lock_irqsave(lock, flags); | ||
72 | v->counter += a; | ||
73 | spin_unlock_irqrestore(lock, flags); | ||
74 | } | ||
75 | |||
76 | long long atomic64_add_return(long long a, atomic64_t *v) | ||
77 | { | ||
78 | unsigned long flags; | ||
79 | spinlock_t *lock = lock_addr(v); | ||
80 | long long val; | ||
81 | |||
82 | spin_lock_irqsave(lock, flags); | ||
83 | val = v->counter += a; | ||
84 | spin_unlock_irqrestore(lock, flags); | ||
85 | return val; | ||
86 | } | ||
87 | |||
88 | void atomic64_sub(long long a, atomic64_t *v) | ||
89 | { | ||
90 | unsigned long flags; | ||
91 | spinlock_t *lock = lock_addr(v); | ||
92 | |||
93 | spin_lock_irqsave(lock, flags); | ||
94 | v->counter -= a; | ||
95 | spin_unlock_irqrestore(lock, flags); | ||
96 | } | ||
97 | |||
98 | long long atomic64_sub_return(long long a, atomic64_t *v) | ||
99 | { | ||
100 | unsigned long flags; | ||
101 | spinlock_t *lock = lock_addr(v); | ||
102 | long long val; | ||
103 | |||
104 | spin_lock_irqsave(lock, flags); | ||
105 | val = v->counter -= a; | ||
106 | spin_unlock_irqrestore(lock, flags); | ||
107 | return val; | ||
108 | } | ||
109 | |||
110 | long long atomic64_dec_if_positive(atomic64_t *v) | ||
111 | { | ||
112 | unsigned long flags; | ||
113 | spinlock_t *lock = lock_addr(v); | ||
114 | long long val; | ||
115 | |||
116 | spin_lock_irqsave(lock, flags); | ||
117 | val = v->counter - 1; | ||
118 | if (val >= 0) | ||
119 | v->counter = val; | ||
120 | spin_unlock_irqrestore(lock, flags); | ||
121 | return val; | ||
122 | } | ||
123 | |||
124 | long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n) | ||
125 | { | ||
126 | unsigned long flags; | ||
127 | spinlock_t *lock = lock_addr(v); | ||
128 | long long val; | ||
129 | |||
130 | spin_lock_irqsave(lock, flags); | ||
131 | val = v->counter; | ||
132 | if (val == o) | ||
133 | v->counter = n; | ||
134 | spin_unlock_irqrestore(lock, flags); | ||
135 | return val; | ||
136 | } | ||
137 | |||
138 | long long atomic64_xchg(atomic64_t *v, long long new) | ||
139 | { | ||
140 | unsigned long flags; | ||
141 | spinlock_t *lock = lock_addr(v); | ||
142 | long long val; | ||
143 | |||
144 | spin_lock_irqsave(lock, flags); | ||
145 | val = v->counter; | ||
146 | v->counter = new; | ||
147 | spin_unlock_irqrestore(lock, flags); | ||
148 | return val; | ||
149 | } | ||
150 | |||
151 | int atomic64_add_unless(atomic64_t *v, long long a, long long u) | ||
152 | { | ||
153 | unsigned long flags; | ||
154 | spinlock_t *lock = lock_addr(v); | ||
155 | int ret = 1; | ||
156 | |||
157 | spin_lock_irqsave(lock, flags); | ||
158 | if (v->counter != u) { | ||
159 | v->counter += a; | ||
160 | ret = 0; | ||
161 | } | ||
162 | spin_unlock_irqrestore(lock, flags); | ||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | static int init_atomic64_lock(void) | ||
167 | { | ||
168 | int i; | ||
169 | |||
170 | for (i = 0; i < NR_LOCKS; ++i) | ||
171 | spin_lock_init(&atomic64_lock[i].lock); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | pure_initcall(init_atomic64_lock); | ||
diff --git a/lib/checksum.c b/lib/checksum.c new file mode 100644 index 000000000000..12e5a1c91cda --- /dev/null +++ b/lib/checksum.c | |||
@@ -0,0 +1,193 @@ | |||
1 | /* | ||
2 | * | ||
3 | * INET An implementation of the TCP/IP protocol suite for the LINUX | ||
4 | * operating system. INET is implemented using the BSD Socket | ||
5 | * interface as the means of communication with the user level. | ||
6 | * | ||
7 | * IP/TCP/UDP checksumming routines | ||
8 | * | ||
9 | * Authors: Jorge Cwik, <jorge@laser.satlink.net> | ||
10 | * Arnt Gulbrandsen, <agulbra@nvg.unit.no> | ||
11 | * Tom May, <ftom@netcom.com> | ||
12 | * Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de> | ||
13 | * Lots of code moved from tcp.c and ip.c; see those files | ||
14 | * for more names. | ||
15 | * | ||
16 | * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek: | ||
17 | * Fixed some nasty bugs, causing some horrible crashes. | ||
18 | * A: At some points, the sum (%0) was used as | ||
19 | * length-counter instead of the length counter | ||
20 | * (%1). Thanks to Roman Hodek for pointing this out. | ||
21 | * B: GCC seems to mess up if one uses too many | ||
22 | * data-registers to hold input values and one tries to | ||
23 | * specify d0 and d1 as scratch registers. Letting gcc | ||
24 | * choose these registers itself solves the problem. | ||
25 | * | ||
26 | * This program is free software; you can redistribute it and/or | ||
27 | * modify it under the terms of the GNU General Public License | ||
28 | * as published by the Free Software Foundation; either version | ||
29 | * 2 of the License, or (at your option) any later version. | ||
30 | */ | ||
31 | |||
32 | /* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access | ||
33 | kills, so most of the assembly has to go. */ | ||
34 | |||
35 | #include <linux/module.h> | ||
36 | #include <net/checksum.h> | ||
37 | |||
38 | #include <asm/byteorder.h> | ||
39 | |||
40 | static inline unsigned short from32to16(unsigned long x) | ||
41 | { | ||
42 | /* add up 16-bit and 16-bit for 16+c bit */ | ||
43 | x = (x & 0xffff) + (x >> 16); | ||
44 | /* add up carry.. */ | ||
45 | x = (x & 0xffff) + (x >> 16); | ||
46 | return x; | ||
47 | } | ||
48 | |||
49 | static unsigned int do_csum(const unsigned char *buff, int len) | ||
50 | { | ||
51 | int odd, count; | ||
52 | unsigned long result = 0; | ||
53 | |||
54 | if (len <= 0) | ||
55 | goto out; | ||
56 | odd = 1 & (unsigned long) buff; | ||
57 | if (odd) { | ||
58 | result = *buff; | ||
59 | len--; | ||
60 | buff++; | ||
61 | } | ||
62 | count = len >> 1; /* nr of 16-bit words.. */ | ||
63 | if (count) { | ||
64 | if (2 & (unsigned long) buff) { | ||
65 | result += *(unsigned short *) buff; | ||
66 | count--; | ||
67 | len -= 2; | ||
68 | buff += 2; | ||
69 | } | ||
70 | count >>= 1; /* nr of 32-bit words.. */ | ||
71 | if (count) { | ||
72 | unsigned long carry = 0; | ||
73 | do { | ||
74 | unsigned long w = *(unsigned long *) buff; | ||
75 | count--; | ||
76 | buff += 4; | ||
77 | result += carry; | ||
78 | result += w; | ||
79 | carry = (w > result); | ||
80 | } while (count); | ||
81 | result += carry; | ||
82 | result = (result & 0xffff) + (result >> 16); | ||
83 | } | ||
84 | if (len & 2) { | ||
85 | result += *(unsigned short *) buff; | ||
86 | buff += 2; | ||
87 | } | ||
88 | } | ||
89 | if (len & 1) | ||
90 | result += (*buff << 8); | ||
91 | result = from32to16(result); | ||
92 | if (odd) | ||
93 | result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); | ||
94 | out: | ||
95 | return result; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * This is a version of ip_compute_csum() optimized for IP headers, | ||
100 | * which always checksum on 4 octet boundaries. | ||
101 | */ | ||
102 | __sum16 ip_fast_csum(const void *iph, unsigned int ihl) | ||
103 | { | ||
104 | return (__force __sum16)~do_csum(iph, ihl*4); | ||
105 | } | ||
106 | EXPORT_SYMBOL(ip_fast_csum); | ||
107 | |||
108 | /* | ||
109 | * computes the checksum of a memory block at buff, length len, | ||
110 | * and adds in "sum" (32-bit) | ||
111 | * | ||
112 | * returns a 32-bit number suitable for feeding into itself | ||
113 | * or csum_tcpudp_magic | ||
114 | * | ||
115 | * this function must be called with even lengths, except | ||
116 | * for the last fragment, which may be odd | ||
117 | * | ||
118 | * it's best to have buff aligned on a 32-bit boundary | ||
119 | */ | ||
120 | __wsum csum_partial(const void *buff, int len, __wsum wsum) | ||
121 | { | ||
122 | unsigned int sum = (__force unsigned int)wsum; | ||
123 | unsigned int result = do_csum(buff, len); | ||
124 | |||
125 | /* add in old sum, and carry.. */ | ||
126 | result += sum; | ||
127 | if (sum > result) | ||
128 | result += 1; | ||
129 | return (__force __wsum)result; | ||
130 | } | ||
131 | EXPORT_SYMBOL(csum_partial); | ||
132 | |||
133 | /* | ||
134 | * this routine is used for miscellaneous IP-like checksums, mainly | ||
135 | * in icmp.c | ||
136 | */ | ||
137 | __sum16 ip_compute_csum(const void *buff, int len) | ||
138 | { | ||
139 | return (__force __sum16)~do_csum(buff, len); | ||
140 | } | ||
141 | EXPORT_SYMBOL(ip_compute_csum); | ||
142 | |||
143 | /* | ||
144 | * copy from fs while checksumming, otherwise like csum_partial | ||
145 | */ | ||
146 | __wsum | ||
147 | csum_partial_copy_from_user(const void __user *src, void *dst, int len, | ||
148 | __wsum sum, int *csum_err) | ||
149 | { | ||
150 | int missing; | ||
151 | |||
152 | missing = __copy_from_user(dst, src, len); | ||
153 | if (missing) { | ||
154 | memset(dst + len - missing, 0, missing); | ||
155 | *csum_err = -EFAULT; | ||
156 | } else | ||
157 | *csum_err = 0; | ||
158 | |||
159 | return csum_partial(dst, len, sum); | ||
160 | } | ||
161 | EXPORT_SYMBOL(csum_partial_copy_from_user); | ||
162 | |||
163 | /* | ||
164 | * copy from ds while checksumming, otherwise like csum_partial | ||
165 | */ | ||
166 | __wsum | ||
167 | csum_partial_copy(const void *src, void *dst, int len, __wsum sum) | ||
168 | { | ||
169 | memcpy(dst, src, len); | ||
170 | return csum_partial(dst, len, sum); | ||
171 | } | ||
172 | EXPORT_SYMBOL(csum_partial_copy); | ||
173 | |||
174 | #ifndef csum_tcpudp_nofold | ||
175 | __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, | ||
176 | unsigned short len, | ||
177 | unsigned short proto, | ||
178 | __wsum sum) | ||
179 | { | ||
180 | unsigned long long s = (__force u32)sum; | ||
181 | |||
182 | s += (__force u32)saddr; | ||
183 | s += (__force u32)daddr; | ||
184 | #ifdef __BIG_ENDIAN | ||
185 | s += proto + len; | ||
186 | #else | ||
187 | s += (proto + len) << 8; | ||
188 | #endif | ||
189 | s += (s >> 32); | ||
190 | return (__force __wsum)s; | ||
191 | } | ||
192 | EXPORT_SYMBOL(csum_tcpudp_nofold); | ||
193 | #endif | ||
diff --git a/lib/extable.c b/lib/extable.c index 179c08745595..4cac81ec225e 100644 --- a/lib/extable.c +++ b/lib/extable.c | |||
@@ -39,7 +39,26 @@ void sort_extable(struct exception_table_entry *start, | |||
39 | sort(start, finish - start, sizeof(struct exception_table_entry), | 39 | sort(start, finish - start, sizeof(struct exception_table_entry), |
40 | cmp_ex, NULL); | 40 | cmp_ex, NULL); |
41 | } | 41 | } |
42 | #endif | 42 | |
43 | #ifdef CONFIG_MODULES | ||
44 | /* | ||
45 | * If the exception table is sorted, any referring to the module init | ||
46 | * will be at the beginning or the end. | ||
47 | */ | ||
48 | void trim_init_extable(struct module *m) | ||
49 | { | ||
50 | /*trim the beginning*/ | ||
51 | while (m->num_exentries && within_module_init(m->extable[0].insn, m)) { | ||
52 | m->extable++; | ||
53 | m->num_exentries--; | ||
54 | } | ||
55 | /*trim the end*/ | ||
56 | while (m->num_exentries && | ||
57 | within_module_init(m->extable[m->num_exentries-1].insn, m)) | ||
58 | m->num_exentries--; | ||
59 | } | ||
60 | #endif /* CONFIG_MODULES */ | ||
61 | #endif /* !ARCH_HAS_SORT_EXTABLE */ | ||
43 | 62 | ||
44 | #ifndef ARCH_HAS_SEARCH_EXTABLE | 63 | #ifndef ARCH_HAS_SEARCH_EXTABLE |
45 | /* | 64 | /* |