diff options
author | lq150181 <none@none> | 2005-11-27 18:29:20 -0800 |
---|---|---|
committer | lq150181 <none@none> | 2005-11-27 18:29:20 -0800 |
commit | e23a7e348c32cb3a1a7072dcac6905a150a028d7 (patch) | |
tree | 42f9a8a991cd2b8de00f045760de644937cef8eb | |
parent | 64dbca9e47a5b508a6b6994fd8c3b903027ef952 (diff) | |
download | illumos-joyent-e23a7e348c32cb3a1a7072dcac6905a150a028d7.tar.gz |
6348316 cyclic subsystem baffled by x86 softint behavior
-rw-r--r-- | usr/src/uts/common/io/avintr.c | 111 | ||||
-rw-r--r-- | usr/src/uts/common/sys/avintr.h | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ddi_intr_impl.h | 4 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/cbe.c | 11 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/ddi_impl.c | 30 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/mp_machdep.c | 26 | ||||
-rw-r--r-- | usr/src/uts/i86pc/sys/smp_impldefs.h | 7 |
7 files changed, 134 insertions, 56 deletions
diff --git a/usr/src/uts/common/io/avintr.c b/usr/src/uts/common/io/avintr.c index e790c27e76..9236b3f4f2 100644 --- a/usr/src/uts/common/io/avintr.c +++ b/usr/src/uts/common/io/avintr.c @@ -48,7 +48,11 @@ #include <sys/stack.h> #include <sys/ddi_impldefs.h> -static int insert_av(void *intr_id, struct av_head *vectp, avfunc f, +typedef struct av_softinfo { + cpuset_t av_pending; /* pending bitmasks */ +} av_softinfo_t; + +static void insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1, caddr_t arg2, uint64_t *ticksp, int pri_level, dev_info_t *dip); static void remove_av(void *intr_id, struct av_head *vectp, avfunc f, @@ -78,11 +82,37 @@ struct av_head autovect[MAX_VECT]; struct av_head softvect[LOCK_LEVEL + 1]; kmutex_t av_lock; ddi_softint_hdl_impl_t softlevel1_hdl = - {0, NULL, NULL, 0, 0, NULL, NULL, NULL}; + {0, NULL, NULL, NULL, 0, NULL, NULL, NULL}; + +/* + * clear/check softint pending flag corresponding for + * the current CPU + */ +void +av_clear_softint_pending(av_softinfo_t *infop) +{ + CPUSET_ATOMIC_DEL(infop->av_pending, CPU->cpu_seqid); +} + +boolean_t +av_check_softint_pending(av_softinfo_t *infop, boolean_t check_all) +{ + if (check_all) + return (!CPUSET_ISNULL(infop->av_pending)); + else + return (CPU_IN_SET(infop->av_pending, CPU->cpu_seqid) != 0); +} + +/* + * It first sets our av softint pending bit for the current CPU, + * then it sets the CPU softint pending bit for pri. + */ void -set_pending(int pri) +av_set_softint_pending(int pri, av_softinfo_t *infop) { + CPUSET_ATOMIC_ADD(infop->av_pending, CPU->cpu_seqid); + atomic_or_32((uint32_t *)&CPU->cpu_softinfo.st_pending, 1 << pri); } @@ -191,8 +221,7 @@ add_avintr(void *intr_id, int lvl, avfunc xxintr, char *name, int vect, cmn_err(CE_NOTE, multilevel, vect); } - if (!insert_av(intr_id, vecp, f, arg1, arg2, ticksp, lvl, dip)) - return (0); + insert_av(intr_id, vecp, f, arg1, arg2, ticksp, lvl, dip); s = splhi(); /* * do what ever machine specific things are necessary @@ -233,6 +262,7 @@ add_avsoftintr(void *intr_id, int lvl, avfunc xxintr, char *name, caddr_t arg1, caddr_t arg2) { int slvl; + ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)intr_id; if ((slvl = slvltovect(lvl)) != -1) return (add_avintr(intr_id, lvl, xxintr, @@ -254,15 +284,19 @@ add_avsoftintr(void *intr_id, int lvl, avfunc xxintr, char *name, printf(badsoft, lvl, name); return (0); } - if (!insert_av(intr_id, &softvect[lvl], xxintr, arg1, arg2, NULL, - lvl, NULL)) { - return (0); + + if (hdlp->ih_pending == NULL) { + hdlp->ih_pending = + kmem_zalloc(sizeof (av_softinfo_t), KM_SLEEP); } + + insert_av(intr_id, &softvect[lvl], xxintr, arg1, arg2, NULL, lvl, NULL); + return (1); } /* insert an interrupt vector into chain */ -static int +static void insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1, caddr_t arg2, uint64_t *ticksp, int pri_level, dev_info_t *dip) { @@ -288,7 +322,7 @@ insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1, vectp->avh_hi_pri = vectp->avh_lo_pri = (ushort_t)pri_level; mutex_exit(&av_lock); - return (1); + return; } /* find where it goes in list */ @@ -309,7 +343,7 @@ insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1, } p->av_vector = f; mutex_exit(&av_lock); - return (1); + return; } } /* insert new intpt at beginning of chain */ @@ -322,18 +356,15 @@ insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1, vectp->avh_lo_pri = (ushort_t)pri_level; } mutex_exit(&av_lock); - - return (1); } -/* - * Remove a driver from the autovector list. - */ -int -rem_avsoftintr(void *intr_id, int lvl, avfunc xxintr) +static int +av_rem_softintr(void *intr_id, int lvl, avfunc xxintr, boolean_t rem_softinfo) { struct av_head *vecp = (struct av_head *)0; int slvl; + ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)intr_id; + av_softinfo_t *infop = (av_softinfo_t *)hdlp->ih_pending; if (xxintr == NULL) return (0); @@ -349,9 +380,40 @@ rem_avsoftintr(void *intr_id, int lvl, avfunc xxintr) vecp = &softvect[lvl]; remove_av(intr_id, vecp, xxintr, lvl, 0); + if (rem_softinfo) { + kmem_free(infop, sizeof (av_softinfo_t)); + hdlp->ih_pending = NULL; + } + return (1); } +int +av_softint_movepri(void *intr_id, int old_lvl) +{ + int ret; + ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)intr_id; + + ret = add_avsoftintr(intr_id, hdlp->ih_pri, hdlp->ih_cb_func, + DEVI(hdlp->ih_dip)->devi_name, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2); + + if (ret) { + (void) av_rem_softintr(intr_id, old_lvl, hdlp->ih_cb_func, + B_FALSE); + } + + return (ret); +} + +/* + * Remove a driver from the autovector list. + */ +int +rem_avsoftintr(void *intr_id, int lvl, avfunc xxintr) +{ + return (av_rem_softintr(intr_id, lvl, xxintr, B_TRUE)); +} + void rem_avintr(void *intr_id, int lvl, avfunc xxintr, int vect) { @@ -480,8 +542,7 @@ remove_av(void *intr_id, struct av_head *vectp, avfunc f, int pri_level, void siron(void) { - softlevel1_hdl.ih_pending = 1; - (*setsoftint)(1); + (*setsoftint)(1, softlevel1_hdl.ih_pending); } /* @@ -556,8 +617,14 @@ av_dispatch_softvect(uint_t pil) hdlp = (ddi_softint_hdl_impl_t *)av->av_intr_id; ASSERT(hdlp); - if (hdlp->ih_pending) { - hdlp->ih_pending = 0; + /* + * Each cpu has its own pending bit in hdlp->ih_pending, + * here av_check/clear_softint_pending is just checking + * and clearing the pending bit for the current cpu, who + * has just triggered a softint. + */ + if (av_check_softint_pending(hdlp->ih_pending, B_FALSE)) { + av_clear_softint_pending(hdlp->ih_pending); (void) (*intr)(arg1, arg2); } } diff --git a/usr/src/uts/common/sys/avintr.h b/usr/src/uts/common/sys/avintr.h index 77ce12eee3..9f907d58b1 100644 --- a/usr/src/uts/common/sys/avintr.h +++ b/usr/src/uts/common/sys/avintr.h @@ -94,6 +94,7 @@ extern int add_nmintr(int lvl, avfunc nmintr, char *name, caddr_t arg); extern int add_avsoftintr(void *intr_id, int lvl, avfunc xxintr, char *name, caddr_t arg1, caddr_t arg2); extern int rem_avsoftintr(void *intr_id, int lvl, avfunc xxintr); +extern int av_softint_movepri(void *intr_id, int old_lvl); extern void update_avsoftintr_args(void *intr_id, int lvl, caddr_t arg2); extern void rem_avintr(void *intr_id, int lvl, avfunc xxintr, int vect); extern void wait_till_seen(int ipl); diff --git a/usr/src/uts/common/sys/ddi_intr_impl.h b/usr/src/uts/common/sys/ddi_intr_impl.h index 1afcca6faa..692e5d353b 100644 --- a/usr/src/uts/common/sys/ddi_intr_impl.h +++ b/usr/src/uts/common/sys/ddi_intr_impl.h @@ -116,6 +116,8 @@ typedef struct ddi_intr_handle_impl { #define DDI_INTR_SUP_TYPES DDI_INTR_TYPE_FIXED|DDI_INTR_TYPE_MSI|\ DDI_INTR_TYPE_MSIX +struct av_softinfo; + /* * One such data structure is allocated per ddi_soft_intr_handle * This is the incore copy of the softint info. @@ -124,7 +126,7 @@ typedef struct ddi_softint_hdl_impl { dev_info_t *ih_dip; /* dip associated with handle */ uint_t ih_pri; /* priority - bus dependent */ krwlock_t ih_rwlock; /* read/write lock per handle */ - uint_t ih_pending; /* whether softint is pending */ + struct av_softinfo *ih_pending; /* whether softint is pending */ uint_t (*ih_cb_func)(caddr_t, caddr_t); /* cb function for soft ints */ diff --git a/usr/src/uts/i86pc/io/cbe.c b/usr/src/uts/i86pc/io/cbe.c index 83c6b4f276..5f1c2577bb 100644 --- a/usr/src/uts/i86pc/io/cbe.c +++ b/usr/src/uts/i86pc/io/cbe.c @@ -38,6 +38,7 @@ #include <sys/clock.h> #include <sys/ddi_impldefs.h> #include <sys/ddi_intr.h> +#include <sys/avintr.h> static int cbe_vector; static int cbe_ticks = 0; @@ -48,9 +49,9 @@ static void *cbe_xcall_farg; static cpuset_t cbe_enabled; static ddi_softint_hdl_impl_t cbe_low_hdl = - {0, NULL, NULL, 0, 0, NULL, NULL, NULL}; + {0, NULL, NULL, NULL, 0, NULL, NULL, NULL}; static ddi_softint_hdl_impl_t cbe_clock_hdl = - {0, NULL, NULL, 0, 0, NULL, NULL, NULL}; + {0, NULL, NULL, NULL, 0, NULL, NULL, NULL}; cyclic_id_t cbe_hres_cyclic; int cbe_psm_timer_mode = TIMER_ONESHOT; @@ -112,12 +113,10 @@ cbe_softint(void *arg, cyc_level_t level) { switch (level) { case CY_LOW_LEVEL: - cbe_low_hdl.ih_pending = 1; - (*setsoftint)(CBE_LOW_PIL); + (*setsoftint)(CBE_LOW_PIL, cbe_low_hdl.ih_pending); break; case CY_LOCK_LEVEL: - cbe_clock_hdl.ih_pending = 1; - (*setsoftint)(CBE_LOCK_PIL); + (*setsoftint)(CBE_LOCK_PIL, cbe_clock_hdl.ih_pending); break; default: panic("cbe_softint: unexpected soft level %d", level); diff --git a/usr/src/uts/i86pc/os/ddi_impl.c b/usr/src/uts/i86pc/os/ddi_impl.c index 085a9351b3..c60ae037e8 100644 --- a/usr/src/uts/i86pc/os/ddi_impl.c +++ b/usr/src/uts/i86pc/os/ddi_impl.c @@ -850,23 +850,19 @@ i_ddi_remove_softint(ddi_softint_hdl_impl_t *hdlp) } -extern void (*setsoftint)(int); +extern void (*setsoftint)(int, struct av_softinfo *); +extern boolean_t av_check_softint_pending(struct av_softinfo *, boolean_t); int i_ddi_trigger_softint(ddi_softint_hdl_impl_t *hdlp, void *arg2) { - int ret = DDI_SUCCESS; + if (av_check_softint_pending(hdlp->ih_pending, B_FALSE)) + return (DDI_EPENDING); - if (hdlp->ih_pending) { - ret = DDI_EPENDING; - } else { - update_avsoftintr_args((void *)hdlp, - hdlp->ih_pri, arg2); - hdlp->ih_pending = 1; - } + update_avsoftintr_args((void *)hdlp, hdlp->ih_pri, arg2); - (*setsoftint)(hdlp->ih_pri); - return (ret); + (*setsoftint)(hdlp->ih_pri, hdlp->ih_pending); + return (DDI_SUCCESS); } /* @@ -879,18 +875,16 @@ i_ddi_trigger_softint(ddi_softint_hdl_impl_t *hdlp, void *arg2) int i_ddi_set_softint_pri(ddi_softint_hdl_impl_t *hdlp, uint_t old_pri) { + int ret; + /* * If a softint is pending at the old priority then fail the request. - * OR - * If we failed to add a softint vector with the new priority; then - * fail the request with a DDI_FAILURE */ - if (hdlp->ih_pending || i_ddi_add_softint(hdlp) != DDI_SUCCESS) + if (av_check_softint_pending(hdlp->ih_pending, B_TRUE)) return (DDI_FAILURE); - /* Now, remove the softint at the old priority */ - (void) rem_avsoftintr((void *)hdlp, old_pri, hdlp->ih_cb_func); - return (DDI_SUCCESS); + ret = av_softint_movepri((void *)hdlp, old_pri); + return (ret ? DDI_SUCCESS : DDI_FAILURE); } void diff --git a/usr/src/uts/i86pc/os/mp_machdep.c b/usr/src/uts/i86pc/os/mp_machdep.c index e6dc73b7f0..2a95e4083d 100644 --- a/usr/src/uts/i86pc/os/mp_machdep.c +++ b/usr/src/uts/i86pc/os/mp_machdep.c @@ -60,7 +60,7 @@ static uint64_t mach_getcpufreq(void); static void mach_fixcpufreq(void); static int mach_clkinit(int, int *); static void mach_smpinit(void); -static void mach_set_softintr(int ipl); +static void mach_set_softintr(int ipl, struct av_softinfo *); static void mach_cpu_start(int cpun); static int mach_softlvl_to_vect(int ipl); static void mach_get_platform(int owner); @@ -105,7 +105,8 @@ void (*send_dirintf)() = return_instr; void (*setspl)(int) = return_instr; int (*addspl)(int, int, int, int) = (int (*)(int, int, int, int))return_instr; int (*delspl)(int, int, int, int) = (int (*)(int, int, int, int))return_instr; -void (*setsoftint)(int) = (void (*)(int))return_instr; +void (*setsoftint)(int, struct av_softinfo *)= + (void (*)(int, struct av_softinfo *))return_instr; int (*slvltovect)(int) = (int (*)(int))return_instr; int (*setlvl)(int, int *) = (int (*)(int, int *))return_instr; void (*setlvlx)(int, int) = (void (*)(int, int))return_instr; @@ -932,6 +933,17 @@ mach_clkinit(int preferred_mode, int *set_mode) } } +/*ARGSUSED*/ +static void +mach_psm_set_softintr(int ipl, struct av_softinfo *pending) +{ + register struct psm_ops *pops; + + /* invoke hardware interrupt */ + pops = mach_set[0]; + (*pops->psm_set_softintr)(ipl); +} + static int mach_softlvl_to_vect(register int ipl) { @@ -942,19 +954,19 @@ mach_softlvl_to_vect(register int ipl) /* check for null handler for set soft interrupt call */ if (pops->psm_set_softintr == NULL) { - setsoftint = set_pending; + setsoftint = av_set_softint_pending; return (PSM_SV_SOFTWARE); } softvect = (*pops->psm_softlvl_to_irq)(ipl); /* check for hardware scheme */ if (softvect > PSM_SV_SOFTWARE) { - setsoftint = pops->psm_set_softintr; + setsoftint = mach_psm_set_softintr; return (softvect); } if (softvect == PSM_SV_SOFTWARE) - setsoftint = set_pending; + setsoftint = av_set_softint_pending; else /* hardware and software mixed scheme */ setsoftint = mach_set_softintr; @@ -962,12 +974,12 @@ mach_softlvl_to_vect(register int ipl) } static void -mach_set_softintr(register int ipl) +mach_set_softintr(register int ipl, struct av_softinfo *pending) { register struct psm_ops *pops; /* set software pending bits */ - set_pending(ipl); + av_set_softint_pending(ipl, pending); /* check if dosoftint will be called at the end of intr */ if (CPU_ON_INTR(CPU) || (curthread->t_intr)) diff --git a/usr/src/uts/i86pc/sys/smp_impldefs.h b/usr/src/uts/i86pc/sys/smp_impldefs.h index 31300aed76..79e41e6852 100644 --- a/usr/src/uts/i86pc/sys/smp_impldefs.h +++ b/usr/src/uts/i86pc/sys/smp_impldefs.h @@ -32,6 +32,7 @@ #include <sys/types.h> #include <sys/sunddi.h> #include <sys/cpuvar.h> +#include <sys/avintr.h> #include <sys/pic.h> #include <sys/xc_levels.h> @@ -77,10 +78,12 @@ extern void (*setlvlx)(int, int); /* set intr pri to specified level */ extern void (*setspl)(int); /* mask intr below or equal given ipl */ extern int (*addspl)(int, int, int, int); /* add intr mask of vector */ extern int (*delspl)(int, int, int, int); /* delete intr mask of vector */ -extern void (*setsoftint)(int); /* trigger a software intr */ + +/* trigger a software intr */ +extern void (*setsoftint)(int, struct av_softinfo *); extern uint_t xc_serv(caddr_t, caddr_t); /* cross call service routine */ -extern void set_pending(); /* set software interrupt pending */ +extern void av_set_softint_pending(); /* set software interrupt pending */ extern void microfind(void); /* initialize tenmicrosec */ /* map physical address */ |