From 6f68de5f723e57e2709b468f55914fd0f963ce90 Mon Sep 17 00:00:00 2001 From: S.Çağlar Onur Date: Tue, 7 Dec 2010 11:09:43 -0500 Subject: [PATCH] linux-2.6-591-chopstix-intern.patch block/blk-core.c: In function '__generic_make_request': block/blk-core.c:1557: warning: assignment makes integer from pointer without a cast fs/exec.c: In function 'open_exec': fs/exec.c:698: warning: ISO C90 forbids mixed declarations and code fs/bio.c: In function 'bio_endio': fs/bio.c:1440: warning: assignment makes integer from pointer without a cast mm/slab.c: In function '__cache_alloc': mm/slab.c:3513: warning: assignment makes integer from pointer without a cast mm/slab.c: In function '__cache_free': mm/slab.c:3646: warning: assignment makes integer from pointer without a cast --- arch/Kconfig | 8 ++++ arch/x86/kernel/asm-offsets_32.c | 25 +++++++++++ arch/x86/kernel/entry_32.S | 28 +++++++++++++ arch/x86/mm/fault.c | 10 +++++ block/blk-core.c | 29 +++++++++++++ drivers/oprofile/cpu_buffer.c | 30 ++++++++++++++ fs/bio.c | 31 ++++++++++++++ fs/exec.c | 8 ++++ include/linux/arrays.h | 39 ++++++++++++++++++ include/linux/mutex.h | 2 +- include/linux/sched.h | 5 ++ kernel/mutex.c | 55 +++++++++++++++++++++++++ kernel/sched.c | 82 +++++++++++++++++++++++++++++++++++++- mm/memory.c | 29 +++++++++++++ mm/slab.c | 54 +++++++++++++++++++++++-- 15 files changed, 429 insertions(+), 6 deletions(-) create mode 100644 include/linux/arrays.h diff --git a/arch/Kconfig b/arch/Kconfig index cdea504..608c64d 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -27,6 +27,14 @@ config OPROFILE_EVENT_MULTIPLEX If unsure, say N. +config CHOPSTIX + bool "Chopstix (PlanetLab)" + depends on MODULES && OPROFILE + help + Chopstix allows you to monitor various events by summarizing them + in lossy data structures and transferring these data structures + into user space. If in doubt, say "N". + config HAVE_OPROFILE bool diff --git a/arch/x86/kernel/asm-offsets_32.c b/arch/x86/kernel/asm-offsets_32.c index dfdbf64..29c79b8 100644 --- a/arch/x86/kernel/asm-offsets_32.c +++ b/arch/x86/kernel/asm-offsets_32.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,18 @@ #include #include "../../../drivers/lguest/lg.h" +#ifdef CONFIG_CHOPSTIX +#define STACKOFFSET(sym, str, mem) \ + DEFINE(sym, offsetof(struct str, mem)-sizeof(struct str)); + +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned count; + unsigned int number; +}; +#endif + /* workaround for a warning with -Wmissing-prototypes */ void foo(void); @@ -51,6 +64,18 @@ void foo(void) OFFSET(CPUINFO_x86_vendor_id, cpuinfo_x86, x86_vendor_id); BLANK(); +#ifdef CONFIG_CHOPSTIX + STACKOFFSET(TASK_thread, task_struct, thread); + STACKOFFSET(THREAD_esp, thread_struct, sp); + STACKOFFSET(EVENT_event_data, event, event_data); + STACKOFFSET(EVENT_task, event, task); + STACKOFFSET(EVENT_event_type, event, event_type); + STACKOFFSET(SPEC_number, event_spec, number); + DEFINE(EVENT_SIZE, sizeof(struct event)); + DEFINE(SPEC_SIZE, sizeof(struct event_spec)); + DEFINE(SPEC_EVENT_SIZE, sizeof(struct event_spec)+sizeof(struct event)); +#endif + OFFSET(TI_task, thread_info, task); OFFSET(TI_exec_domain, thread_info, exec_domain); OFFSET(TI_flags, thread_info, flags); diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 8b8db35..fc82d32 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -538,6 +538,34 @@ ENTRY(system_call) cmpl $(nr_syscalls), %eax jae syscall_badsys syscall_call: +#ifdef CONFIG_CHOPSTIX + /* Move Chopstix syscall probe here */ + /* Save and clobber: eax, ecx, ebp */ + pushl %eax + pushl %ecx + pushl %ebp + movl %esp, %ebp + subl $SPEC_EVENT_SIZE, %esp + movl rec_event, %ecx + testl %ecx, %ecx + jz carry_on + # struct event is first, just below %ebp + movl %eax, (SPEC_number-EVENT_SIZE)(%ebp) + leal -SPEC_EVENT_SIZE(%ebp), %eax + movl %eax, EVENT_event_data(%ebp) + movl $7, EVENT_event_type(%ebp) + movl rec_event, %edx + movl $1, 4(%esp) + leal -EVENT_SIZE(%ebp), %eax + movl %eax, (%esp) + call rec_event_asm +carry_on: + addl $SPEC_EVENT_SIZE, %esp + popl %ebp + popl %ecx + popl %eax + /* End chopstix */ +#endif call *sys_call_table(,%eax,4) movl %eax,PT_EAX(%esp) # store the return value syscall_exit: diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index b899fb7..c827e81 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -65,6 +65,16 @@ static inline int notify_page_fault(struct pt_regs *regs) return ret; } +#ifdef CONFIG_CHOPSTIX +extern void (*rec_event)(void *,unsigned int); +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned count; + unsigned char reason; +}; +#endif + /* * Prefetch quirks: * diff --git a/block/blk-core.c b/block/blk-core.c index 48dbd8d..94030b1 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -27,12 +27,23 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include #include "blk.h" +#ifdef CONFIG_CHOPSTIX +extern void (*rec_event)(void *,unsigned int); +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned count; + unsigned char reason; +}; +#endif + EXPORT_TRACEPOINT_SYMBOL_GPL(block_remap); EXPORT_TRACEPOINT_SYMBOL_GPL(block_rq_remap); EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_complete); @@ -1568,6 +1579,24 @@ static inline void __generic_make_request(struct bio *bio) trace_block_bio_queue(q, bio); +#ifdef CONFIG_CHOPSTIX + if (rec_event) { + struct event event; + struct event_spec espec; + unsigned long eip; + + espec.reason = 0;/*request */ + + eip = bio->bi_end_io; + event.event_data=&espec; + espec.pc=eip; + event.event_type=3; + /* index in the event array currently set up */ + /* make sure the counters are loaded in the order we want them to show up*/ + (*rec_event)(&event, bio->bi_size); + } +#endif + ret = q->make_request_fn(q, bio); } while (ret); diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 9e0ef46..f6217fd 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "event_buffer.h" #include "cpu_buffer.h" @@ -286,6 +287,17 @@ static inline void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf) cpu_buf->tracing = 0; } +#ifdef CONFIG_CHOPSTIX + +struct event_spec { + unsigned int pc; + unsigned long dcookie; + unsigned count; +}; + +extern void (*rec_event)(void *,unsigned int); +#endif + static inline void __oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, unsigned long event, int is_kernel) @@ -328,7 +340,25 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) pc = ESCAPE_CODE; /* as this causes an early return. */ } +#ifdef CONFIG_CHOPSTIX + if (rec_event) { + struct event esig; + struct event_spec espec; + esig.task = current; + espec.pc = pc; + espec.count = 1; + esig.event_data = &espec; + esig.event_type = event; /* index in the event array currently set up */ + /* make sure the counters are loaded in the order we want them to show up*/ + (*rec_event)(&esig, 1); + } + else { + __oprofile_add_ext_sample(pc, regs, event, is_kernel); + } +#else __oprofile_add_ext_sample(pc, regs, event, is_kernel); +#endif + } /* diff --git a/fs/bio.c b/fs/bio.c index 06f71fc..56fc42d 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -26,6 +26,7 @@ #include #include #include /* for struct sg_iovec */ +#include #include @@ -48,6 +49,7 @@ struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { }; #undef BV + /* * fs_bio_set is the bio_set containing bio and iovec memory pools used by * IO code that does not need private memory pools. @@ -1408,6 +1410,17 @@ void bio_check_pages_dirty(struct bio *bio) } } +#ifdef CONFIG_CHOPSTIX +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned count; + unsigned char reason; +}; + +extern void (*rec_event)(void *,unsigned int); +#endif + /** * bio_endio - end I/O on a bio * @bio: bio @@ -1429,6 +1442,24 @@ void bio_endio(struct bio *bio, int error) else if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) error = -EIO; +#ifdef CONFIG_CHOPSTIX + if (rec_event) { + struct event event; + struct event_spec espec; + unsigned long eip; + + espec.reason = 1;/*response */ + + eip = bio->bi_end_io; + event.event_data=&espec; + espec.pc=eip; + event.event_type=3; + /* index in the event array currently set up */ + /* make sure the counters are loaded in the order we want them to show up*/ + (*rec_event)(&event, bio->bi_size); + } +#endif + if (bio->bi_end_io) bio->bi_end_io(bio, error); } diff --git a/fs/exec.c b/fs/exec.c index 7fdbf49..6f2d772 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -735,6 +736,13 @@ struct file *open_exec(const char *name) if (err) goto exit; +#ifdef CONFIG_CHOPSTIX + unsigned long cookie; + extern void (*rec_event)(void *, unsigned int); + if (rec_event && !(file->f_path.dentry->d_flags & DCACHE_COOKIE)) + get_dcookie(&file->f_path, &cookie); +#endif + out: return file; diff --git a/include/linux/arrays.h b/include/linux/arrays.h new file mode 100644 index 0000000..7641a3c --- /dev/null +++ b/include/linux/arrays.h @@ -0,0 +1,39 @@ +#ifndef __ARRAYS_H__ +#define __ARRAYS_H__ +#include + +#define SAMPLING_METHOD_DEFAULT 0 +#define SAMPLING_METHOD_LOG 1 + +#define DEFAULT_ARRAY_SIZE 2048 + +/* Every probe has an array handler */ + +/* XXX - Optimize this structure */ + +extern void (*rec_event)(void *,unsigned int); +struct array_handler { + struct list_head link; + unsigned int (*hash_func)(void *); + unsigned int (*sampling_func)(void *,int,void *); + unsigned short size; + unsigned int threshold; + unsigned char **expcount; + unsigned int sampling_method; + unsigned int **arrays; + unsigned int arraysize; + unsigned int num_samples[2]; + void **epoch_samples; /* size-sized lists of samples */ + unsigned int (*serialize)(void *, void *); + unsigned char code[5]; + unsigned int last_threshold; +}; + +struct event { + struct list_head link; + void *event_data; + unsigned int count; + unsigned int event_type; + struct task_struct *task; +}; +#endif diff --git a/include/linux/mutex.h b/include/linux/mutex.h index 878cab4..6c21914 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -50,7 +50,7 @@ struct mutex { atomic_t count; spinlock_t wait_lock; struct list_head wait_list; -#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) +#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) || defined(CONFIG_CHOPSTIX) struct thread_info *owner; #endif #ifdef CONFIG_DEBUG_MUTEXES diff --git a/include/linux/sched.h b/include/linux/sched.h index b0cb58b..45f69c3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1398,6 +1398,11 @@ struct task_struct { cputime_t utime, stime, utimescaled, stimescaled; cputime_t gtime; cputime_t prev_utime, prev_stime; + + #ifdef CONFIG_CHOPSTIX + unsigned long last_interrupted, last_ran_j; + #endif + unsigned long nvcsw, nivcsw; /* context switch counts */ struct timespec start_time; /* monotonic time */ struct timespec real_start_time; /* boot based time */ diff --git a/kernel/mutex.c b/kernel/mutex.c index e04aa45..196ac04 100644 --- a/kernel/mutex.c +++ b/kernel/mutex.c @@ -23,6 +23,16 @@ #include #include #include +#include + +#ifdef CONFIG_CHOPSTIX +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned count; + unsigned char reason; +}; +#endif /* * In the DEBUG case we are using the "NULL fastpath" for mutexes, @@ -49,6 +59,9 @@ void __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) { atomic_set(&lock->count, 1); +#ifdef CONFIG_CHOPSTIX + lock->owner = NULL; +#endif spin_lock_init(&lock->wait_lock); INIT_LIST_HEAD(&lock->wait_list); mutex_clear_owner(lock); @@ -254,6 +267,25 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, } __set_task_state(task, state); +#if 0 && CONFIG_CHOPSTIX + if (rec_event) { + if (lock->owner) { + struct event event; + struct event_spec espec; + struct task_struct *p = lock->owner->task; + + espec.reason = 0; /* lock */ + event.event_data = &espec; + event.task = p; + espec.pc = lock; + event.event_type = 5; + (*rec_event)(&event, 1); + } else { + BUG(); + } + } +#endif + /* didnt get the lock, go to sleep: */ spin_unlock_mutex(&lock->wait_lock, flags); preempt_enable_no_resched(); @@ -268,6 +300,10 @@ done: mutex_remove_waiter(lock, &waiter, current_thread_info()); mutex_set_owner(lock); +#ifdef CONFIG_CHOPSTIX + lock->owner = task_thread_info(task); +#endif + /* set it to 0 if there are no waiters left: */ if (likely(list_empty(&lock->wait_list))) atomic_set(&lock->count, 0); @@ -338,6 +374,25 @@ __mutex_unlock_common_slowpath(atomic_t *lock_count, int nested) debug_mutex_wake_waiter(lock, waiter); +#if 0 && CONFIG_CHOPSTIX + if (rec_event) { + if (lock->owner) { + struct event event; + struct event_spec espec; + struct task_struct *p = lock->owner->task; + + espec.reason = 1; /* unlock */ + event.event_data = &espec; + event.task = p; + espec.pc = lock; + event.event_type = 5; + (*rec_event)(&event, 1); + } else { + BUG(); + } + } +#endif + wake_up_process(waiter->task); } diff --git a/kernel/sched.c b/kernel/sched.c index 1e90fc0..aa4d3d7 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -10,7 +10,7 @@ * 1998-11-19 Implemented schedule_timeout() and related stuff * by Andrea Arcangeli * 2002-01-04 New ultra-scalable O(1) scheduler by Ingo Molnar: - * hybrid priority-list and round-robin design with + * hybrid priority-list and round-robin deventn with * an array-switch method of distributing timeslices * and per-CPU runqueues. Cleanups and useful suggestions * by Davide Libenzi, preemptible kernel bits by Robert Love. @@ -73,6 +73,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,9 @@ #include "sched_cpupri.h" #include "sched_autogroup.h" +#define INTERRUPTIBLE -1 +#define RUNNING 0 + #define CREATE_TRACE_POINTS #include @@ -2670,6 +2674,10 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, rq = cpu_rq(cpu); spin_lock(&rq->lock); +#ifdef CONFIG_CHOPSTIX + p->last_ran_j = jiffies; + p->last_interrupted = INTERRUPTIBLE; +#endif /* * We migrated the task without holding either rq->lock, however * since the task is not on the task list itself, nobody else @@ -5988,6 +5996,30 @@ pick_next_task(struct rq *rq) } } +#ifdef CONFIG_CHOPSTIX +void (*rec_event)(void *,unsigned int) = NULL; +EXPORT_SYMBOL(rec_event); +EXPORT_SYMBOL(in_sched_functions); + +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned int count; + unsigned int reason; +}; + +/* To support safe calling from asm */ +asmlinkage void rec_event_asm (struct event *event_signature_in, unsigned int count) { + struct pt_regs *regs; + struct event_spec *es = event_signature_in->event_data; + regs = task_pt_regs(current); + event_signature_in->task=current; + es->pc=regs->ip; + event_signature_in->count=1; + (*rec_event)(event_signature_in, count); +} +#endif + /* * schedule() is the main scheduler function. */ @@ -6034,6 +6066,54 @@ need_resched_nonpreemptible: next = pick_next_task(rq); if (likely(prev != next)) { + +#ifdef CONFIG_CHOPSTIX + /* Run only if the Chopstix module so decrees it */ + if (rec_event) { + unsigned long diff; + int sampling_reason; + prev->last_ran_j = jiffies; + if (next->last_interrupted!=INTERRUPTIBLE) { + if (next->last_interrupted!=RUNNING) { + diff = (jiffies-next->last_interrupted); + sampling_reason = 0;/* BLOCKING */ + } + else { + diff = jiffies-next->last_ran_j; + sampling_reason = 1;/* PREEMPTION */ + } + + if (diff >= HZ/10) { + struct event event; + struct event_spec espec; + struct pt_regs *regs; + regs = task_pt_regs(current); + + espec.reason = sampling_reason; + event.event_data=&espec; + event.task=next; + espec.pc=regs->ip; + event.event_type=2; + /* index in the event array currently set up */ + /* make sure the counters are loaded in the order we want them to show up*/ + (*rec_event)(&event, diff); + } + } + /* next has been elected to run */ + next->last_interrupted=0; + + /* An uninterruptible process just yielded. Record the current jiffy */ + if (prev->state & TASK_UNINTERRUPTIBLE) { + prev->last_interrupted=jiffies; + } + /* An interruptible process just yielded, or it got preempted. + * Mark it as interruptible */ + else if (prev->state & TASK_INTERRUPTIBLE) { + prev->last_interrupted=INTERRUPTIBLE; + } + } +#endif + sched_info_switch(prev, next); perf_event_task_sched_out(prev, next); diff --git a/mm/memory.c b/mm/memory.c index dbd2c19..7f3667d 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -57,6 +57,7 @@ #include #include // #include +#include #include #include @@ -3168,6 +3169,16 @@ out: return ret; } +#ifdef CONFIG_CHOPSTIX +extern void (*rec_event)(void *,unsigned int); +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned count; + unsigned char reason; +}; +#endif + /* * By the time we get here, we already hold the mm semaphore */ @@ -3213,6 +3224,24 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (!pte) return VM_FAULT_OOM; +#ifdef CONFIG_CHOPSTIX + if (rec_event) { + struct event event; + struct event_spec espec; + struct pt_regs *regs; + unsigned int pc; + regs = task_pt_regs(current); + pc = regs->ip & (unsigned int) ~4095; + + espec.reason = 0; /* alloc */ + event.event_data=&espec; + event.task = current; + espec.pc=pc; + event.event_type = 6; + (*rec_event)(&event, 1); + } +#endif + return handle_pte_fault(mm, vma, address, pte, pmd, flags); } diff --git a/mm/slab.c b/mm/slab.c index c3ceb66..ad2f1a9 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -113,6 +113,7 @@ #include #include #include +#include #include #include #include @@ -253,6 +254,16 @@ struct slab_rcu { void *addr; }; +#ifdef CONFIG_CHOPSTIX +extern void (*rec_event)(void *,unsigned int); +struct event_spec { + unsigned long pc; + unsigned long dcookie; + unsigned count; + unsigned char reason; +}; +#endif + /* * struct array_cache * @@ -3497,6 +3508,19 @@ __cache_alloc(struct kmem_cache *cachep, gfp_t flags, void *caller) kmemleak_alloc_recursive(objp, obj_size(cachep), 1, cachep->flags, flags); prefetchw(objp); +#ifdef CONFIG_CHOPSTIX + if (rec_event && objp) { + struct event event; + struct event_spec espec; + + espec.reason = 0; /* alloc */ + event.event_data=&espec; + event.task = current; + espec.pc=caller; + event.event_type=4; + (*rec_event)(&event, cachep->buffer_size); + } +#endif if (likely(objp)) kmemcheck_slab_alloc(cachep, flags, objp, obj_size(cachep)); @@ -3609,13 +3633,28 @@ free_done: * Release an obj back to its cache. If the obj has a constructed state, it must * be in this state _before_ it is released. Called with disabled ints. */ -static inline void __cache_free(struct kmem_cache *cachep, void *objp) +static inline void __cache_free(struct kmem_cache *cachep, void *objp, void *caller) { struct array_cache *ac = cpu_cache_get(cachep); check_irq_off(); kmemleak_free_recursive(objp, cachep->flags); - objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0)); + objp = cache_free_debugcheck(cachep, objp, caller); + +#ifdef CONFIG_CHOPSTIX + if (rec_event && objp) { + struct event event; + struct event_spec espec; + + espec.reason = 1; /* free */ + event.event_data = &espec; + event.task = current; + espec.pc = caller; + event.event_type = 4; + (*rec_event)(&event, cachep->buffer_size); + } +#endif + vx_slab_free(cachep); kmemcheck_slab_free(cachep, objp, obj_size(cachep)); @@ -3817,10 +3856,17 @@ void *__kmalloc_track_caller(size_t size, gfp_t flags, unsigned long caller) EXPORT_SYMBOL(__kmalloc_track_caller); #else +#ifdef CONFIG_CHOPSTIX +void *__kmalloc(size_t size, gfp_t flags) +{ + return __do_kmalloc(size, flags, __builtin_return_address(0)); +} +#else void *__kmalloc(size_t size, gfp_t flags) { return __do_kmalloc(size, flags, NULL); } +#endif EXPORT_SYMBOL(__kmalloc); #endif @@ -3840,7 +3886,7 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) debug_check_no_locks_freed(objp, obj_size(cachep)); if (!(cachep->flags & SLAB_DEBUG_OBJECTS)) debug_check_no_obj_freed(objp, obj_size(cachep)); - __cache_free(cachep, objp); + __cache_free(cachep, objp,__builtin_return_address(0)); local_irq_restore(flags); trace_kmem_cache_free(_RET_IP_, objp); @@ -3870,7 +3916,7 @@ void kfree(const void *objp) c = virt_to_cache(objp); debug_check_no_locks_freed(objp, obj_size(c)); debug_check_no_obj_freed(objp, obj_size(c)); - __cache_free(c, (void *)objp); + __cache_free(c, (void *)objp,__builtin_return_address(0)); local_irq_restore(flags); } EXPORT_SYMBOL(kfree);