diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Kconfig | 8 | ||||
-rw-r--r-- | lib/Kconfig.debug | 13 | ||||
-rw-r--r-- | lib/Makefile | 3 | ||||
-rw-r--r-- | lib/bug.c | 5 | ||||
-rw-r--r-- | lib/check_signature.c | 26 | ||||
-rw-r--r-- | lib/crc7.c | 68 | ||||
-rw-r--r-- | lib/genalloc.c | 3 | ||||
-rw-r--r-- | lib/idr.c | 102 | ||||
-rw-r--r-- | lib/percpu_counter.c | 68 | ||||
-rw-r--r-- | lib/vsprintf.c | 173 |
10 files changed, 436 insertions, 33 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index 3eb29d5dc4f..e5c2c514174 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -41,6 +41,14 @@ config CRC32 kernel tree does. Such modules that use library CRC32 functions require M here. +config CRC7 + tristate "CRC7 functions" + help + This option is provided for the case where no in-kernel-tree + modules require CRC7 functions, but a module built outside + the kernel tree does. Such modules that use library CRC7 + functions require M here. + config LIBCRC32C tristate "CRC32c (Castagnoli, et al) Cyclic Redundancy-Check" help diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index fab32a28637..640844024ff 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -152,6 +152,19 @@ config DEBUG_SLAB_LEAK bool "Memory leak debugging" depends on DEBUG_SLAB +config SLUB_DEBUG_ON + bool "SLUB debugging on by default" + depends on SLUB && SLUB_DEBUG + default n + help + Boot with debugging on by default. SLUB boots by default with + the runtime debug capabilities switched off. Enabling this is + equivalent to specifying the "slub_debug" parameter on boot. + There is no support for more fine grained debug control like + possible with slub_debug=xxx. SLUB debugging may be switched + off in a kernel built with CONFIG_SLUB_DEBUG_ON by specifying + "slub_debug=-". + config DEBUG_PREEMPT bool "Debug preemptible kernel" depends on DEBUG_KERNEL && PREEMPT && TRACE_IRQFLAGS_SUPPORT diff --git a/lib/Makefile b/lib/Makefile index d1b366bdf86..da68b2ca060 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -21,7 +21,7 @@ CFLAGS_kobject_uevent.o += -DDEBUG endif obj-$(CONFIG_GENERIC_IOMAP) += iomap.o -obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o +obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o check_signature.o obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o @@ -43,6 +43,7 @@ obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o obj-$(CONFIG_CRC16) += crc16.o obj-$(CONFIG_CRC_ITU_T) += crc-itu-t.o obj-$(CONFIG_CRC32) += crc32.o +obj-$(CONFIG_CRC7) += crc7.o obj-$(CONFIG_LIBCRC32C) += libcrc32c.o obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o diff --git a/lib/bug.c b/lib/bug.c index 014b582c5c4..530f38f5578 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -38,6 +38,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/bug.h> +#include <linux/sched.h> extern const struct bug_entry __start___bug_table[], __stop___bug_table[]; @@ -112,7 +113,7 @@ const struct bug_entry *find_bug(unsigned long bugaddr) return module_find_bug(bugaddr); } -enum bug_trap_type report_bug(unsigned long bugaddr) +enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) { const struct bug_entry *bug; const char *file; @@ -147,7 +148,7 @@ enum bug_trap_type report_bug(unsigned long bugaddr) "[verbose debug info unavailable]\n", (void *)bugaddr); - dump_stack(); + show_regs(regs); return BUG_TRAP_TYPE_WARN; } diff --git a/lib/check_signature.c b/lib/check_signature.c new file mode 100644 index 00000000000..fd6af199247 --- /dev/null +++ b/lib/check_signature.c @@ -0,0 +1,26 @@ +#include <linux/io.h> +#include <linux/module.h> + +/** + * check_signature - find BIOS signatures + * @io_addr: mmio address to check + * @signature: signature block + * @length: length of signature + * + * Perform a signature comparison with the mmio address io_addr. This + * address should have been obtained by ioremap. + * Returns 1 on a match. + */ + +int check_signature(const volatile void __iomem *io_addr, + const unsigned char *signature, int length) +{ + while (length--) { + if (readb(io_addr) != *signature) + return 0; + io_addr++; + signature++; + } + return 1; +} +EXPORT_SYMBOL(check_signature); diff --git a/lib/crc7.c b/lib/crc7.c new file mode 100644 index 00000000000..f1c3a144cec --- /dev/null +++ b/lib/crc7.c @@ -0,0 +1,68 @@ +/* + * crc7.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/crc7.h> + + +/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */ +const u8 crc7_syndrome_table[256] = { + 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, + 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, + 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, + 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, + 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, + 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, + 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, + 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, + 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, + 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, + 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, + 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, + 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, + 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, + 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, + 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, + 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, + 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, + 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, + 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, + 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, + 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, + 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, + 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, + 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, + 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, + 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, + 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, + 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, + 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, + 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, + 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 +}; +EXPORT_SYMBOL(crc7_syndrome_table); + +/** + * crc7 - update the CRC7 for the data buffer + * @crc: previous CRC7 value + * @buffer: data pointer + * @len: number of bytes in the buffer + * Context: any + * + * Returns the updated CRC7 value. + */ +u8 crc7(u8 crc, const u8 *buffer, size_t len) +{ + while (len--) + crc = crc7_byte(crc, *buffer++); + return crc; +} +EXPORT_SYMBOL(crc7); + +MODULE_DESCRIPTION("CRC7 calculations"); +MODULE_LICENSE("GPL"); diff --git a/lib/genalloc.c b/lib/genalloc.c index eb7c2bab9eb..f6d276db2d5 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -54,11 +54,10 @@ int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, int nbytes = sizeof(struct gen_pool_chunk) + (nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE; - chunk = kmalloc_node(nbytes, GFP_KERNEL, nid); + chunk = kmalloc_node(nbytes, GFP_KERNEL | __GFP_ZERO, nid); if (unlikely(chunk == NULL)) return -1; - memset(chunk, 0, nbytes); spin_lock_init(&chunk->lock); chunk->start_addr = addr; chunk->end_addr = addr + size; diff --git a/lib/idr.c b/lib/idr.c index b98f01a2eb9..5ca67b3cfd3 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -391,6 +391,53 @@ void idr_remove(struct idr *idp, int id) EXPORT_SYMBOL(idr_remove); /** + * idr_remove_all - remove all ids from the given idr tree + * @idp: idr handle + * + * idr_destroy() only frees up unused, cached idp_layers, but this + * function will remove all id mappings and leave all idp_layers + * unused. + * + * A typical clean-up sequence for objects stored in an idr tree, will + * use idr_for_each() to free all objects, if necessay, then + * idr_remove_all() to remove all ids, and idr_destroy() to free + * up the cached idr_layers. + */ +void idr_remove_all(struct idr *idp) +{ + int n, id, max, error = 0; + struct idr_layer *p; + struct idr_layer *pa[MAX_LEVEL]; + struct idr_layer **paa = &pa[0]; + + n = idp->layers * IDR_BITS; + p = idp->top; + max = 1 << n; + + id = 0; + while (id < max && !error) { + while (n > IDR_BITS && p) { + n -= IDR_BITS; + *paa++ = p; + p = p->ary[(id >> n) & IDR_MASK]; + } + + id += 1 << n; + while (n < fls(id)) { + if (p) { + memset(p, 0, sizeof *p); + free_layer(idp, p); + } + n += IDR_BITS; + p = *--paa; + } + } + idp->top = NULL; + idp->layers = 0; +} +EXPORT_SYMBOL(idr_remove_all); + +/** * idr_destroy - release all cached layers within an idr tree * idp: idr handle */ @@ -437,6 +484,61 @@ void *idr_find(struct idr *idp, int id) EXPORT_SYMBOL(idr_find); /** + * idr_for_each - iterate through all stored pointers + * @idp: idr handle + * @fn: function to be called for each pointer + * @data: data passed back to callback function + * + * Iterate over the pointers registered with the given idr. The + * callback function will be called for each pointer currently + * registered, passing the id, the pointer and the data pointer passed + * to this function. It is not safe to modify the idr tree while in + * the callback, so functions such as idr_get_new and idr_remove are + * not allowed. + * + * We check the return of @fn each time. If it returns anything other + * than 0, we break out and return that value. + * + * The caller must serialize idr_for_each() vs idr_get_new() and idr_remove(). + */ +int idr_for_each(struct idr *idp, + int (*fn)(int id, void *p, void *data), void *data) +{ + int n, id, max, error = 0; + struct idr_layer *p; + struct idr_layer *pa[MAX_LEVEL]; + struct idr_layer **paa = &pa[0]; + + n = idp->layers * IDR_BITS; + p = idp->top; + max = 1 << n; + + id = 0; + while (id < max) { + while (n > 0 && p) { + n -= IDR_BITS; + *paa++ = p; + p = p->ary[(id >> n) & IDR_MASK]; + } + + if (p) { + error = fn(id, (void *)p, data); + if (error) + break; + } + + id += 1 << n; + while (n < fls(id)) { + n += IDR_BITS; + p = *--paa; + } + } + + return error; +} +EXPORT_SYMBOL(idr_for_each); + +/** * idr_replace - replace pointer for given id * @idp: idr handle * @ptr: pointer you want associated with the id diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index 850449080e1..cf22c617baa 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -3,8 +3,17 @@ */ #include <linux/percpu_counter.h> +#include <linux/notifier.h> +#include <linux/mutex.h> +#include <linux/init.h> +#include <linux/cpu.h> #include <linux/module.h> +#ifdef CONFIG_HOTPLUG_CPU +static LIST_HEAD(percpu_counters); +static DEFINE_MUTEX(percpu_counters_lock); +#endif + void percpu_counter_mod(struct percpu_counter *fbc, s32 amount) { long count; @@ -36,7 +45,7 @@ s64 percpu_counter_sum(struct percpu_counter *fbc) spin_lock(&fbc->lock); ret = fbc->count; - for_each_possible_cpu(cpu) { + for_each_online_cpu(cpu) { s32 *pcount = per_cpu_ptr(fbc->counters, cpu); ret += *pcount; } @@ -44,3 +53,60 @@ s64 percpu_counter_sum(struct percpu_counter *fbc) return ret < 0 ? 0 : ret; } EXPORT_SYMBOL(percpu_counter_sum); + +void percpu_counter_init(struct percpu_counter *fbc, s64 amount) +{ + spin_lock_init(&fbc->lock); + fbc->count = amount; + fbc->counters = alloc_percpu(s32); +#ifdef CONFIG_HOTPLUG_CPU + mutex_lock(&percpu_counters_lock); + list_add(&fbc->list, &percpu_counters); + mutex_unlock(&percpu_counters_lock); +#endif +} +EXPORT_SYMBOL(percpu_counter_init); + +void percpu_counter_destroy(struct percpu_counter *fbc) +{ + free_percpu(fbc->counters); +#ifdef CONFIG_HOTPLUG_CPU + mutex_lock(&percpu_counters_lock); + list_del(&fbc->list); + mutex_unlock(&percpu_counters_lock); +#endif +} +EXPORT_SYMBOL(percpu_counter_destroy); + +#ifdef CONFIG_HOTPLUG_CPU +static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + unsigned int cpu; + struct percpu_counter *fbc; + + if (action != CPU_DEAD) + return NOTIFY_OK; + + cpu = (unsigned long)hcpu; + mutex_lock(&percpu_counters_lock); + list_for_each_entry(fbc, &percpu_counters, list) { + s32 *pcount; + + spin_lock(&fbc->lock); + pcount = per_cpu_ptr(fbc->counters, cpu); + fbc->count += *pcount; + *pcount = 0; + spin_unlock(&fbc->lock); + } + mutex_unlock(&percpu_counters_lock); + return NOTIFY_OK; +} + +static int __init percpu_counter_startup(void) +{ + hotcpu_notifier(percpu_counter_hotcpu_callback, 0); + return 0; +} +module_init(percpu_counter_startup); +#endif diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 01729024126..6b6734df6d2 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -135,6 +135,103 @@ static int skip_atoi(const char **s) return i; } +/* Decimal conversion is by far the most typical, and is used + * for /proc and /sys data. This directly impacts e.g. top performance + * with many processes running. We optimize it for speed + * using code from + * http://www.cs.uiowa.edu/~jones/bcd/decimal.html + * (with permission from the author, Douglas W. Jones). */ + +/* Formats correctly any integer in [0,99999]. + * Outputs from one to five digits depending on input. + * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */ +static char* put_dec_trunc(char *buf, unsigned q) +{ + unsigned d3, d2, d1, d0; + d1 = (q>>4) & 0xf; + d2 = (q>>8) & 0xf; + d3 = (q>>12); + + d0 = 6*(d3 + d2 + d1) + (q & 0xf); + q = (d0 * 0xcd) >> 11; + d0 = d0 - 10*q; + *buf++ = d0 + '0'; /* least significant digit */ + d1 = q + 9*d3 + 5*d2 + d1; + if (d1 != 0) { + q = (d1 * 0xcd) >> 11; + d1 = d1 - 10*q; + *buf++ = d1 + '0'; /* next digit */ + + d2 = q + 2*d2; + if ((d2 != 0) || (d3 != 0)) { + q = (d2 * 0xd) >> 7; + d2 = d2 - 10*q; + *buf++ = d2 + '0'; /* next digit */ + + d3 = q + 4*d3; + if (d3 != 0) { + q = (d3 * 0xcd) >> 11; + d3 = d3 - 10*q; + *buf++ = d3 + '0'; /* next digit */ + if (q != 0) + *buf++ = q + '0'; /* most sign. digit */ + } + } + } + return buf; +} +/* Same with if's removed. Always emits five digits */ +static char* put_dec_full(char *buf, unsigned q) +{ + /* BTW, if q is in [0,9999], 8-bit ints will be enough, */ + /* but anyway, gcc produces better code with full-sized ints */ + unsigned d3, d2, d1, d0; + d1 = (q>>4) & 0xf; + d2 = (q>>8) & 0xf; + d3 = (q>>12); + + /* Possible ways to approx. divide by 10 */ + /* gcc -O2 replaces multiply with shifts and adds */ + // (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386) + // (x * 0x67) >> 10: 1100111 + // (x * 0x34) >> 9: 110100 - same + // (x * 0x1a) >> 8: 11010 - same + // (x * 0x0d) >> 7: 1101 - same, shortest code (on i386) + + d0 = 6*(d3 + d2 + d1) + (q & 0xf); + q = (d0 * 0xcd) >> 11; + d0 = d0 - 10*q; + *buf++ = d0 + '0'; + d1 = q + 9*d3 + 5*d2 + d1; + q = (d1 * 0xcd) >> 11; + d1 = d1 - 10*q; + *buf++ = d1 + '0'; + + d2 = q + 2*d2; + q = (d2 * 0xd) >> 7; + d2 = d2 - 10*q; + *buf++ = d2 + '0'; + + d3 = q + 4*d3; + q = (d3 * 0xcd) >> 11; /* - shorter code */ + /* q = (d3 * 0x67) >> 10; - would also work */ + d3 = d3 - 10*q; + *buf++ = d3 + '0'; + *buf++ = q + '0'; + return buf; +} +/* No inlining helps gcc to use registers better */ +static noinline char* put_dec(char *buf, unsigned long long num) +{ + while (1) { + unsigned rem; + if (num < 100000) + return put_dec_trunc(buf, num); + rem = do_div(num, 100000); + buf = put_dec_full(buf, rem); + } +} + #define ZEROPAD 1 /* pad with zero */ #define SIGN 2 /* unsigned/signed long */ #define PLUS 4 /* show plus */ @@ -143,12 +240,14 @@ static int skip_atoi(const char **s) #define SPECIAL 32 /* 0x */ #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ -static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) +static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type) { - char c,sign,tmp[66]; + char sign,tmp[66]; const char *digits; - static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /* we are called with base 8, 10 or 16, only, thus don't need "g..." */ + static const char small_digits[] = "0123456789abcdefx"; /* "ghijklmnopqrstuvwxyz"; */ + static const char large_digits[] = "0123456789ABCDEFX"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ + int need_pfx = ((type & SPECIAL) && base != 10); int i; digits = (type & LARGE) ? large_digits : small_digits; @@ -156,7 +255,6 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i type &= ~ZEROPAD; if (base < 2 || base > 36) return NULL; - c = (type & ZEROPAD) ? '0' : ' '; sign = 0; if (type & SIGN) { if ((signed long long) num < 0) { @@ -171,64 +269,85 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i size--; } } - if (type & SPECIAL) { + if (need_pfx) { + size--; if (base == 16) - size -= 2; - else if (base == 8) size--; } + + /* generate full string in tmp[], in reverse order */ i = 0; if (num == 0) - tmp[i++]='0'; - else while (num != 0) + tmp[i++] = '0'; + /* Generic code, for any base: + else do { tmp[i++] = digits[do_div(num,base)]; + } while (num != 0); + */ + else if (base != 10) { /* 8 or 16 */ + int mask = base - 1; + int shift = 3; + if (base == 16) shift = 4; + do { + tmp[i++] = digits[((unsigned char)num) & mask]; + num >>= shift; + } while (num); + } else { /* base 10 */ + i = put_dec(tmp, num) - tmp; + } + + /* printing 100 using %2d gives "100", not "00" */ if (i > precision) precision = i; + /* leading space padding */ size -= precision; - if (!(type&(ZEROPAD+LEFT))) { - while(size-->0) { + if (!(type & (ZEROPAD+LEFT))) { + while(--size >= 0) { if (buf < end) *buf = ' '; ++buf; } } + /* sign */ if (sign) { if (buf < end) *buf = sign; ++buf; } - if (type & SPECIAL) { - if (base==8) { - if (buf < end) - *buf = '0'; - ++buf; - } else if (base==16) { - if (buf < end) - *buf = '0'; - ++buf; + /* "0x" / "0" prefix */ + if (need_pfx) { + if (buf < end) + *buf = '0'; + ++buf; + if (base == 16) { if (buf < end) - *buf = digits[33]; + *buf = digits[16]; /* for arbitrary base: digits[33]; */ ++buf; } } + /* zero or space padding */ if (!(type & LEFT)) { - while (size-- > 0) { + char c = (type & ZEROPAD) ? '0' : ' '; + while (--size >= 0) { if (buf < end) *buf = c; ++buf; } } - while (i < precision--) { + /* hmm even more zero padding? */ + while (i <= --precision) { if (buf < end) *buf = '0'; ++buf; } - while (i-- > 0) { + /* actual digits of result */ + while (--i >= 0) { if (buf < end) *buf = tmp[i]; ++buf; } - while (size-- > 0) { + /* trailing space padding */ + while (--size >= 0) { if (buf < end) *buf = ' '; ++buf; @@ -276,7 +395,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) used for unknown buffer sizes. */ if (unlikely((int) size < 0)) { /* There can be only one.. */ - static int warn = 1; + static char warn = 1; WARN_ON(warn); warn = 0; return 0; |