This introduce the CLOCK_REALTIME part of ptimer, which allows to remove
all the special handling of absolute timers in posix-timers.c.
Signed-off-by: Roman Zippel <[email protected]>
---
include/linux/posix-timers.h | 7
include/linux/ptimer.h | 1
kernel/posix-timers.c | 372 +++++++++----------------------------------
kernel/ptimer.c | 12 +
4 files changed, 95 insertions(+), 297 deletions(-)
Index: linux-2.6-mm/include/linux/posix-timers.h
===================================================================
--- linux-2.6-mm.orig/include/linux/posix-timers.h 2005-11-28 23:54:07.000000000 +0100
+++ linux-2.6-mm/include/linux/posix-timers.h 2005-11-29 00:41:48.000000000 +0100
@@ -53,8 +53,6 @@ struct k_itimer {
union {
struct {
struct ptimer timer;
- struct list_head abs_timer_entry; /* clock abs_timer_list */
- ktime_t wall_to_prev; /* wall_to_monotonic used when set */
ktime_t incr; /* interval */
} real;
struct cpu_timer_list cpu;
@@ -67,14 +65,9 @@ struct k_itimer {
} it;
};
-struct k_clock_abs {
- struct list_head list;
- spinlock_t lock;
-};
struct k_clock {
int res; /* in nano seconds */
int (*clock_getres) (clockid_t which_clock, struct timespec *tp);
- struct k_clock_abs *abs_struct;
int (*clock_set) (clockid_t which_clock, struct timespec * tp);
int (*clock_get) (clockid_t which_clock, struct timespec * tp);
int (*timer_create) (struct k_itimer *timer);
Index: linux-2.6-mm/include/linux/ptimer.h
===================================================================
--- linux-2.6-mm.orig/include/linux/ptimer.h 2005-11-28 23:54:07.000000000 +0100
+++ linux-2.6-mm/include/linux/ptimer.h 2005-11-29 00:41:48.000000000 +0100
@@ -37,6 +37,7 @@ struct ptimer {
};
extern void ptimer_init(struct ptimer *timer, int base);
+extern void ptimer_set_base(struct ptimer *timer, int base);
extern void ptimer_start(struct ptimer *timer);
extern void ptimer_modify(struct ptimer *timer, ktime_t);
extern int ptimer_stop(struct ptimer *timer);
Index: linux-2.6-mm/kernel/posix-timers.c
===================================================================
--- linux-2.6-mm.orig/kernel/posix-timers.c 2005-11-28 23:54:07.000000000 +0100
+++ linux-2.6-mm/kernel/posix-timers.c 2005-11-29 00:41:48.000000000 +0100
@@ -138,12 +138,6 @@ static DEFINE_SPINLOCK(idr_lock);
*/
static struct k_clock posix_clocks[MAX_CLOCKS];
-/*
- * We only have one real clock that can be set so we need only one abs list,
- * even if we should want to have several clocks with differing resolutions.
- */
-static struct k_clock_abs abs_list = {.list = LIST_HEAD_INIT(abs_list.list),
- .lock = SPIN_LOCK_UNLOCKED};
static int posix_timer_fn(struct ptimer *timer);
static void do_posix_clock_monotonic_gettime_parts(struct timespec *tp,
@@ -195,9 +189,7 @@ static inline int common_clock_set(clock
static inline int common_timer_create(struct k_itimer *new_timer)
{
- INIT_LIST_HEAD(&new_timer->it.real.abs_timer_entry);
ptimer_init(&new_timer->it.real.timer, CLOCK_MONOTONIC);
- new_timer->it.real.timer.function = posix_timer_fn;
return 0;
}
@@ -235,10 +227,8 @@ static inline int invalid_clockid(clocki
static __init int init_posix_timers(void)
{
struct k_clock clock_realtime = {.res = CLOCK_REALTIME_RES,
- .abs_struct = &abs_list
};
struct k_clock clock_monotonic = {.res = CLOCK_REALTIME_RES,
- .abs_struct = NULL,
.clock_get = do_posix_clock_monotonic_get,
.clock_set = do_posix_clock_nosettime
};
@@ -270,91 +260,23 @@ static u64 ktime_to_jiffie(const ktime_t
(NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
}
-/*
- * This function adjusts the timer as needed as a result of the clock
- * being set. It should only be called for absolute timers, and then
- * under the abs_list lock. It computes the time difference and sets
- * the new jiffies value in the timer. It also updates the timers
- * reference wall_to_monotonic value. It is complicated by the fact
- * that tstojiffies() only handles positive times and it needs to work
- * with both positive and negative times. Also, for negative offsets,
- * we need to defeat the res round up.
- *
- * Return is true if there is a new time, else false.
- */
-static long add_clockset_delta(struct k_itimer *timr,
- ktime_t new_wall_to)
-{
- ktime_t delta;
-
- delta = ktime_sub(new_wall_to, timr->it.real.wall_to_prev);
- if (delta.tv64 == KTIME_ZERO)
- return 0;
- timr->it.real.wall_to_prev = new_wall_to;
- timr->it.real.timer.expires = ktime_add(timr->it.real.timer.expires, delta);
- return 1;
-}
-
-static void remove_from_abslist(struct k_itimer *timr)
-{
- if (!list_empty(&timr->it.real.abs_timer_entry)) {
- spin_lock(&abs_list.lock);
- list_del_init(&timr->it.real.abs_timer_entry);
- spin_unlock(&abs_list.lock);
- }
-}
-
-static void posix_advance_timer(struct k_itimer *timr, ktime_t now)
+static void posix_advance_timer(struct k_itimer *timr)
{
+ struct now_struct now;
ktime_t t;
u64 j;
unsigned long i, rem;
- if (now.tv64 < timr->it.real.timer.expires.tv64)
+ posix_get_now(timr->it.real.timer.base->index, &now);
+ if (now.now.tv64 < timr->it.real.timer.expires.tv64)
return;
- t = ktime_sub(now, timr->it.real.timer.expires);
+ t = ktime_sub(now.now, timr->it.real.timer.expires);
i = ktime_to_jiffie(timr->it.real.incr, TICK_NSEC);
j = ktime_to_jiffie(t, 0);
rem = do_div(j, i);
timr->it_overrun += j + 1;
t = nsec_to_ktime((u64)(i - rem) * TICK_NSEC);
- timr->it.real.timer.expires = ktime_add(now, t);
-}
-
-static int schedule_next_timer(struct k_itimer *timr)
-{
- struct now_struct now;
-
- /*
- * Set up the timer for the next interval (if there is one).
- * Note: this code uses the abs_timer_lock to protect
- * it.real.wall_to_prev and must hold it until exp is set, not exactly
- * obvious...
-
- * This function is used for CLOCK_REALTIME* and
- * CLOCK_MONOTONIC* timers. If we ever want to handle other
- * CLOCKs, the calling code (do_schedule_next_timer) would need
- * to pull the "clock" info from the timer and dispatch the
- * "other" CLOCKs "next timer" code (which, I suppose should
- * also be added to the k_clock structure).
- */
- if (timr->it.real.incr.tv64 == KTIME_ZERO)
- return 0;
-
- posix_get_now(CLOCK_MONOTONIC, &now);
-
- if (!list_empty(&timr->it.real.abs_timer_entry)) {
- spin_lock(&abs_list.lock);
- add_clockset_delta(timr, now.to_mono);
-
- posix_advance_timer(timr, now.now);
-
- spin_unlock(&abs_list.lock);
- } else {
- posix_advance_timer(timr, now.now);
- }
- timr->it_overrun--;
- return 1;
+ timr->it.real.timer.expires = ktime_add(now.now, t);
}
/*
@@ -386,8 +308,14 @@ void do_schedule_next_timer(struct sigin
} else {
BUG_ON(!timr->it_requeue_pending);
if (timr->it_requeue_pending > 1 &&
- schedule_next_timer(timr))
+ timr->it.real.incr.tv64 != KTIME_ZERO) {
+ /*
+ * Set up the timer for the next interval.
+ */
+ posix_advance_timer(timr);
+ timr->it_overrun--;
ptimer_start(&timr->it.real.timer);
+ }
timr->it_requeue_pending = 0;
info->si_overrun = timr->it_overrun_last = timr->it_overrun;
timr->it_overrun = 0;
@@ -448,21 +376,6 @@ static int posix_timer_fn(struct ptimer
int do_restart = 0;
spin_lock_irqsave(&timr->it_lock, flags);
- if (!list_empty(&timr->it.real.abs_timer_entry)) {
- struct now_struct now;
-
- spin_lock(&abs_list.lock);
- posix_get_now(CLOCK_MONOTONIC, &now);
- add_clockset_delta(timr, now.to_mono);
- if (now.now.tv64 < timr->it.real.timer.expires.tv64) {
- /* timer is early due to a clock set */
- spin_unlock(&abs_list.lock);
- do_restart = 1;
- goto exit;
- }
- list_del_init(&timr->it.real.abs_timer_entry);
- spin_unlock(&abs_list.lock);
- }
if (!timr->it_requeue_pending) {
timr->it_requeue_pending = 1;
@@ -476,15 +389,27 @@ static int posix_timer_fn(struct ptimer
do_restart = 1;
}
} else {
+ timer->expires = timer->base->last_expired;
timr->it_requeue_pending = 2;
timr->it_overrun++;
}
-exit:
+
unlock_timer(timr, flags); /* hold thru abs lock to keep irq off */
return do_restart;
}
+static int posix_real_timer_fn(struct ptimer *timer)
+{
+ ptimer_set_base(timer, CLOCK_MONOTONIC);
+
+ if (posix_timer_fn(timer)) {
+ timer->function = posix_timer_fn;
+ ptimer_start(timer);
+ }
+
+ return 0;
+}
static inline struct task_struct * good_sigevent(sigevent_t * event)
{
@@ -748,19 +673,23 @@ static struct k_itimer * lock_timer(time
static void
common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
{
- ktime_t expires;
- struct now_struct now;
+ struct timespec ts_now, ts_to_mono;
+ ktime_t expires, now;
expires = timr->it.real.timer.expires;
- posix_get_now(CLOCK_MONOTONIC, &now);
-
- if (ptimer_active(&timr->it.real.timer)) {
- expires = ktime_sub(timr->it.real.timer.expires, now.now);
- } else if (timr->it_requeue_pending) {
- posix_advance_timer(timr, now.now);
- expires = ktime_sub(timr->it.real.timer.expires, now.now);
+ do_posix_clock_monotonic_gettime_parts(&ts_now, &ts_to_mono);
+ now = timespec_to_ktime(ts_now);
+ if (timr->it.real.timer.base->index == CLOCK_MONOTONIC)
+ now = ktime_add(now, timespec_to_ktime(ts_to_mono));
+
+ if (expires.tv64 != KTIME_ZERO) {
+ if (timr->it_requeue_pending > 1 ||
+ (timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE)
+ posix_advance_timer(timr);
+ expires = ktime_sub(timr->it.real.timer.expires, now);
}
+
cur_setting->it_value = ktime_to_timespec(expires);
cur_setting->it_interval = ktime_to_timespec(timr->it.real.incr);
@@ -817,35 +746,6 @@ sys_timer_getoverrun(timer_t timer_id)
return overrun;
}
-/*
- * Adjust for absolute time
- *
- * If absolute time is given and it is not CLOCK_MONOTONIC, we need to
- * adjust for the offset between the timer clock (CLOCK_MONOTONIC) and
- * what ever clock he is using.
- *
- * If it is relative time, we need to add the current (CLOCK_MONOTONIC)
- * time to it to get the proper time for the timer.
- */
-static int adjust_abs_time(struct k_clock *clock, struct timespec *tp,
- int abs, struct ptimer *timer, ktime_t *to_mono)
-{
- struct now_struct now;
- int rtn = 0;
-
- timer->expires = timespec_to_ktime(*tp);
- if (!abs) {
- posix_get_now(CLOCK_MONOTONIC, &now);
- now.now = ktime_add_nsec(now.now, clock->res);
- timer->expires = ktime_add(now.now, timer->expires);
- } else if ((clock - posix_clocks) == CLOCK_REALTIME) {
- posix_get_now(CLOCK_MONOTONIC, &now);
- if (to_mono)
- *to_mono = now.to_mono;
- timer->expires = ktime_add(timer->expires, now.to_mono);
- }
- return rtn;
-}
/* Set a POSIX.1b interval timer. */
/* timr->it_lock is taken. */
@@ -853,7 +753,7 @@ static inline int
common_timer_set(struct k_itimer *timr, int flags,
struct itimerspec *new_setting, struct itimerspec *old_setting)
{
- struct k_clock *clock = &posix_clocks[timr->it_clock];
+ clock_t which_clock = timr->it_clock;
if (old_setting)
common_timer_get(timr, old_setting);
@@ -867,39 +767,40 @@ common_timer_set(struct k_itimer *timr,
if (ptimer_try_to_stop(&timr->it.real.timer) < 0)
return TIMER_RETRY;
- remove_from_abslist(timr);
-
if (timr->it_requeue_pending)
timr->it_requeue_pending = 1;
timr->it_overrun = 0;
/*
- *switch off the timer when it_value is zero
+ * switch off the timer when it_value is zero
*/
if (!new_setting->it_value.tv_sec && !new_setting->it_value.tv_nsec) {
timr->it.real.timer.expires.tv64 = KTIME_ZERO;
return 0;
}
- if (adjust_abs_time(clock, &new_setting->it_value,
- flags & TIMER_ABSTIME, &timr->it.real.timer,
- &timr->it.real.wall_to_prev))
- return -EINVAL;
-
+ timr->it.real.timer.function = posix_timer_fn;
+ timr->it.real.timer.expires = timespec_to_ktime(new_setting->it_value);
timr->it.real.incr = timespec_to_ktime(new_setting->it_interval);
+ if (!(flags & TIMER_ABSTIME)) {
+ struct now_struct now;
+
+ posix_get_now(CLOCK_MONOTONIC, &now);
+ timr->it.real.timer.expires = ktime_add(timr->it.real.timer.expires,
+ now.now);
+ timr->it.real.timer.expires = ktime_add_nsec(timr->it.real.timer.expires,
+ TICK_NSEC);
+ which_clock = CLOCK_MONOTONIC;
+ } else if (which_clock == CLOCK_REALTIME)
+ timr->it.real.timer.function = posix_real_timer_fn;
+ ptimer_set_base(&timr->it.real.timer, which_clock);
+
/*
- * We do not even queue SIGEV_NONE timers! But we do put them
- * in the abs list so we can do that right.
+ * We do not even queue SIGEV_NONE timers!
*/
- if (((timr->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE))
+ if ((timr->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE)
ptimer_start(&timr->it.real.timer);
- if (flags & TIMER_ABSTIME && clock->abs_struct) {
- spin_lock(&clock->abs_struct->lock);
- list_add_tail(&(timr->it.real.abs_timer_entry),
- &(clock->abs_struct->list));
- spin_unlock(&clock->abs_struct->lock);
- }
return 0;
}
@@ -963,8 +864,6 @@ static inline int common_timer_del(struc
#endif
}
- remove_from_abslist(timer);
-
return 0;
}
@@ -1196,105 +1095,8 @@ static int nanosleep_wake_up(struct ptim
return 0;
}
-/*
- * The standard says that an absolute nanosleep call MUST wake up at
- * the requested time in spite of clock settings. Here is what we do:
- * For each nanosleep call that needs it (only absolute and not on
- * CLOCK_MONOTONIC* (as it can not be set)) we thread a little structure
- * into the "nanosleep_abs_list". All we need is the task_struct pointer.
- * When ever the clock is set we just wake up all those tasks. The rest
- * is done by the while loop in clock_nanosleep().
- *
- * On locking, clock_was_set() is called from update_wall_clock which
- * holds (or has held for it) a write_lock_irq( xtime_lock) and is
- * called from the timer bh code. Thus we need the irq save locks.
- *
- * Also, on the call from update_wall_clock, that is done as part of a
- * softirq thing. We don't want to delay the system that much (possibly
- * long list of timers to fix), so we defer that work to keventd.
- */
-
-static DECLARE_WAIT_QUEUE_HEAD(nanosleep_abs_wqueue);
-static DECLARE_WORK(clock_was_set_work, (void(*)(void*))clock_was_set, NULL);
-
-static DECLARE_MUTEX(clock_was_set_lock);
-
void clock_was_set(void)
{
- struct k_itimer *timr;
- ktime_t new_wall_to;
- LIST_HEAD(cws_list);
- unsigned long seq;
-
-
- if (unlikely(in_interrupt())) {
- schedule_work(&clock_was_set_work);
- return;
- }
- wake_up_all(&nanosleep_abs_wqueue);
-
- /*
- * Check if there exist TIMER_ABSTIME timers to correct.
- *
- * Notes on locking: This code is run in task context with irq
- * on. We CAN be interrupted! All other usage of the abs list
- * lock is under the timer lock which holds the irq lock as
- * well. We REALLY don't want to scan the whole list with the
- * interrupt system off, AND we would like a sequence lock on
- * this code as well. Since we assume that the clock will not
- * be set often, it seems ok to take and release the irq lock
- * for each timer. In fact add_timer will do this, so this is
- * not an issue. So we know when we are done, we will move the
- * whole list to a new location. Then as we process each entry,
- * we will move it to the actual list again. This way, when our
- * copy is empty, we are done. We are not all that concerned
- * about preemption so we will use a semaphore lock to protect
- * aginst reentry. This way we will not stall another
- * processor. It is possible that this may delay some timers
- * that should have expired, given the new clock, but even this
- * will be minimal as we will always update to the current time,
- * even if it was set by a task that is waiting for entry to
- * this code. Timers that expire too early will be caught by
- * the expire code and restarted.
-
- * Absolute timers that repeat are left in the abs list while
- * waiting for the task to pick up the signal. This means we
- * may find timers that are not in the "add_timer" list, but are
- * in the abs list. We do the same thing for these, save
- * putting them back in the "add_timer" list. (Note, these are
- * left in the abs list mainly to indicate that they are
- * ABSOLUTE timers, a fact that is used by the re-arm code, and
- * for which we have no other flag.)
-
- */
-
- down(&clock_was_set_lock);
- spin_lock_irq(&abs_list.lock);
- list_splice_init(&abs_list.list, &cws_list);
- spin_unlock_irq(&abs_list.lock);
- do {
- do {
- seq = read_seqbegin(&xtime_lock);
- new_wall_to = timespec_to_ktime(wall_to_monotonic);
- } while (read_seqretry(&xtime_lock, seq));
-
- spin_lock_irq(&abs_list.lock);
- if (list_empty(&cws_list)) {
- spin_unlock_irq(&abs_list.lock);
- break;
- }
- timr = list_entry(cws_list.next, struct k_itimer,
- it.real.abs_timer_entry);
-
- list_del_init(&timr->it.real.abs_timer_entry);
- if (add_clockset_delta(timr, new_wall_to) &&
- ptimer_stop(&timr->it.real.timer)) /* timer run yet? */
- ptimer_start(&timr->it.real.timer);
- list_add(&timr->it.real.abs_timer_entry, &abs_list.list);
- spin_unlock_irq(&abs_list.lock);
- } while (1);
-
- up(&clock_was_set_lock);
}
long clock_nanosleep_restart(struct restart_block *restart_block);
@@ -1335,20 +1137,22 @@ sys_clock_nanosleep(clockid_t which_cloc
static int common_nsleep(clockid_t which_clock,
int flags, struct timespec *tsave)
{
- struct timespec t;
struct nsleep_timer timer;
struct now_struct now;
- DECLARE_WAITQUEUE(abs_wqueue, current);
int abs;
struct restart_block *restart_block =
¤t_thread_info()->restart_block;
- abs_wqueue.flags = 0;
- ptimer_init(&timer.timer, CLOCK_MONOTONIC);
+ abs = flags & TIMER_ABSTIME;
+ if (!abs)
+ which_clock = CLOCK_MONOTONIC;
+
+ posix_get_now(which_clock, &now);
+
+ ptimer_init(&timer.timer, which_clock);
timer.timer.function = nanosleep_wake_up;
timer.task = current;
timer.done = 0;
- abs = flags & TIMER_ABSTIME;
if (restart_block->fn == clock_nanosleep_restart) {
/*
@@ -1359,47 +1163,35 @@ static int common_nsleep(clockid_t which
timer.timer.expires.tv64 = (s64)restart_block->arg3 << 32;
timer.timer.expires.tv64 += restart_block->arg2;
+ } else {
+ timer.timer.expires = timespec_to_ktime(*tsave);
+ if (!abs) {
+ timer.timer.expires = ktime_add(now.now, timer.timer.expires);
+ timer.timer.expires = ktime_add_nsec(timer.timer.expires,
+ TICK_NSEC);
+ }
}
- if (abs && (posix_clocks[which_clock].clock_get !=
- posix_clocks[CLOCK_MONOTONIC].clock_get))
- add_wait_queue(&nanosleep_abs_wqueue, &abs_wqueue);
+ if (now.now.tv64 > timer.timer.expires.tv64)
+ return 0;
- do {
- t = *tsave;
- if (abs || timer.timer.expires.tv64 == KTIME_ZERO) {
- adjust_abs_time(&posix_clocks[which_clock], &t,
- abs, &timer.timer, NULL);
- }
+ ptimer_start(&timer.timer);
- posix_get_now(CLOCK_MONOTONIC, &now);
- if (now.now.tv64 > timer.timer.expires.tv64) {
- timer.done = 1;
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (timer.done || signal_pending(current))
break;
- }
-
- ptimer_start(&timer.timer);
-
schedule();
+ }
- ptimer_stop(&timer.timer);
- } while (!signal_pending(current));
-
- if (abs_wqueue.task_list.next)
- finish_wait(&nanosleep_abs_wqueue, &abs_wqueue);
-
+ ptimer_stop(&timer.timer);
if (timer.done)
return 0;
- /*
- * Always restart abs calls from scratch to pick up any
- * clock shifting that happened while we are away.
- */
if (abs)
return -ERESTARTNOHAND;
-
/*
- * Restart works by saving the time remaing in
+ * Restart works by saving the expiry time in
* arg2 & 3 (it is 64-bits of jiffies). The other
* info we need is the clock_id (saved in arg0).
* The sys_call interface needs the users
@@ -1415,7 +1207,7 @@ static int common_nsleep(clockid_t which
restart_block->arg2 = timer.timer.expires.tv64 & 0xffffffffLL;
restart_block->arg3 = timer.timer.expires.tv64 >> 32;
- posix_get_now(CLOCK_MONOTONIC, &now);
+ posix_get_now(which_clock, &now);
now.now = ktime_sub(timer.timer.expires, now.now);
*tsave = ktime_to_timespec(now.now);
Index: linux-2.6-mm/kernel/ptimer.c
===================================================================
--- linux-2.6-mm.orig/kernel/ptimer.c 2005-11-29 00:41:44.000000000 +0100
+++ linux-2.6-mm/kernel/ptimer.c 2005-11-29 00:41:48.000000000 +0100
@@ -53,6 +53,12 @@ void ptimer_init(struct ptimer *timer, i
timer->node.rb_parent = PTIMER_INACTIVE;
}
+void ptimer_set_base(struct ptimer *timer, int base)
+{
+ BUG_ON(ptimer_active(timer));
+ timer->base = &per_cpu(ptimer_bases[base], raw_smp_processor_id());
+}
+
int ptimer_active(struct ptimer *timer)
{
return timer->node.rb_parent != PTIMER_INACTIVE;
@@ -273,6 +279,10 @@ void ptimer_run_queues(void)
return;
do {
base[CLOCK_MONOTONIC].last_jiffies = last;
+ base[CLOCK_REALTIME].last_expired =
+ ktime_add_nsec(base[CLOCK_REALTIME].last_expired,
+ TICK_NSEC);
+ ptimer_run_queue(&base[CLOCK_REALTIME]);
base[CLOCK_MONOTONIC].last_expired =
ktime_add_nsec(base[CLOCK_MONOTONIC].last_expired,
TICK_NSEC);
@@ -281,6 +291,8 @@ void ptimer_run_queues(void)
}
base[CLOCK_MONOTONIC].last_jiffies = last;
+ base[CLOCK_REALTIME].last_expired = now;
+ ptimer_run_queue(&base[CLOCK_REALTIME]);
base[CLOCK_MONOTONIC].last_expired = ktime_add(now, mono);
ptimer_run_queue(&base[CLOCK_MONOTONIC]);
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]