Commit db8b50ba authored by George Anzinger's avatar George Anzinger Committed by Linus Torvalds

[PATCH] POSIX clocks & timers

This is version 23 or so of the POSIX timer code.

Internal changelog:

 - Changed the signals code to match the new order of things.  Also the
   new xtime_lock code needed to be picked up.  It made some things a lot
   simpler.

 - Fixed a spin lock hand off problem in locking timers (thanks
   to Randy).

 - Fixed nanosleep to test for out of bound nanoseconds
   (thanks to Julie).

 - Fixed a couple of id deallocation bugs that left old ids
   laying around (hey I get this one).

 - This version has a new timer id manager.  Andrew Morton
   suggested elimination of recursion (done) and I added code
   to allow it to release unused nodes.  The prior version only
   released the leaf nodes.  (The id manager uses radix tree
   type nodes.)  Also added is a reuse count so ids will not
   repeat for at least 256 alloc/ free cycles.

 - The changes for the new sys_call restart now allow one
   restart function to handle both nanosleep and clock_nanosleep.
   Saves a bit of code, nice.

 - All the requested changes and Lindent too :).

 - I also broke clock_nanosleep() apart much the same way
   nanosleep() was with the 2.5.50-bk5 changes.

TIMER STORMS

The POSIX clocks and timers code prevents "timer storms" by
not putting repeating timers back in the timer list until
the signal is delivered for the prior expiry.  Timer events
missed by this delay are accounted for in the timer overrun
count.  The net result is MUCH lower system overhead while
presenting the same info to the user as would be the case if
an interrupt and timer processing were required for each
increment in the overrun count.
parent 307d1849
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/sys.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <asm/thread_info.h> #include <asm/thread_info.h>
#include <asm/errno.h> #include <asm/errno.h>
...@@ -252,7 +251,7 @@ ENTRY(sysenter_entry) ...@@ -252,7 +251,7 @@ ENTRY(sysenter_entry)
pushl %eax pushl %eax
SAVE_ALL SAVE_ALL
GET_THREAD_INFO(%ebp) GET_THREAD_INFO(%ebp)
cmpl $(NR_syscalls), %eax cmpl $(nr_syscalls), %eax
jae syscall_badsys jae syscall_badsys
testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp) testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp)
...@@ -276,7 +275,7 @@ ENTRY(system_call) ...@@ -276,7 +275,7 @@ ENTRY(system_call)
pushl %eax # save orig_eax pushl %eax # save orig_eax
SAVE_ALL SAVE_ALL
GET_THREAD_INFO(%ebp) GET_THREAD_INFO(%ebp)
cmpl $(NR_syscalls), %eax cmpl $(nr_syscalls), %eax
jae syscall_badsys jae syscall_badsys
# system call tracing in operation # system call tracing in operation
testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp) testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp)
...@@ -339,7 +338,7 @@ syscall_trace_entry: ...@@ -339,7 +338,7 @@ syscall_trace_entry:
xorl %edx,%edx xorl %edx,%edx
call do_syscall_trace call do_syscall_trace
movl ORIG_EAX(%esp), %eax movl ORIG_EAX(%esp), %eax
cmpl $(NR_syscalls), %eax cmpl $(nr_syscalls), %eax
jnae syscall_call jnae syscall_call
jmp syscall_exit jmp syscall_exit
...@@ -801,8 +800,15 @@ ENTRY(sys_call_table) ...@@ -801,8 +800,15 @@ ENTRY(sys_call_table)
.long sys_epoll_wait .long sys_epoll_wait
.long sys_remap_file_pages .long sys_remap_file_pages
.long sys_set_tid_address .long sys_set_tid_address
.long sys_timer_create
.long sys_timer_settime /* 260 */
.rept NR_syscalls-(.-sys_call_table)/4 .long sys_timer_gettime
.long sys_ni_syscall .long sys_timer_getoverrun
.endr .long sys_timer_delete
.long sys_clock_settime
.long sys_clock_gettime /* 265 */
.long sys_clock_getres
.long sys_clock_nanosleep
nr_syscalls=(.-sys_call_table)/4
...@@ -135,6 +135,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -135,6 +135,7 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_sequnlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
clock_was_set();
} }
/* /*
......
...@@ -823,6 +823,7 @@ int flush_old_exec(struct linux_binprm * bprm) ...@@ -823,6 +823,7 @@ int flush_old_exec(struct linux_binprm * bprm)
flush_signal_handlers(current); flush_signal_handlers(current);
flush_old_files(current->files); flush_old_files(current->files);
exit_itimers(current);
return 0; return 0;
......
...@@ -43,8 +43,11 @@ typedef struct siginfo { ...@@ -43,8 +43,11 @@ typedef struct siginfo {
/* POSIX.1b timers */ /* POSIX.1b timers */
struct { struct {
unsigned int _timer1; timer_t _tid; /* timer id */
unsigned int _timer2; int _overrun; /* overrun count */
char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
sigval_t _sigval; /* same as below */
int _sys_private; /* not to be passed to user */
} _timer; } _timer;
/* POSIX.1b signals */ /* POSIX.1b signals */
...@@ -86,8 +89,9 @@ typedef struct siginfo { ...@@ -86,8 +89,9 @@ typedef struct siginfo {
*/ */
#define si_pid _sifields._kill._pid #define si_pid _sifields._kill._pid
#define si_uid _sifields._kill._uid #define si_uid _sifields._kill._uid
#define si_timer1 _sifields._timer._timer1 #define si_tid _sifields._timer._tid
#define si_timer2 _sifields._timer._timer2 #define si_overrun _sifields._timer._overrun
#define si_sys_private _sifields._timer._sys_private
#define si_status _sifields._sigchld._status #define si_status _sifields._sigchld._status
#define si_utime _sifields._sigchld._utime #define si_utime _sifields._sigchld._utime
#define si_stime _sifields._sigchld._stime #define si_stime _sifields._sigchld._stime
...@@ -221,6 +225,7 @@ typedef struct siginfo { ...@@ -221,6 +225,7 @@ typedef struct siginfo {
#define SIGEV_SIGNAL 0 /* notify via signal */ #define SIGEV_SIGNAL 0 /* notify via signal */
#define SIGEV_NONE 1 /* other notification: meaningless */ #define SIGEV_NONE 1 /* other notification: meaningless */
#define SIGEV_THREAD 2 /* deliver via thread creation */ #define SIGEV_THREAD 2 /* deliver via thread creation */
#define SIGEV_THREAD_ID 4 /* deliver to thread */
#define SIGEV_MAX_SIZE 64 #define SIGEV_MAX_SIZE 64
#ifndef SIGEV_PAD_SIZE #ifndef SIGEV_PAD_SIZE
...@@ -235,6 +240,7 @@ typedef struct sigevent { ...@@ -235,6 +240,7 @@ typedef struct sigevent {
int sigev_notify; int sigev_notify;
union { union {
int _pad[SIGEV_PAD_SIZE]; int _pad[SIGEV_PAD_SIZE];
int _tid;
struct { struct {
void (*_function)(sigval_t); void (*_function)(sigval_t);
...@@ -247,10 +253,12 @@ typedef struct sigevent { ...@@ -247,10 +253,12 @@ typedef struct sigevent {
#define sigev_notify_function _sigev_un._sigev_thread._function #define sigev_notify_function _sigev_un._sigev_thread._function
#define sigev_notify_attributes _sigev_un._sigev_thread._attribute #define sigev_notify_attributes _sigev_un._sigev_thread._attribute
#define sigev_notify_thread_id _sigev_un._tid
#ifdef __KERNEL__ #ifdef __KERNEL__
struct siginfo; struct siginfo;
void do_schedule_next_timer(struct siginfo *info);
#ifndef HAVE_ARCH_COPY_SIGINFO #ifndef HAVE_ARCH_COPY_SIGINFO
......
...@@ -22,6 +22,8 @@ typedef int __kernel_ptrdiff_t; ...@@ -22,6 +22,8 @@ typedef int __kernel_ptrdiff_t;
typedef long __kernel_time_t; typedef long __kernel_time_t;
typedef long __kernel_suseconds_t; typedef long __kernel_suseconds_t;
typedef long __kernel_clock_t; typedef long __kernel_clock_t;
typedef int __kernel_timer_t;
typedef int __kernel_clockid_t;
typedef int __kernel_daddr_t; typedef int __kernel_daddr_t;
typedef char * __kernel_caddr_t; typedef char * __kernel_caddr_t;
typedef unsigned short __kernel_uid16_t; typedef unsigned short __kernel_uid16_t;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/time.h>
/* Avoid too many header ordering problems. */ /* Avoid too many header ordering problems. */
struct siginfo; struct siginfo;
......
...@@ -264,7 +264,17 @@ ...@@ -264,7 +264,17 @@
#define __NR_epoll_wait 256 #define __NR_epoll_wait 256
#define __NR_remap_file_pages 257 #define __NR_remap_file_pages 257
#define __NR_set_tid_address 258 #define __NR_set_tid_address 258
#define __NR_timer_create 259
#define __NR_timer_settime (__NR_timer_create+1)
#define __NR_timer_gettime (__NR_timer_create+2)
#define __NR_timer_getoverrun (__NR_timer_create+3)
#define __NR_timer_delete (__NR_timer_create+4)
#define __NR_clock_settime (__NR_timer_create+5)
#define __NR_clock_gettime (__NR_timer_create+6)
#define __NR_clock_getres (__NR_timer_create+7)
#define __NR_clock_nanosleep (__NR_timer_create+8)
#define NR_syscalls 268
/* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */ /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
......
/*
* include/linux/id.h
*
* 2002-10-18 written by Jim Houston jim.houston@ccur.com
* Copyright (C) 2002 by Concurrent Computer Corporation
* Distributed under the GNU GPL license version 2.
*
* Small id to pointer translation service avoiding fixed sized
* tables.
*/
#include <linux/types.h>
#include <asm/bitops.h>
#define RESERVED_ID_BITS 8
#if BITS_PER_LONG == 32
#define IDR_BITS 5
#define IDR_FULL 0xffffffff
#elif BITS_PER_LONG == 64
#define IDR_BITS 6
#define IDR_FULL 0xffffffffffffffff
#else
#error "BITS_PER_LONG is not 32 or 64"
#endif
#define IDR_MASK ((1 << IDR_BITS)-1)
/* Leave the possibility of an incomplete final layer */
#define MAX_LEVEL (BITS_PER_LONG - RESERVED_ID_BITS + IDR_BITS - 1) / IDR_BITS
#define MAX_ID_SHIFT (BITS_PER_LONG - RESERVED_ID_BITS)
#define MAX_ID_BIT (1 << MAX_ID_SHIFT)
#define MAX_ID_MASK (MAX_ID_BIT - 1)
/* Number of id_layer structs to leave in free list */
#define IDR_FREE_MAX MAX_LEVEL + MAX_LEVEL
struct idr_layer {
unsigned long bitmap; // A zero bit means "space here"
int count; // When zero, we can release it
struct idr_layer *ary[1<<IDR_BITS];
};
struct idr {
struct idr_layer *top;
int layers;
int count;
struct idr_layer *id_free;
int id_free_cnt;
spinlock_t lock;
};
/*
* This is what we export.
*/
void *idr_find(struct idr *idp, int id);
int idr_pre_get(struct idr *idp);
int idr_get_new(struct idr *idp, void *ptr);
void idr_remove(struct idr *idp, int id);
void idr_init(struct idr *idp);
extern kmem_cache_t *idr_layer_cache;
...@@ -98,6 +98,7 @@ ...@@ -98,6 +98,7 @@
.sighand = &init_sighand, \ .sighand = &init_sighand, \
.pending = { NULL, &tsk.pending.head, {{0}}}, \ .pending = { NULL, &tsk.pending.head, {{0}}}, \
.blocked = {{0}}, \ .blocked = {{0}}, \
.posix_timers = LIST_HEAD_INIT(tsk.posix_timers), \
.alloc_lock = SPIN_LOCK_UNLOCKED, \ .alloc_lock = SPIN_LOCK_UNLOCKED, \
.switch_lock = SPIN_LOCK_UNLOCKED, \ .switch_lock = SPIN_LOCK_UNLOCKED, \
.journal_info = NULL, \ .journal_info = NULL, \
......
#ifndef _linux_POSIX_TIMERS_H
#define _linux_POSIX_TIMERS_H
struct k_clock {
int res; /* in nano seconds */
int (*clock_set) (struct timespec * tp);
int (*clock_get) (struct timespec * tp);
int (*nsleep) (int flags,
struct timespec * new_setting,
struct itimerspec * old_setting);
int (*timer_set) (struct k_itimer * timr, int flags,
struct itimerspec * new_setting,
struct itimerspec * old_setting);
int (*timer_del) (struct k_itimer * timr);
void (*timer_get) (struct k_itimer * timr,
struct itimerspec * cur_setting);
};
struct now_struct {
unsigned long jiffies;
};
#define posix_get_now(now) (now)->jiffies = jiffies;
#define posix_time_before(timer, now) \
time_before((timer)->expires, (now)->jiffies)
#define posix_bump_timer(timr) do { \
(timr)->it_timer.expires += (timr)->it_incr; \
(timr)->it_overrun++; \
}while (0)
#endif
...@@ -295,6 +295,25 @@ extern struct user_struct root_user; ...@@ -295,6 +295,25 @@ extern struct user_struct root_user;
typedef struct prio_array prio_array_t; typedef struct prio_array prio_array_t;
struct backing_dev_info; struct backing_dev_info;
/* POSIX.1b interval timer structure. */
struct k_itimer {
struct list_head list; /* free/ allocate list */
spinlock_t it_lock;
clockid_t it_clock; /* which timer type */
timer_t it_id; /* timer id */
int it_overrun; /* overrun on pending signal */
int it_overrun_last; /* overrun on last delivered signal */
int it_requeue_pending; /* waiting to requeue this timer */
int it_sigev_notify; /* notify word of sigevent struct */
int it_sigev_signo; /* signo word of sigevent struct */
sigval_t it_sigev_value; /* value word of sigevent struct */
unsigned long it_incr; /* interval specified in jiffies */
struct task_struct *it_process; /* process to send signal to */
struct timer_list it_timer;
};
struct task_struct { struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
struct thread_info *thread_info; struct thread_info *thread_info;
...@@ -358,6 +377,7 @@ struct task_struct { ...@@ -358,6 +377,7 @@ struct task_struct {
unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_incr; unsigned long it_real_incr, it_prof_incr, it_virt_incr;
struct timer_list real_timer; struct timer_list real_timer;
struct list_head posix_timers; /* POSIX.1b Interval Timers */
unsigned long utime, stime, cutime, cstime; unsigned long utime, stime, cutime, cstime;
u64 start_time; u64 start_time;
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
...@@ -597,6 +617,7 @@ extern void exit_signal(struct task_struct *); ...@@ -597,6 +617,7 @@ extern void exit_signal(struct task_struct *);
extern void __exit_signal(struct task_struct *); extern void __exit_signal(struct task_struct *);
extern void exit_sighand(struct task_struct *); extern void exit_sighand(struct task_struct *);
extern void __exit_sighand(struct task_struct *); extern void __exit_sighand(struct task_struct *);
extern void exit_itimers(struct task_struct *);
extern NORET_TYPE void do_group_exit(int); extern NORET_TYPE void do_group_exit(int);
......
...@@ -208,7 +208,7 @@ extern int sigprocmask(int, sigset_t *, sigset_t *); ...@@ -208,7 +208,7 @@ extern int sigprocmask(int, sigset_t *, sigset_t *);
struct pt_regs; struct pt_regs;
extern int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs, void *cookie); extern int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs, void *cookie);
#endif #endif
#define FOLD_NANO_SLEEP_INTO_CLOCK_NANO_SLEEP
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_SIGNAL_H */ #endif /* _LINUX_SIGNAL_H */
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
#define _LINUX_SYS_H #define _LINUX_SYS_H
/* /*
* system call entry points ... but not all are defined * This file is no longer used or needed
*/ */
#define NR_syscalls 260
/* /*
* These are system calls that will be removed at some time * These are system calls that will be removed at some time
......
...@@ -41,6 +41,19 @@ struct timezone { ...@@ -41,6 +41,19 @@ struct timezone {
*/ */
#define MAX_JIFFY_OFFSET ((~0UL >> 1)-1) #define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)
/* Parameters used to convert the timespec values */
#ifndef USEC_PER_SEC
#define USEC_PER_SEC (1000000L)
#endif
#ifndef NSEC_PER_SEC
#define NSEC_PER_SEC (1000000000L)
#endif
#ifndef NSEC_PER_USEC
#define NSEC_PER_USEC (1000L)
#endif
static __inline__ unsigned long static __inline__ unsigned long
timespec_to_jiffies(struct timespec *value) timespec_to_jiffies(struct timespec *value)
{ {
...@@ -139,6 +152,8 @@ struct timespec current_kernel_time(void); ...@@ -139,6 +152,8 @@ struct timespec current_kernel_time(void);
#ifdef __KERNEL__ #ifdef __KERNEL__
extern void do_gettimeofday(struct timeval *tv); extern void do_gettimeofday(struct timeval *tv);
extern void do_settimeofday(struct timeval *tv); extern void do_settimeofday(struct timeval *tv);
extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz);
extern void clock_was_set(void); // call when ever the clock is set
extern long do_nanosleep(struct timespec *t); extern long do_nanosleep(struct timespec *t);
extern long do_utimes(char * filename, struct timeval * times); extern long do_utimes(char * filename, struct timeval * times);
#endif #endif
...@@ -167,4 +182,24 @@ struct itimerval { ...@@ -167,4 +182,24 @@ struct itimerval {
struct timeval it_value; /* current value */ struct timeval it_value; /* current value */
}; };
/*
* The IDs of the various system clocks (for POSIX.1b interval timers).
*/
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
#define CLOCK_PROCESS_CPUTIME_ID 2
#define CLOCK_THREAD_CPUTIME_ID 3
#define CLOCK_REALTIME_HR 4
#define CLOCK_MONOTONIC_HR 5
#define MAX_CLOCKS 6
/*
* The various flags for setting POSIX.1b interval timers.
*/
#define TIMER_ABSTIME 0x01
#endif #endif
...@@ -27,6 +27,8 @@ typedef __kernel_pid_t pid_t; ...@@ -27,6 +27,8 @@ typedef __kernel_pid_t pid_t;
typedef __kernel_daddr_t daddr_t; typedef __kernel_daddr_t daddr_t;
typedef __kernel_key_t key_t; typedef __kernel_key_t key_t;
typedef __kernel_suseconds_t suseconds_t; typedef __kernel_suseconds_t suseconds_t;
typedef __kernel_timer_t timer_t;
typedef __kernel_clockid_t clockid_t;
#ifdef __KERNEL__ #ifdef __KERNEL__
typedef __kernel_uid32_t uid_t; typedef __kernel_uid32_t uid_t;
......
...@@ -6,7 +6,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ ...@@ -6,7 +6,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
exit.o itimer.o time.o softirq.o resource.o \ exit.o itimer.o time.o softirq.o resource.o \
sysctl.o capability.o ptrace.o timer.o user.o \ sysctl.o capability.o ptrace.o timer.o user.o \
signal.o sys.o kmod.o workqueue.o futex.o pid.o \ signal.o sys.o kmod.o workqueue.o futex.o pid.o \
rcupdate.o intermodule.o extable.o params.o rcupdate.o intermodule.o extable.o params.o posix-timers.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
obj-$(CONFIG_SMP) += cpu.o obj-$(CONFIG_SMP) += cpu.o
......
...@@ -460,6 +460,7 @@ void end_lazy_tlb(struct mm_struct *mm) ...@@ -460,6 +460,7 @@ void end_lazy_tlb(struct mm_struct *mm)
mmdrop(active_mm); mmdrop(active_mm);
} }
/* /*
* Turn us into a lazy TLB process if we * Turn us into a lazy TLB process if we
* aren't already.. * aren't already..
...@@ -741,6 +742,7 @@ NORET_TYPE void do_exit(long code) ...@@ -741,6 +742,7 @@ NORET_TYPE void do_exit(long code)
__exit_files(tsk); __exit_files(tsk);
__exit_fs(tsk); __exit_fs(tsk);
exit_namespace(tsk); exit_namespace(tsk);
exit_itimers(tsk);
exit_thread(); exit_thread();
if (tsk->leader) if (tsk->leader)
......
...@@ -813,6 +813,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -813,6 +813,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
INIT_LIST_HEAD(&p->children); INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling); INIT_LIST_HEAD(&p->sibling);
INIT_LIST_HEAD(&p->posix_timers);
init_waitqueue_head(&p->wait_chldexit); init_waitqueue_head(&p->wait_chldexit);
p->vfork_done = NULL; p->vfork_done = NULL;
spin_lock_init(&p->alloc_lock); spin_lock_init(&p->alloc_lock);
......
This diff is collapsed.
...@@ -472,8 +472,6 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask, ...@@ -472,8 +472,6 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
if (!collect_signal(sig, pending, info)) if (!collect_signal(sig, pending, info))
sig = 0; sig = 0;
/* XXX: Once POSIX.1b timers are in, if si_code == SI_TIMER,
we need to xchg out the timer overrun values. */
} }
recalc_sigpending(); recalc_sigpending();
...@@ -492,6 +490,11 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) ...@@ -492,6 +490,11 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
if (!signr) if (!signr)
signr = __dequeue_signal(&tsk->signal->shared_pending, signr = __dequeue_signal(&tsk->signal->shared_pending,
mask, info); mask, info);
if ( signr &&
((info->si_code & __SI_MASK) == __SI_TIMER) &&
info->si_sys_private){
do_schedule_next_timer(info);
}
return signr; return signr;
} }
...@@ -677,6 +680,7 @@ static void handle_stop_signal(int sig, struct task_struct *p) ...@@ -677,6 +680,7 @@ static void handle_stop_signal(int sig, struct task_struct *p)
static int send_signal(int sig, struct siginfo *info, struct sigpending *signals) static int send_signal(int sig, struct siginfo *info, struct sigpending *signals)
{ {
struct sigqueue * q = NULL; struct sigqueue * q = NULL;
int ret = 0;
/* /*
* fast-pathed signals for kernel-internal things like SIGSTOP * fast-pathed signals for kernel-internal things like SIGSTOP
...@@ -720,17 +724,25 @@ static int send_signal(int sig, struct siginfo *info, struct sigpending *signals ...@@ -720,17 +724,25 @@ static int send_signal(int sig, struct siginfo *info, struct sigpending *signals
copy_siginfo(&q->info, info); copy_siginfo(&q->info, info);
break; break;
} }
} else if (sig >= SIGRTMIN && info && (unsigned long)info != 1 } else {
if (sig >= SIGRTMIN && info && (unsigned long)info != 1
&& info->si_code != SI_USER) && info->si_code != SI_USER)
/* /*
* Queue overflow, abort. We may abort if the signal was rt * Queue overflow, abort. We may abort if the signal was rt
* and sent by user using something other than kill(). * and sent by user using something other than kill().
*/ */
return -EAGAIN; return -EAGAIN;
if (((unsigned long)info > 1) && (info->si_code == SI_TIMER))
/*
* Set up a return to indicate that we dropped
* the signal.
*/
ret = info->si_sys_private;
}
out_set: out_set:
sigaddset(&signals->signal, sig); sigaddset(&signals->signal, sig);
return 0; return ret;
} }
#define LEGACY_QUEUE(sigptr, sig) \ #define LEGACY_QUEUE(sigptr, sig) \
...@@ -749,20 +761,26 @@ specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t) ...@@ -749,20 +761,26 @@ specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
BUG(); BUG();
#endif #endif
if (((unsigned long)info > 2) && (info->si_code == SI_TIMER))
/*
* Set up a return to indicate that we dropped the signal.
*/
ret = info->si_sys_private;
/* Short-circuit ignored signals. */ /* Short-circuit ignored signals. */
if (sig_ignored(t, sig)) if (sig_ignored(t, sig))
return 0; goto out;
/* Support queueing exactly one non-rt signal, so that we /* Support queueing exactly one non-rt signal, so that we
can get more detailed information about the cause of can get more detailed information about the cause of
the signal. */ the signal. */
if (LEGACY_QUEUE(&t->pending, sig)) if (LEGACY_QUEUE(&t->pending, sig))
return 0; goto out;
ret = send_signal(sig, info, &t->pending); ret = send_signal(sig, info, &t->pending);
if (!ret && !sigismember(&t->blocked, sig)) if (!ret && !sigismember(&t->blocked, sig))
signal_wake_up(t, sig == SIGKILL); signal_wake_up(t, sig == SIGKILL);
out:
return ret; return ret;
} }
...@@ -821,7 +839,7 @@ __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) ...@@ -821,7 +839,7 @@ __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{ {
struct task_struct *t; struct task_struct *t;
unsigned int mask; unsigned int mask;
int ret; int ret = 0;
#if CONFIG_SMP #if CONFIG_SMP
if (!spin_is_locked(&p->sighand->siglock)) if (!spin_is_locked(&p->sighand->siglock))
...@@ -829,13 +847,19 @@ __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) ...@@ -829,13 +847,19 @@ __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
#endif #endif
handle_stop_signal(sig, p); handle_stop_signal(sig, p);
if (((unsigned long)info > 2) && (info->si_code == SI_TIMER))
/*
* Set up a return to indicate that we dropped the signal.
*/
ret = info->si_sys_private;
/* Short-circuit ignored signals. */ /* Short-circuit ignored signals. */
if (sig_ignored(p, sig)) if (sig_ignored(p, sig))
return 0; return ret;
if (LEGACY_QUEUE(&p->signal->shared_pending, sig)) if (LEGACY_QUEUE(&p->signal->shared_pending, sig))
/* This is a non-RT signal and we already have one queued. */ /* This is a non-RT signal and we already have one queued. */
return 0; return ret;
/* /*
* Don't bother zombies and stopped tasks (but * Don't bother zombies and stopped tasks (but
...@@ -1322,6 +1346,7 @@ do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent) ...@@ -1322,6 +1346,7 @@ do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent)
static void static void
finish_stop(int stop_count) finish_stop(int stop_count)
{ {
int ret;
/* /*
* If there are no other threads in the group, or if there is * If there are no other threads in the group, or if there is
* a group stop in progress and we are the last to stop, * a group stop in progress and we are the last to stop,
...@@ -1753,8 +1778,9 @@ int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) ...@@ -1753,8 +1778,9 @@ int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
err |= __put_user(from->si_uid, &to->si_uid); err |= __put_user(from->si_uid, &to->si_uid);
break; break;
case __SI_TIMER: case __SI_TIMER:
err |= __put_user(from->si_timer1, &to->si_timer1); err |= __put_user(from->si_tid, &to->si_tid);
err |= __put_user(from->si_timer2, &to->si_timer2); err |= __put_user(from->si_overrun, &to->si_overrun);
err |= __put_user(from->si_ptr, &to->si_ptr);
break; break;
case __SI_POLL: case __SI_POLL:
err |= __put_user(from->si_band, &to->si_band); err |= __put_user(from->si_band, &to->si_band);
......
...@@ -52,12 +52,11 @@ typedef struct tvec_root_s { ...@@ -52,12 +52,11 @@ typedef struct tvec_root_s {
struct list_head vec[TVR_SIZE]; struct list_head vec[TVR_SIZE];
} tvec_root_t; } tvec_root_t;
typedef struct timer_list timer_t;
struct tvec_t_base_s { struct tvec_t_base_s {
spinlock_t lock; spinlock_t lock;
unsigned long timer_jiffies; unsigned long timer_jiffies;
timer_t *running_timer; struct timer_list *running_timer;
tvec_root_t tv1; tvec_root_t tv1;
tvec_t tv2; tvec_t tv2;
tvec_t tv3; tvec_t tv3;
...@@ -70,7 +69,7 @@ typedef struct tvec_t_base_s tvec_base_t; ...@@ -70,7 +69,7 @@ typedef struct tvec_t_base_s tvec_base_t;
/* Fake initialization */ /* Fake initialization */
static DEFINE_PER_CPU(tvec_base_t, tvec_bases) = { SPIN_LOCK_UNLOCKED }; static DEFINE_PER_CPU(tvec_base_t, tvec_bases) = { SPIN_LOCK_UNLOCKED };
static void check_timer_failed(timer_t *timer) static void check_timer_failed(struct timer_list *timer)
{ {
static int whine_count; static int whine_count;
if (whine_count < 16) { if (whine_count < 16) {
...@@ -88,13 +87,13 @@ static void check_timer_failed(timer_t *timer) ...@@ -88,13 +87,13 @@ static void check_timer_failed(timer_t *timer)
timer->magic = TIMER_MAGIC; timer->magic = TIMER_MAGIC;
} }
static inline void check_timer(timer_t *timer) static inline void check_timer(struct timer_list *timer)
{ {
if (timer->magic != TIMER_MAGIC) if (timer->magic != TIMER_MAGIC)
check_timer_failed(timer); check_timer_failed(timer);
} }
static inline void internal_add_timer(tvec_base_t *base, timer_t *timer) static inline void internal_add_timer(tvec_base_t *base, struct timer_list *timer)
{ {
unsigned long expires = timer->expires; unsigned long expires = timer->expires;
unsigned long idx = expires - base->timer_jiffies; unsigned long idx = expires - base->timer_jiffies;
...@@ -146,7 +145,7 @@ static inline void internal_add_timer(tvec_base_t *base, timer_t *timer) ...@@ -146,7 +145,7 @@ static inline void internal_add_timer(tvec_base_t *base, timer_t *timer)
* Timers with an ->expired field in the past will be executed in the next * Timers with an ->expired field in the past will be executed in the next
* timer tick. It's illegal to add an already pending timer. * timer tick. It's illegal to add an already pending timer.
*/ */
void add_timer(timer_t *timer) void add_timer(struct timer_list *timer)
{ {
int cpu = get_cpu(); int cpu = get_cpu();
tvec_base_t *base = &per_cpu(tvec_bases, cpu); tvec_base_t *base = &per_cpu(tvec_bases, cpu);
...@@ -204,7 +203,7 @@ void add_timer_on(struct timer_list *timer, int cpu) ...@@ -204,7 +203,7 @@ void add_timer_on(struct timer_list *timer, int cpu)
* (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an * (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
* active timer returns 1.) * active timer returns 1.)
*/ */
int mod_timer(timer_t *timer, unsigned long expires) int mod_timer(struct timer_list *timer, unsigned long expires)
{ {
tvec_base_t *old_base, *new_base; tvec_base_t *old_base, *new_base;
unsigned long flags; unsigned long flags;
...@@ -281,7 +280,7 @@ int mod_timer(timer_t *timer, unsigned long expires) ...@@ -281,7 +280,7 @@ int mod_timer(timer_t *timer, unsigned long expires)
* (ie. del_timer() of an inactive timer returns 0, del_timer() of an * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
* active timer returns 1.) * active timer returns 1.)
*/ */
int del_timer(timer_t *timer) int del_timer(struct timer_list *timer)
{ {
unsigned long flags; unsigned long flags;
tvec_base_t *base; tvec_base_t *base;
...@@ -320,7 +319,7 @@ int del_timer(timer_t *timer) ...@@ -320,7 +319,7 @@ int del_timer(timer_t *timer)
* *
* The function returns whether it has deactivated a pending timer or not. * The function returns whether it has deactivated a pending timer or not.
*/ */
int del_timer_sync(timer_t *timer) int del_timer_sync(struct timer_list *timer)
{ {
tvec_base_t *base; tvec_base_t *base;
int i, ret = 0; int i, ret = 0;
...@@ -363,9 +362,9 @@ static int cascade(tvec_base_t *base, tvec_t *tv) ...@@ -363,9 +362,9 @@ static int cascade(tvec_base_t *base, tvec_t *tv)
* detach them individually, just clear the list afterwards. * detach them individually, just clear the list afterwards.
*/ */
while (curr != head) { while (curr != head) {
timer_t *tmp; struct timer_list *tmp;
tmp = list_entry(curr, timer_t, entry); tmp = list_entry(curr, struct timer_list, entry);
if (tmp->base != base) if (tmp->base != base)
BUG(); BUG();
next = curr->next; next = curr->next;
...@@ -404,9 +403,9 @@ static inline void __run_timers(tvec_base_t *base) ...@@ -404,9 +403,9 @@ static inline void __run_timers(tvec_base_t *base)
if (curr != head) { if (curr != head) {
void (*fn)(unsigned long); void (*fn)(unsigned long);
unsigned long data; unsigned long data;
timer_t *timer; struct timer_list *timer;
timer = list_entry(curr, timer_t, entry); timer = list_entry(curr, struct timer_list, entry);
fn = timer->function; fn = timer->function;
data = timer->data; data = timer->data;
...@@ -508,6 +507,7 @@ static void second_overflow(void) ...@@ -508,6 +507,7 @@ static void second_overflow(void)
if (xtime.tv_sec % 86400 == 0) { if (xtime.tv_sec % 86400 == 0) {
xtime.tv_sec--; xtime.tv_sec--;
time_state = TIME_OOP; time_state = TIME_OOP;
clock_was_set();
printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n"); printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n");
} }
break; break;
...@@ -516,6 +516,7 @@ static void second_overflow(void) ...@@ -516,6 +516,7 @@ static void second_overflow(void)
if ((xtime.tv_sec + 1) % 86400 == 0) { if ((xtime.tv_sec + 1) % 86400 == 0) {
xtime.tv_sec++; xtime.tv_sec++;
time_state = TIME_WAIT; time_state = TIME_WAIT;
clock_was_set();
printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n"); printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n");
} }
break; break;
...@@ -968,7 +969,7 @@ static void process_timeout(unsigned long __data) ...@@ -968,7 +969,7 @@ static void process_timeout(unsigned long __data)
*/ */
signed long schedule_timeout(signed long timeout) signed long schedule_timeout(signed long timeout)
{ {
timer_t timer; struct timer_list timer;
unsigned long expire; unsigned long expire;
switch (timeout) switch (timeout)
...@@ -1023,6 +1024,7 @@ asmlinkage long sys_gettid(void) ...@@ -1023,6 +1024,7 @@ asmlinkage long sys_gettid(void)
{ {
return current->pid; return current->pid;
} }
#ifndef FOLD_NANO_SLEEP_INTO_CLOCK_NANO_SLEEP
static long nanosleep_restart(struct restart_block *restart) static long nanosleep_restart(struct restart_block *restart)
{ {
...@@ -1081,6 +1083,7 @@ asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp) ...@@ -1081,6 +1083,7 @@ asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp)
} }
return ret; return ret;
} }
#endif // ! FOLD_NANO_SLEEP_INTO_CLOCK_NANO_SLEEP
/* /*
* sys_sysinfo - fill in sysinfo struct * sys_sysinfo - fill in sysinfo struct
......
...@@ -10,7 +10,7 @@ L_TARGET := lib.a ...@@ -10,7 +10,7 @@ L_TARGET := lib.a
obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \
bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \ bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
kobject.o kobject.o idr.o
obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
......
/*
* linux/kernel/id.c
*
* 2002-10-18 written by Jim Houston jim.houston@ccur.com
* Copyright (C) 2002 by Concurrent Computer Corporation
* Distributed under the GNU GPL license version 2.
*
* Small id to pointer translation service.
*
* It uses a radix tree like structure as a sparse array indexed
* by the id to obtain the pointer. The bitmap makes allocating
* a new id quick.
* Modified by George Anzinger to reuse immediately and to use
* find bit instructions. Also removed _irq on spinlocks.
* So here is what this bit of code does:
* You call it to allocate an id (an int) an associate with that id a
* pointer or what ever, we treat it as a (void *). You can pass this
* id to a user for him to pass back at a later time. You then pass
* that id to this code and it returns your pointer.
* You can release ids at any time. When all ids are released, most of
* the memory is returned (we keep IDR_FREE_MAX) in a local pool so we
* don't need to go to the memory "store" during an id allocate, just
* so you don't need to be too concerned about locking and conflicts
* with the slab allocator.
* A word on reuse. We reuse empty id slots as soon as we can, always
* using the lowest one available. But we also merge a counter in the
* high bits of the id. The counter is RESERVED_ID_BITS (8 at this time)
* long. This means that if you allocate and release the same id in a
* loop we will reuse an id after about 256 times around the loop. The
* word about is used here as we will NOT return a valid id of -1 so if
* you loop on the largest possible id (and that is 24 bits, wow!) we
* will kick the counter to avoid -1. (Paranoid? You bet!)
*
* What you need to do is, since we don't keep the counter as part of
* id / ptr pair, to keep a copy of it in the pointed to structure
* (or else where) so that when you ask for a ptr you can varify that
* the returned ptr is correct by comparing the id it contains with the one
* you asked for. In other words, we only did half the reuse protection.
* Since the code depends on your code doing this check, we ignore high
* order bits in the id, not just the count, but bits that would, if used,
* index outside of the allocated ids. In other words, if the largest id
* currently allocated is 32 a look up will only look at the low 5 bits of
* the id. Since you will want to keep this id in the structure anyway
* (if for no other reason than to be able to eliminate the id when the
* structure is found in some other way) this seems reasonable. If you
* really think otherwise, the code to check these bits here, it is just
* disabled with a #if 0.
* So here are the complete details:
* include <linux/idr.h>
* void idr_init(struct idr *idp)
* This function is use to set up the handle (idp) that you will pass
* to the rest of the functions. The structure is defined in the
* header.
* int idr_pre_get(struct idr *idp)
* This function should be called prior to locking and calling the
* following function. It pre allocates enough memory to satisfy the
* worst possible allocation. It can sleep, so must not be called
* with any spinlocks held. If the system is REALLY out of memory
* this function returns 0, other wise 1.
* int idr_get_new(struct idr *idp, void *ptr);
* This is the allocate id function. It should be called with any
* required locks. In fact, in the SMP case, you MUST lock prior to
* calling this function to avoid possible out of memory problems. If
* memory is required, it will return a -1, in which case you should
* unlock and go back to the idr_pre_get() call. ptr is the pointer
* you want associated with the id. In other words:
* void *idr_find(struct idr *idp, int id);
* returns the "ptr", given the id. A NULL return indicates that the
* id is not valid (or you passed NULL in the idr_get_new(), shame on
* you). This function must be called with a spinlock that prevents
* calling either idr_get_new() or idr_remove() or idr_find() while it
* is working.
* void idr_remove(struct idr *idp, int id);
* removes the given id, freeing that slot and any memory that may
* now be unused. See idr_find() for locking restrictions.
*/
#ifndef TEST // to test in user space...
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#endif
#include <linux/string.h>
#include <linux/idr.h>
static kmem_cache_t *idr_layer_cache;
static inline struct idr_layer *alloc_layer(struct idr *idp)
{
struct idr_layer *p;
spin_lock(&idp->lock);
if (!(p = idp->id_free))
BUG();
idp->id_free = p->ary[0];
idp->id_free_cnt--;
p->ary[0] = 0;
spin_unlock(&idp->lock);
return(p);
}
static inline void free_layer(struct idr *idp, struct idr_layer *p)
{
/*
* Depends on the return element being zeroed.
*/
spin_lock(&idp->lock);
p->ary[0] = idp->id_free;
idp->id_free = p;
idp->id_free_cnt++;
spin_unlock(&idp->lock);
}
int idr_pre_get(struct idr *idp)
{
while (idp->id_free_cnt < idp->layers + 1) {
struct idr_layer *new;
new = kmem_cache_alloc(idr_layer_cache, GFP_KERNEL);
if(new == NULL)
return (0);
free_layer(idp, new);
}
return 1;
}
EXPORT_SYMBOL(idr_pre_get);
static inline int sub_alloc(struct idr *idp, int shift, void *ptr)
{
int n, v = 0;
struct idr_layer *p;
struct idr_layer **pa[MAX_LEVEL];
struct idr_layer ***paa = &pa[0];
*paa = NULL;
*++paa = &idp->top;
/*
* By keeping each pointer in an array we can do the
* "after" recursion processing. In this case, that means
* we can update the upper level bit map.
*/
while (1){
p = **paa;
n = ffz(p->bitmap);
if (shift){
/*
* We run around this while until we
* reach the leaf node...
*/
if (!p->ary[n]){
/*
* If no node, allocate one, AFTER
* we insure that we will not
* intrude on the reserved bit field.
*/
if ((n << shift) >= MAX_ID_BIT)
return -1;
p->ary[n] = alloc_layer(idp);
p->count++;
}
*++paa = &p->ary[n];
v += (n << shift);
shift -= IDR_BITS;
} else {
/*
* We have reached the leaf node, plant the
* users pointer and return the raw id.
*/
p->ary[n] = (struct idr_layer *)ptr;
__set_bit(n, &p->bitmap);
v += n;
p->count++;
/*
* This is the post recursion processing. Once
* we find a bitmap that is not full we are
* done
*/
while (*(paa-1) && (**paa)->bitmap == IDR_FULL){
n = *paa - &(**(paa-1))->ary[0];
__set_bit(n, &(**--paa)->bitmap);
}
return(v);
}
}
}
int idr_get_new(struct idr *idp, void *ptr)
{
int v;
if (idp->id_free_cnt < idp->layers + 1)
return (-1);
/*
* Add a new layer if the array is full
*/
if (unlikely(!idp->top || idp->top->bitmap == IDR_FULL)){
/*
* This is a bit different than the lower layers because
* we have one branch already allocated and full.
*/
struct idr_layer *new = alloc_layer(idp);
new->ary[0] = idp->top;
if ( idp->top)
++new->count;
idp->top = new;
if ( idp->layers++ )
__set_bit(0, &new->bitmap);
}
v = sub_alloc(idp, (idp->layers - 1) * IDR_BITS, ptr);
if ( likely(v >= 0 )){
idp->count++;
v += (idp->count << MAX_ID_SHIFT);
if ( unlikely( v == -1 ))
v += (1 << MAX_ID_SHIFT);
}
return(v);
}
EXPORT_SYMBOL(idr_get_new);
static inline void sub_remove(struct idr *idp, int shift, int id)
{
struct idr_layer *p = idp->top;
struct idr_layer **pa[MAX_LEVEL];
struct idr_layer ***paa = &pa[0];
*paa = NULL;
*++paa = &idp->top;
while ((shift > 0) && p) {
int n = (id >> shift) & IDR_MASK;
__clear_bit(n, &p->bitmap);
*++paa = &p->ary[n];
p = p->ary[n];
shift -= IDR_BITS;
}
if ( likely(p)){
int n = id & IDR_MASK;
__clear_bit(n, &p->bitmap);
p->ary[n] = NULL;
while(*paa && ! --((**paa)->count)){
free_layer(idp, **paa);
**paa-- = NULL;
}
if ( ! *paa )
idp->layers = 0;
}
}
void idr_remove(struct idr *idp, int id)
{
struct idr_layer *p;
sub_remove(idp, (idp->layers - 1) * IDR_BITS, id);
if ( idp->top && idp->top->count == 1 &&
(idp->layers > 1) &&
idp->top->ary[0]){ // We can drop a layer
p = idp->top->ary[0];
idp->top->bitmap = idp->top->count = 0;
free_layer(idp, idp->top);
idp->top = p;
--idp->layers;
}
while (idp->id_free_cnt >= IDR_FREE_MAX) {
p = alloc_layer(idp);
kmem_cache_free(idr_layer_cache, p);
return;
}
}
EXPORT_SYMBOL(idr_remove);
void *idr_find(struct idr *idp, int id)
{
int n;
struct idr_layer *p;
n = idp->layers * IDR_BITS;
p = idp->top;
#if 0
/*
* This tests to see if bits outside the current tree are
* present. If so, tain't one of ours!
*/
if ( unlikely( (id & ~(~0 << MAX_ID_SHIFT)) >> (n + IDR_BITS)))
return NULL;
#endif
while (n > 0 && p) {
n -= IDR_BITS;
p = p->ary[(id >> n) & IDR_MASK];
}
return((void *)p);
}
EXPORT_SYMBOL(idr_find);
static void idr_cache_ctor(void * idr_layer,
kmem_cache_t *idr_layer_cache, unsigned long flags)
{
memset(idr_layer, 0, sizeof(struct idr_layer));
}
static int init_id_cache(void)
{
if (!idr_layer_cache)
idr_layer_cache = kmem_cache_create("idr_layer_cache",
sizeof(struct idr_layer), 0, 0, idr_cache_ctor, 0);
return 0;
}
void idr_init(struct idr *idp)
{
init_id_cache();
memset(idp, 0, sizeof(struct idr));
spin_lock_init(&idp->lock);
}
EXPORT_SYMBOL(idr_init);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment