Print this page
11584 ::xcall would be useful
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
*** 23,32 ****
--- 23,33 ----
* Use is subject to license terms.
*/
/*
* Copyright (c) 2010, Intel Corporation.
* All rights reserved.
+ * Copyright 2018 Joyent, Inc.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/t_lock.h>
*** 77,135 ****
* The reason for this asynchronous approach is to allow for fast global
* TLB shootdowns. If all CPUs, say N, tried to do a global TLB invalidation
* on a different Virtual Address at the same time. The old code required
* N squared IPIs. With this method, depending on timing, it could happen
* with just N IPIs.
- */
-
- /*
- * The default is to not enable collecting counts of IPI information, since
- * the updating of shared cachelines could cause excess bus traffic.
- */
- uint_t xc_collect_enable = 0;
- uint64_t xc_total_cnt = 0; /* total #IPIs sent for cross calls */
- uint64_t xc_multi_cnt = 0; /* # times we piggy backed on another IPI */
-
- /*
- * Values for message states. Here are the normal transitions. A transition
- * of "->" happens in the slave cpu and "=>" happens in the master cpu as
- * the messages are passed back and forth.
*
* FREE => ASYNC -> DONE => FREE
* FREE => CALL -> DONE => FREE
* FREE => SYNC -> WAITING => RELEASED -> DONE => FREE
*
! * The interesing one above is ASYNC. You might ask, why not go directly
! * to FREE, instead of DONE. If it did that, it might be possible to exhaust
* the master's xc_free list if a master can generate ASYNC messages faster
* then the slave can process them. That could be handled with more complicated
* handling. However since nothing important uses ASYNC, I've not bothered.
*/
- #define XC_MSG_FREE (0) /* msg in xc_free queue */
- #define XC_MSG_ASYNC (1) /* msg in slave xc_msgbox */
- #define XC_MSG_CALL (2) /* msg in slave xc_msgbox */
- #define XC_MSG_SYNC (3) /* msg in slave xc_msgbox */
- #define XC_MSG_WAITING (4) /* msg in master xc_msgbox or xc_waiters */
- #define XC_MSG_RELEASED (5) /* msg in slave xc_msgbox */
- #define XC_MSG_DONE (6) /* msg in master xc_msgbox */
/*
* We allow for one high priority message at a time to happen in the system.
* This is used for panic, kmdb, etc., so no locking is done.
*/
static volatile cpuset_t xc_priority_set_store;
static volatile ulong_t *xc_priority_set = CPUSET2BV(xc_priority_set_store);
static xc_data_t xc_priority_data;
/*
- * Wrappers to avoid C compiler warnings due to volatile. The atomic bit
- * operations don't accept volatile bit vectors - which is a bit silly.
- */
- #define XC_BT_SET(vector, b) BT_ATOMIC_SET((ulong_t *)(vector), (b))
- #define XC_BT_CLEAR(vector, b) BT_ATOMIC_CLEAR((ulong_t *)(vector), (b))
-
- /*
* Decrement a CPU's work count
*/
static void
xc_decrement(struct machcpu *mcpu)
{
--- 78,120 ----
* The reason for this asynchronous approach is to allow for fast global
* TLB shootdowns. If all CPUs, say N, tried to do a global TLB invalidation
* on a different Virtual Address at the same time. The old code required
* N squared IPIs. With this method, depending on timing, it could happen
* with just N IPIs.
*
+ * Here are the normal transitions for XC_MSG_* values in ->xc_command. A
+ * transition of "->" happens in the slave cpu and "=>" happens in the master
+ * cpu as the messages are passed back and forth.
+ *
* FREE => ASYNC -> DONE => FREE
* FREE => CALL -> DONE => FREE
* FREE => SYNC -> WAITING => RELEASED -> DONE => FREE
*
! * The interesting one above is ASYNC. You might ask, why not go directly
! * to FREE, instead of DONE? If it did that, it might be possible to exhaust
* the master's xc_free list if a master can generate ASYNC messages faster
* then the slave can process them. That could be handled with more complicated
* handling. However since nothing important uses ASYNC, I've not bothered.
*/
/*
+ * The default is to not enable collecting counts of IPI information, since
+ * the updating of shared cachelines could cause excess bus traffic.
+ */
+ uint_t xc_collect_enable = 0;
+ uint64_t xc_total_cnt = 0; /* total #IPIs sent for cross calls */
+ uint64_t xc_multi_cnt = 0; /* # times we piggy backed on another IPI */
+
+ /*
* We allow for one high priority message at a time to happen in the system.
* This is used for panic, kmdb, etc., so no locking is done.
*/
static volatile cpuset_t xc_priority_set_store;
static volatile ulong_t *xc_priority_set = CPUSET2BV(xc_priority_set_store);
static xc_data_t xc_priority_data;
/*
* Decrement a CPU's work count
*/
static void
xc_decrement(struct machcpu *mcpu)
{
*** 191,200 ****
--- 176,199 ----
old_head->xc_next = NULL;
return (old_head);
}
/*
+ * Extract the next message from the CPU's queue, and place the message in
+ * .xc_curmsg. The latter is solely to make debugging (and ::xcall) more
+ * useful.
+ */
+ static xc_msg_t *
+ xc_get(void)
+ {
+ struct machcpu *mcpup = &CPU->cpu_m;
+ xc_msg_t *msg = xc_extract(&mcpup->xc_msgbox);
+ mcpup->xc_curmsg = msg;
+ return (msg);
+ }
+
+ /*
* Initialize the machcpu fields used for cross calls
*/
static uint_t xc_initialized = 0;
void
*** 326,347 ****
rc = DDI_INTR_CLAIMED;
/*
* We may have to wait for a message to arrive.
*/
! for (msg = NULL; msg == NULL;
! msg = xc_extract(&mcpup->xc_msgbox)) {
/*
* Alway check for and handle a priority message.
*/
if (BT_TEST(xc_priority_set, CPU->cpu_id)) {
func = xc_priority_data.xc_func;
a1 = xc_priority_data.xc_a1;
a2 = xc_priority_data.xc_a2;
a3 = xc_priority_data.xc_a3;
! XC_BT_CLEAR(xc_priority_set, CPU->cpu_id);
xc_decrement(mcpup);
func(a1, a2, a3);
if (mcpup->xc_work_cnt == 0)
return (rc);
}
--- 325,345 ----
rc = DDI_INTR_CLAIMED;
/*
* We may have to wait for a message to arrive.
*/
! for (msg = NULL; msg == NULL; msg = xc_get()) {
/*
* Alway check for and handle a priority message.
*/
if (BT_TEST(xc_priority_set, CPU->cpu_id)) {
func = xc_priority_data.xc_func;
a1 = xc_priority_data.xc_a1;
a2 = xc_priority_data.xc_a2;
a3 = xc_priority_data.xc_a3;
! BT_ATOMIC_CLEAR(xc_priority_set, CPU->cpu_id);
xc_decrement(mcpup);
func(a1, a2, a3);
if (mcpup->xc_work_cnt == 0)
return (rc);
}
*** 441,450 ****
--- 439,450 ----
default:
panic("bad message 0x%p in msgbox", (void *)msg);
break;
}
+
+ CPU->cpu_m.xc_curmsg = NULL;
}
return (rc);
}
/*
*** 579,589 ****
* problem. We'll just erase the previous request - which was
* most likely a kmdb_enter that has already expired - and plow
* ahead.
*/
if (BT_TEST(xc_priority_set, c)) {
! XC_BT_CLEAR(xc_priority_set, c);
if (cpup->cpu_m.xc_work_cnt > 0)
xc_decrement(&cpup->cpu_m);
}
}
--- 579,589 ----
* problem. We'll just erase the previous request - which was
* most likely a kmdb_enter that has already expired - and plow
* ahead.
*/
if (BT_TEST(xc_priority_set, c)) {
! BT_ATOMIC_CLEAR(xc_priority_set, c);
if (cpup->cpu_m.xc_work_cnt > 0)
xc_decrement(&cpup->cpu_m);
}
}
*** 605,615 ****
cpup = cpu[c];
if (cpup == NULL || !(cpup->cpu_flags & CPU_READY) ||
cpup == CPU)
continue;
(void) xc_increment(&cpup->cpu_m);
! XC_BT_SET(xc_priority_set, c);
send_dirint(c, XC_HI_PIL);
for (i = 0; i < 10; ++i) {
(void) atomic_cas_ptr(&cpup->cpu_m.xc_msgbox,
cpup->cpu_m.xc_msgbox, cpup->cpu_m.xc_msgbox);
}
--- 605,615 ----
cpup = cpu[c];
if (cpup == NULL || !(cpup->cpu_flags & CPU_READY) ||
cpup == CPU)
continue;
(void) xc_increment(&cpup->cpu_m);
! BT_ATOMIC_SET(xc_priority_set, c);
send_dirint(c, XC_HI_PIL);
for (i = 0; i < 10; ++i) {
(void) atomic_cas_ptr(&cpup->cpu_m.xc_msgbox,
cpup->cpu_m.xc_msgbox, cpup->cpu_m.xc_msgbox);
}