summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorMadhavan Venkataraman <Madhavan.Venkataraman@Sun.COM>2009-01-18 10:21:18 -0800
committerMadhavan Venkataraman <Madhavan.Venkataraman@Sun.COM>2009-01-18 10:21:18 -0800
commit454ab20244cd84c2b93aa273b462eab1166cf539 (patch)
tree6c5bf39f1b21e05e87c0fb9981fb2e23504a9b75 /usr
parent7e48531733326c7034772ff72376656c5b1183ca (diff)
downloadillumos-gate-454ab20244cd84c2b93aa273b462eab1166cf539.tar.gz
6784948 Bug fixes to the Callout implementation putback in SNV 103
Diffstat (limited to 'usr')
-rw-r--r--usr/src/uts/common/os/callout.c111
-rw-r--r--usr/src/uts/common/os/clock.c5
-rw-r--r--usr/src/uts/common/os/condvar.c23
-rw-r--r--usr/src/uts/common/os/cpu.c15
-rw-r--r--usr/src/uts/common/sys/callo.h25
-rw-r--r--usr/src/uts/common/sys/thread.h4
-rw-r--r--usr/src/uts/i86pc/io/cbe.c4
7 files changed, 121 insertions, 66 deletions
diff --git a/usr/src/uts/common/os/callout.c b/usr/src/uts/common/os/callout.c
index a3eccfa518..65e94f0f59 100644
--- a/usr/src/uts/common/os/callout.c
+++ b/usr/src/uts/common/os/callout.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -43,6 +43,7 @@
static hrtime_t callout_debug_hrtime; /* debugger entry time */
static int callout_min_resolution; /* Minimum resolution */
static callout_table_t *callout_boot_ct; /* Boot CPU's callout tables */
+static clock_t callout_max_ticks; /* max interval */
static hrtime_t callout_longterm; /* longterm nanoseconds */
static ulong_t callout_counter_low; /* callout ID increment */
static ulong_t callout_table_bits; /* number of table bits in ID */
@@ -377,7 +378,7 @@ callout_heap_insert(callout_table_t *ct, hrtime_t expiration)
* entered, the cyclic will be programmed for the earliest expiration
* in the heap.
*/
- if (callout_upheap(ct) && !(ct->ct_flags & CALLOUT_TABLE_SUSPENDED))
+ if (callout_upheap(ct) && (ct->ct_suspend == 0))
(void) cyclic_reprogram(ct->ct_cyclic, expiration);
}
@@ -521,12 +522,22 @@ callout_heap_delete(callout_table_t *ct)
* by CPR, just return. The cyclic has already been programmed to
* infinity by the cyclic subsystem.
*/
- if ((ct->ct_heap_num == 0) || (ct->ct_flags & CALLOUT_TABLE_SUSPENDED))
+ if ((ct->ct_heap_num == 0) || (ct->ct_suspend > 0))
return;
(void) cyclic_reprogram(ct->ct_cyclic, expiration);
}
+/*
+ * Common function used to create normal and realtime callouts.
+ *
+ * Realtime callouts are handled at CY_LOW_PIL by a cyclic handler. So,
+ * there is one restriction on a realtime callout handler - it should not
+ * directly or indirectly acquire cpu_lock. CPU offline waits for pending
+ * cyclic handlers to complete while holding cpu_lock. So, if a realtime
+ * callout handler were to try to get cpu_lock, there would be a deadlock
+ * during CPU offline.
+ */
callout_id_t
timeout_generic(int type, void (*func)(void *), void *arg,
hrtime_t expiration, hrtime_t resolution, int flags)
@@ -596,6 +607,13 @@ timeout_generic(int type, void (*func)(void *), void *arg,
if (flags & CALLOUT_FLAG_ROUNDUP)
expiration += resolution - 1;
expiration = (expiration / resolution) * resolution;
+ if (expiration <= 0) {
+ /*
+ * expiration hrtime overflow has occurred. Just set the
+ * expiration to infinity.
+ */
+ expiration = CY_INFINITY;
+ }
/*
* Assign an ID to this callout
@@ -705,6 +723,8 @@ timeout(void (*func)(void *), void *arg, clock_t delta)
*/
if (delta <= 0)
delta = 1;
+ else if (delta > callout_max_ticks)
+ delta = callout_max_ticks;
id = (ulong_t)timeout_generic(CALLOUT_NORMAL, func, arg,
TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
@@ -726,6 +746,8 @@ timeout_default(void (*func)(void *), void *arg, clock_t delta)
*/
if (delta <= 0)
delta = 1;
+ else if (delta > callout_max_ticks)
+ delta = callout_max_ticks;
id = timeout_generic(CALLOUT_NORMAL, func, arg, TICK_TO_NSEC(delta),
nsec_per_tick, 0);
@@ -743,6 +765,8 @@ realtime_timeout(void (*func)(void *), void *arg, clock_t delta)
*/
if (delta <= 0)
delta = 1;
+ else if (delta > callout_max_ticks)
+ delta = callout_max_ticks;
id = (ulong_t)timeout_generic(CALLOUT_REALTIME, func, arg,
TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
@@ -764,6 +788,8 @@ realtime_timeout_default(void (*func)(void *), void *arg, clock_t delta)
*/
if (delta <= 0)
delta = 1;
+ else if (delta > callout_max_ticks)
+ delta = callout_max_ticks;
id = timeout_generic(CALLOUT_REALTIME, func, arg, TICK_TO_NSEC(delta),
nsec_per_tick, 0);
@@ -1092,12 +1118,14 @@ callout_suspend(void)
ct = &callout_table[CALLOUT_TABLE(t, f)];
mutex_enter(&ct->ct_mutex);
- ct->ct_flags |= CALLOUT_TABLE_SUSPENDED;
+ ct->ct_suspend++;
if (ct->ct_cyclic == CYCLIC_NONE) {
mutex_exit(&ct->ct_mutex);
continue;
}
- (void) cyclic_reprogram(ct->ct_cyclic, CY_INFINITY);
+ if (ct->ct_suspend == 1)
+ (void) cyclic_reprogram(ct->ct_cyclic,
+ CY_INFINITY);
mutex_exit(&ct->ct_mutex);
}
}
@@ -1172,7 +1200,7 @@ callout_resume(hrtime_t delta)
mutex_enter(&ct->ct_mutex);
if (ct->ct_cyclic == CYCLIC_NONE) {
- ct->ct_flags &= ~CALLOUT_TABLE_SUSPENDED;
+ ct->ct_suspend--;
mutex_exit(&ct->ct_mutex);
continue;
}
@@ -1180,21 +1208,23 @@ callout_resume(hrtime_t delta)
if (delta)
callout_adjust(ct, delta);
- ct->ct_flags &= ~CALLOUT_TABLE_SUSPENDED;
-
- /*
- * If the expired list is non-empty, then have the
- * cyclic expire immediately. Else, program the
- * cyclic based on the heap.
- */
- if (ct->ct_expired.ch_head != NULL)
- exp = gethrtime();
- else if (ct->ct_heap_num > 0)
- exp = ct->ct_heap[0];
- else
- exp = 0;
- if (exp != 0)
- (void) cyclic_reprogram(ct->ct_cyclic, exp);
+ ct->ct_suspend--;
+ if (ct->ct_suspend == 0) {
+ /*
+ * If the expired list is non-empty, then have
+ * the cyclic expire immediately. Else, program
+ * the cyclic based on the heap.
+ */
+ if (ct->ct_expired.ch_head != NULL)
+ exp = gethrtime();
+ else if (ct->ct_heap_num > 0)
+ exp = ct->ct_heap[0];
+ else
+ exp = 0;
+ if (exp != 0)
+ (void) cyclic_reprogram(ct->ct_cyclic,
+ exp);
+ }
mutex_exit(&ct->ct_mutex);
}
}
@@ -1280,7 +1310,7 @@ callout_hrestime_one(callout_table_t *ct)
if (ecl->cl_callouts.ch_head != NULL) {
CALLOUT_LIST_APPEND(ct->ct_expired, ecl);
- if (!(ct->ct_flags & CALLOUT_TABLE_SUSPENDED))
+ if (ct->ct_suspend == 0)
(void) cyclic_reprogram(ct->ct_cyclic, gethrtime());
} else {
ecl->cl_next = ct->ct_lfree;
@@ -1412,16 +1442,8 @@ callout_cyclic_init(callout_table_t *ct)
*/
ASSERT(ct->ct_cyclic == CYCLIC_NONE);
- /*
- * Ideally, the handlers for CALLOUT_REALTIME and CALLOUT_NORMAL should
- * be run at CY_LOW_LEVEL. But there are some callers of the delay(9F)
- * function that call delay(9F) illegally from PIL > 0. delay(9F) uses
- * normal callouts. In order to avoid a deadlock, we run the normal
- * handler from LOCK level. When the delay(9F) issue is fixed, this
- * should be fixed as well.
- */
hdlr.cyh_func = (cyc_func_t)CALLOUT_CYCLIC_HANDLER(t);
- hdlr.cyh_level = (t == CALLOUT_REALTIME) ? CY_LOW_LEVEL : CY_LOCK_LEVEL;
+ hdlr.cyh_level = CY_LOW_LEVEL;
hdlr.cyh_arg = ct;
when.cyt_when = CY_INFINITY;
when.cyt_interval = CY_INFINITY;
@@ -1498,11 +1520,30 @@ callout_cpu_online(cpu_t *cp)
mutex_exit(&ct->ct_mutex);
/*
- * Move the cyclic to this CPU by doing a bind. Then unbind
- * the cyclic. This will allow the cyclic subsystem to juggle
- * the cyclic during CPU offline.
+ * Move the cyclic to this CPU by doing a bind.
*/
cyclic_bind(ct->ct_cyclic, cp, NULL);
+ }
+}
+
+void
+callout_cpu_offline(cpu_t *cp)
+{
+ callout_table_t *ct;
+ processorid_t seqid;
+ int t;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ seqid = cp->cpu_seqid;
+
+ for (t = 0; t < CALLOUT_NTYPES; t++) {
+ ct = &callout_table[CALLOUT_TABLE(t, seqid)];
+
+ /*
+ * Unbind the cyclic. This will allow the cyclic subsystem
+ * to juggle the cyclic during CPU offline.
+ */
cyclic_bind(ct->ct_cyclic, NULL, NULL);
}
}
@@ -1549,6 +1590,7 @@ callout_init(void)
callout_table_mask = (1 << callout_table_bits) - 1;
callout_counter_low = 1 << CALLOUT_COUNTER_SHIFT;
callout_longterm = TICK_TO_NSEC(CALLOUT_LONGTERM_TICKS);
+ callout_max_ticks = CALLOUT_MAX_TICKS;
/*
* Because of the variability in timing behavior across systems with
@@ -1603,6 +1645,7 @@ callout_init(void)
for (t = 0; t < CALLOUT_NTYPES; t++) {
table_id = CALLOUT_TABLE(t, f);
ct = &callout_table[table_id];
+ ct->ct_type = t;
mutex_init(&ct->ct_mutex, NULL, MUTEX_DEFAULT, NULL);
/*
* Precompute the base IDs for long and short-term
diff --git a/usr/src/uts/common/os/clock.c b/usr/src/uts/common/os/clock.c
index 9f6ba19769..45f7f53e39 100644
--- a/usr/src/uts/common/os/clock.c
+++ b/usr/src/uts/common/os/clock.c
@@ -23,7 +23,7 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -1591,8 +1591,9 @@ delay(clock_t ticks)
timeout_id_t id;
extern hrtime_t volatile devinfo_freeze;
- if ((panicstr || devinfo_freeze) && ticks > 0) {
+ if ((getpil() > 0 || panicstr || devinfo_freeze) && ticks > 0) {
/*
+ * Either the caller's PIL is not safe for timeout wait or
* Timeouts aren't running, so all we can do is spin.
*/
drv_usecwait(TICK_TO_USEC(ticks));
diff --git a/usr/src/uts/common/os/condvar.c b/usr/src/uts/common/os/condvar.c
index 344746063a..cb1543e767 100644
--- a/usr/src/uts/common/os/condvar.c
+++ b/usr/src/uts/common/os/condvar.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -206,10 +206,9 @@ cv_wakeup(void *arg)
* This mutex is acquired and released in order to make sure that
* the wakeup does not happen before the block itself happens.
*/
- mutex_enter(t->t_wait_mp);
- mutex_exit(t->t_wait_mp);
+ mutex_enter(&t->t_wait_mutex);
+ mutex_exit(&t->t_wait_mutex);
setrun(t);
- t->t_wait_mp = NULL;
}
/*
@@ -233,11 +232,12 @@ cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t tim)
timeleft = tim - lbolt;
if (timeleft <= 0)
return (-1);
- t->t_wait_mp = mp;
+ mutex_enter(&t->t_wait_mutex);
id = realtime_timeout_default((void (*)(void *))cv_wakeup, t, timeleft);
thread_lock(t); /* lock the thread */
cv_block((condvar_impl_t *)cvp);
thread_unlock_nopreempt(t);
+ mutex_exit(&t->t_wait_mutex);
mutex_exit(mp);
swtch();
signalled = (t->t_schedflag & TS_SIGNALLED);
@@ -248,7 +248,7 @@ cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t tim)
* we called untimeout. We will treat this as if the timeout
* has occured and set timeleft to -1.
*/
- timeleft = (t->t_wait_mp == NULL) ? -1 : untimeout_default(id, 0);
+ timeleft = untimeout_default(id, 0);
mutex_enter(mp);
if (timeleft <= 0) {
timeleft = -1;
@@ -363,7 +363,7 @@ cv_timedwait_sig_internal(kcondvar_t *cvp, kmutex_t *mp, clock_t tim, int flag)
* Set the timeout and wait.
*/
cancel_pending = schedctl_cancel_pending();
- t->t_wait_mp = mp;
+ mutex_enter(&t->t_wait_mutex);
id = timeout_generic(CALLOUT_REALTIME, (void (*)(void *))cv_wakeup, t,
TICK_TO_NSEC(timeleft), nsec_per_tick, flag);
lwp->lwp_asleep = 1;
@@ -371,6 +371,7 @@ cv_timedwait_sig_internal(kcondvar_t *cvp, kmutex_t *mp, clock_t tim, int flag)
thread_lock(t);
cv_block_sig(t, (condvar_impl_t *)cvp);
thread_unlock_nopreempt(t);
+ mutex_exit(&t->t_wait_mutex);
mutex_exit(mp);
if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t) || cancel_pending)
setrun(t);
@@ -386,7 +387,7 @@ cv_timedwait_sig_internal(kcondvar_t *cvp, kmutex_t *mp, clock_t tim, int flag)
* we called untimeout. We will treat this as if the timeout
* has occured and set rval to -1.
*/
- rval = (t->t_wait_mp == NULL) ? -1 : untimeout_default(id, 0);
+ rval = untimeout_default(id, 0);
mutex_enter(mp);
if (rval <= 0)
rval = -1;
@@ -593,17 +594,17 @@ cv_wait_stop(kcondvar_t *cvp, kmutex_t *mp, int wakeup_time)
* Wakeup in wakeup_time milliseconds, i.e., human time.
*/
tim = lbolt + MSEC_TO_TICK(wakeup_time);
- t->t_wait_mp = mp;
+ mutex_enter(&t->t_wait_mutex);
id = realtime_timeout_default((void (*)(void *))cv_wakeup, t,
tim - lbolt);
thread_lock(t); /* lock the thread */
cv_block((condvar_impl_t *)cvp);
thread_unlock_nopreempt(t);
+ mutex_exit(&t->t_wait_mutex);
mutex_exit(mp);
/* ASSERT(no locks are held); */
swtch();
- if (t->t_wait_mp != NULL)
- (void) untimeout_default(id, 0);
+ (void) untimeout_default(id, 0);
/*
* Check for reasons to stop, if lwp_nostop is not true.
diff --git a/usr/src/uts/common/os/cpu.c b/usr/src/uts/common/os/cpu.c
index d07e926084..74dd46fa17 100644
--- a/usr/src/uts/common/os/cpu.c
+++ b/usr/src/uts/common/os/cpu.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -1231,6 +1231,7 @@ cpu_offline(cpu_t *cp, int flags)
cpu_t *ncp;
int intr_enable;
int cyclic_off = 0;
+ int callout_off = 0;
int loop_count;
int no_quiesce = 0;
int (*bound_func)(struct cpu *, int);
@@ -1356,6 +1357,11 @@ again: for (loop_count = 0; (*bound_func)(cp, 0); loop_count++) {
delay(hz/100);
}
+ if (error == 0 && callout_off == 0) {
+ callout_cpu_offline(cp);
+ callout_off = 1;
+ }
+
if (error == 0 && cyclic_off == 0) {
if (!cyclic_offline(cp)) {
/*
@@ -1535,6 +1541,13 @@ out:
cyclic_online(cp);
/*
+ * If we failed, but managed to offline callouts on this CPU,
+ * bring it back online.
+ */
+ if (error && callout_off)
+ callout_cpu_online(cp);
+
+ /*
* If we failed, tell the PG subsystem that the CPU is back
*/
pg_cpupart_in(cp, pp);
diff --git a/usr/src/uts/common/sys/callo.h b/usr/src/uts/common/sys/callo.h
index 9402cfa050..95ec7e769c 100644
--- a/usr/src/uts/common/sys/callo.h
+++ b/usr/src/uts/common/sys/callo.h
@@ -23,7 +23,7 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -300,14 +300,6 @@ typedef enum callout_stat_type {
#endif
/*
- * Callout table flags:
- *
- * CALLOUT_TABLE_SUSPENDED
- * Callout processing on this table has been suspended.
- */
-#define CALLOUT_TABLE_SUSPENDED 0x1UL
-
-/*
* All of the state information associated with a callout table.
* The fields are ordered with cache performance in mind.
*/
@@ -321,7 +313,8 @@ typedef struct callout_table {
callout_hash_t *ct_clhash; /* callout list hash */
kstat_named_t *ct_kstat_data; /* callout kstat data */
- ulong_t ct_flags; /* flags */
+ uint_t ct_type; /* callout table type */
+ uint_t ct_suspend; /* suspend count */
cyclic_id_t ct_cyclic; /* cyclic for this table */
hrtime_t *ct_heap; /* callout expiration heap */
ulong_t ct_heap_num; /* occupied slots in the heap */
@@ -336,7 +329,7 @@ typedef struct callout_table {
#ifdef _LP64
ulong_t ct_pad[4]; /* cache alignment */
#else
- ulong_t ct_pad[8]; /* cache alignment */
+ ulong_t ct_pad[7]; /* cache alignment */
#endif
} callout_table_t;
@@ -377,12 +370,18 @@ typedef struct callout_table {
#define CALLOUT_ALIGN 64 /* cache line size */
+#ifdef _LP64
+#define CALLOUT_MAX_TICKS NSEC_TO_TICK(CY_INFINITY);
+#else
+#define CALLOUT_MAX_TICKS LONG_MAX
+#endif
+
extern void callout_init(void);
extern void membar_sync(void);
-extern void callout_cpu_configure(cpu_t *);
extern void callout_cpu_online(cpu_t *);
-extern void callout_cpu_init(cpu_t *);
+extern void callout_cpu_offline(cpu_t *);
extern void callout_hrestime(void);
+
#endif
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/thread.h b/usr/src/uts/common/sys/thread.h
index 85198171d7..6d112ef065 100644
--- a/usr/src/uts/common/sys/thread.h
+++ b/usr/src/uts/common/sys/thread.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -341,7 +341,7 @@ typedef struct _kthread {
hrtime_t t_hrtime; /* high-res last time on cpu */
kmutex_t t_ctx_lock; /* protects t_ctx in removectx() */
struct waitq *t_waitq; /* wait queue */
- kmutex_t *t_wait_mp; /* used in CV wait functions */
+ kmutex_t t_wait_mutex; /* used in CV wait functions */
} kthread_t;
/*
diff --git a/usr/src/uts/i86pc/io/cbe.c b/usr/src/uts/i86pc/io/cbe.c
index 29f0176cc2..b1163e1ba1 100644
--- a/usr/src/uts/i86pc/io/cbe.c
+++ b/usr/src/uts/i86pc/io/cbe.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -201,8 +201,6 @@ cbe_xcall(void *arg, cpu_t *dest, cyc_func_t func, void *farg)
mutex_exit(&cbe_xcall_lock);
kpreempt_enable();
-
- ASSERT(cbe_xcall_func == NULL && cbe_xcall_cpu == NULL);
}
void *