summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/avintr.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/avintr.c')
-rw-r--r--usr/src/uts/common/io/avintr.c111
1 files changed, 89 insertions, 22 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);
}
}