1*a8ea0c9dSJohn Levon /*
2*a8ea0c9dSJohn Levon * This file and its contents are supplied under the terms of the
3*a8ea0c9dSJohn Levon * Common Development and Distribution License ("CDDL"), version 1.0.
4*a8ea0c9dSJohn Levon * You may only use this file in accordance with the terms of version
5*a8ea0c9dSJohn Levon * 1.0 of the CDDL.
6*a8ea0c9dSJohn Levon *
7*a8ea0c9dSJohn Levon * A full copy of the text of the CDDL should have accompanied this
8*a8ea0c9dSJohn Levon * source. A copy of the CDDL is also available via the Internet at
9*a8ea0c9dSJohn Levon * http://www.illumos.org/license/CDDL.
10*a8ea0c9dSJohn Levon */
11*a8ea0c9dSJohn Levon /*
12*a8ea0c9dSJohn Levon * Copyright 2019 Joyent, Inc.
13*a8ea0c9dSJohn Levon */
14*a8ea0c9dSJohn Levon
15*a8ea0c9dSJohn Levon #include <mdb/mdb_modapi.h>
16*a8ea0c9dSJohn Levon #include <mdb/mdb_ctf.h>
17*a8ea0c9dSJohn Levon #include <sys/cpuvar.h>
18*a8ea0c9dSJohn Levon #include <sys/x_call.h>
19*a8ea0c9dSJohn Levon
20*a8ea0c9dSJohn Levon typedef struct {
21*a8ea0c9dSJohn Levon uint32_t xc_work_cnt;
22*a8ea0c9dSJohn Levon struct xc_msg *xc_curmsg;
23*a8ea0c9dSJohn Levon struct xc_msg *xc_msgbox;
24*a8ea0c9dSJohn Levon xc_data_t xc_data;
25*a8ea0c9dSJohn Levon } mdb_xcall_machcpu_t;
26*a8ea0c9dSJohn Levon
27*a8ea0c9dSJohn Levon typedef struct {
28*a8ea0c9dSJohn Levon processorid_t cpu_id;
29*a8ea0c9dSJohn Levon mdb_xcall_machcpu_t cpu_m;
30*a8ea0c9dSJohn Levon } mdb_xcall_cpu_t;
31*a8ea0c9dSJohn Levon
32*a8ea0c9dSJohn Levon typedef struct {
33*a8ea0c9dSJohn Levon uint_t xd_flags;
34*a8ea0c9dSJohn Levon processorid_t xd_cpu_id;
35*a8ea0c9dSJohn Levon size_t xd_msg_index;
36*a8ea0c9dSJohn Levon struct xc_msg xd_msgs[NCPU];
37*a8ea0c9dSJohn Levon } xcall_data_t;
38*a8ea0c9dSJohn Levon
39*a8ea0c9dSJohn Levon void
xcall_help(void)40*a8ea0c9dSJohn Levon xcall_help(void)
41*a8ea0c9dSJohn Levon {
42*a8ea0c9dSJohn Levon mdb_printf(
43*a8ea0c9dSJohn Levon "Print all active cross-calls where the given CPU is the master.\n"
44*a8ea0c9dSJohn Levon "The PEND column is ->xc_work_cnt, the pending message count -\n"
45*a8ea0c9dSJohn Levon "this includes both master and slave messages. For each\n"
46*a8ea0c9dSJohn Levon "cross call, the message type and the slave CPU ID are shown.\n");
47*a8ea0c9dSJohn Levon }
48*a8ea0c9dSJohn Levon
49*a8ea0c9dSJohn Levon static int
cpu_id_to_addr(processorid_t cpun,uintptr_t * addrp)50*a8ea0c9dSJohn Levon cpu_id_to_addr(processorid_t cpun, uintptr_t *addrp)
51*a8ea0c9dSJohn Levon {
52*a8ea0c9dSJohn Levon uintptr_t addr;
53*a8ea0c9dSJohn Levon GElf_Sym sym;
54*a8ea0c9dSJohn Levon
55*a8ea0c9dSJohn Levon if (mdb_lookup_by_name("cpu", &sym) == -1) {
56*a8ea0c9dSJohn Levon mdb_warn("failed to find symbol for 'cpu'");
57*a8ea0c9dSJohn Levon return (-1);
58*a8ea0c9dSJohn Levon }
59*a8ea0c9dSJohn Levon
60*a8ea0c9dSJohn Levon if (cpun * sizeof (uintptr_t) > sym.st_size)
61*a8ea0c9dSJohn Levon return (-1);
62*a8ea0c9dSJohn Levon
63*a8ea0c9dSJohn Levon addr = (uintptr_t)sym.st_value + cpun * sizeof (uintptr_t);
64*a8ea0c9dSJohn Levon
65*a8ea0c9dSJohn Levon if (mdb_vread(&addr, sizeof (addr), addr) == -1) {
66*a8ea0c9dSJohn Levon mdb_warn("failed to read cpu[%lu]", cpun);
67*a8ea0c9dSJohn Levon return (-1);
68*a8ea0c9dSJohn Levon }
69*a8ea0c9dSJohn Levon
70*a8ea0c9dSJohn Levon if (addr != (uintptr_t)NULL) {
71*a8ea0c9dSJohn Levon *addrp = addr;
72*a8ea0c9dSJohn Levon return (0);
73*a8ea0c9dSJohn Levon }
74*a8ea0c9dSJohn Levon
75*a8ea0c9dSJohn Levon return (-1);
76*a8ea0c9dSJohn Levon }
77*a8ea0c9dSJohn Levon
78*a8ea0c9dSJohn Levon static int
xcall_copy_msg(struct xc_msg * msg,xcall_data_t * data,boolean_t current)79*a8ea0c9dSJohn Levon xcall_copy_msg(struct xc_msg *msg, xcall_data_t *data, boolean_t current)
80*a8ea0c9dSJohn Levon {
81*a8ea0c9dSJohn Levon if (data->xd_msg_index >= NCPU) {
82*a8ea0c9dSJohn Levon mdb_warn("ran out of msg space: %lu >= %lu\n",
83*a8ea0c9dSJohn Levon data->xd_msg_index, NCPU);
84*a8ea0c9dSJohn Levon return (-1);
85*a8ea0c9dSJohn Levon }
86*a8ea0c9dSJohn Levon
87*a8ea0c9dSJohn Levon bcopy(msg, &data->xd_msgs[data->xd_msg_index], sizeof (*msg));
88*a8ea0c9dSJohn Levon
89*a8ea0c9dSJohn Levon /*
90*a8ea0c9dSJohn Levon * As we don't use .xc_next, store 'current' there.
91*a8ea0c9dSJohn Levon */
92*a8ea0c9dSJohn Levon data->xd_msgs[data->xd_msg_index].xc_next = (void *)(uintptr_t)current;
93*a8ea0c9dSJohn Levon data->xd_msg_index++;
94*a8ea0c9dSJohn Levon return (0);
95*a8ea0c9dSJohn Levon }
96*a8ea0c9dSJohn Levon
97*a8ea0c9dSJohn Levon static int
xcall_get_msgs(uintptr_t addr,const void * wdata,void * priv)98*a8ea0c9dSJohn Levon xcall_get_msgs(uintptr_t addr, const void *wdata, void *priv)
99*a8ea0c9dSJohn Levon {
100*a8ea0c9dSJohn Levon _NOTE(ARGUNUSED(wdata));
101*a8ea0c9dSJohn Levon xcall_data_t *data = priv;
102*a8ea0c9dSJohn Levon mdb_xcall_cpu_t xcpu = { 0, };
103*a8ea0c9dSJohn Levon struct xc_msg msg;
104*a8ea0c9dSJohn Levon uintptr_t msgaddr;
105*a8ea0c9dSJohn Levon
106*a8ea0c9dSJohn Levon if (mdb_ctf_vread(&xcpu, "unix`cpu_t", "mdb_xcall_cpu_t",
107*a8ea0c9dSJohn Levon addr, MDB_CTF_VREAD_IGNORE_ABSENT) == -1)
108*a8ea0c9dSJohn Levon return (WALK_ERR);
109*a8ea0c9dSJohn Levon
110*a8ea0c9dSJohn Levon if (xcpu.cpu_m.xc_curmsg != NULL) {
111*a8ea0c9dSJohn Levon msgaddr = (uintptr_t)xcpu.cpu_m.xc_curmsg;
112*a8ea0c9dSJohn Levon
113*a8ea0c9dSJohn Levon if (mdb_vread(&msg, sizeof (msg), msgaddr) != sizeof (msg))
114*a8ea0c9dSJohn Levon return (WALK_ERR);
115*a8ea0c9dSJohn Levon
116*a8ea0c9dSJohn Levon if (msg.xc_master == data->xd_cpu_id) {
117*a8ea0c9dSJohn Levon if (data->xd_flags & DCMD_PIPE_OUT)
118*a8ea0c9dSJohn Levon mdb_printf("%p\n", msgaddr);
119*a8ea0c9dSJohn Levon else if (xcall_copy_msg(&msg, data, B_TRUE) != 0)
120*a8ea0c9dSJohn Levon return (WALK_ERR);
121*a8ea0c9dSJohn Levon }
122*a8ea0c9dSJohn Levon }
123*a8ea0c9dSJohn Levon
124*a8ea0c9dSJohn Levon for (msgaddr = (uintptr_t)xcpu.cpu_m.xc_msgbox;
125*a8ea0c9dSJohn Levon msgaddr != (uintptr_t)NULL; msgaddr = (uintptr_t)msg.xc_next) {
126*a8ea0c9dSJohn Levon if (mdb_vread(&msg, sizeof (msg), msgaddr) != sizeof (msg))
127*a8ea0c9dSJohn Levon return (WALK_ERR);
128*a8ea0c9dSJohn Levon
129*a8ea0c9dSJohn Levon if (msg.xc_master != data->xd_cpu_id)
130*a8ea0c9dSJohn Levon continue;
131*a8ea0c9dSJohn Levon
132*a8ea0c9dSJohn Levon if (data->xd_flags & DCMD_PIPE_OUT)
133*a8ea0c9dSJohn Levon mdb_printf("%p\n", msgaddr);
134*a8ea0c9dSJohn Levon else if (xcall_copy_msg(&msg, data, B_FALSE) != 0)
135*a8ea0c9dSJohn Levon return (WALK_ERR);
136*a8ea0c9dSJohn Levon }
137*a8ea0c9dSJohn Levon
138*a8ea0c9dSJohn Levon return (WALK_NEXT);
139*a8ea0c9dSJohn Levon }
140*a8ea0c9dSJohn Levon
141*a8ea0c9dSJohn Levon static int
print_xcall_msg(struct xc_msg * msg)142*a8ea0c9dSJohn Levon print_xcall_msg(struct xc_msg *msg)
143*a8ea0c9dSJohn Levon {
144*a8ea0c9dSJohn Levon boolean_t current = (boolean_t)msg->xc_next;
145*a8ea0c9dSJohn Levon char indent[] = " ";
146*a8ea0c9dSJohn Levon const char *cmd;
147*a8ea0c9dSJohn Levon
148*a8ea0c9dSJohn Levon switch (msg->xc_command) {
149*a8ea0c9dSJohn Levon case XC_MSG_ASYNC: cmd = "ASYNC"; break;
150*a8ea0c9dSJohn Levon case XC_MSG_CALL: cmd = "CALL"; break;
151*a8ea0c9dSJohn Levon case XC_MSG_SYNC: cmd = "SYNC"; break;
152*a8ea0c9dSJohn Levon case XC_MSG_FREE:cmd = "FREE"; break;
153*a8ea0c9dSJohn Levon case XC_MSG_WAITING: cmd = "WAITING"; break;
154*a8ea0c9dSJohn Levon case XC_MSG_RELEASED: cmd = "RELEASED"; break;
155*a8ea0c9dSJohn Levon case XC_MSG_DONE: cmd = "DONE"; break;
156*a8ea0c9dSJohn Levon default: cmd = "?"; break;
157*a8ea0c9dSJohn Levon }
158*a8ea0c9dSJohn Levon
159*a8ea0c9dSJohn Levon mdb_printf("%s %s%-*s %-6u\n", indent, current ? "*" : "",
160*a8ea0c9dSJohn Levon 9 - current, cmd, msg->xc_slave);
161*a8ea0c9dSJohn Levon return (0);
162*a8ea0c9dSJohn Levon }
163*a8ea0c9dSJohn Levon
164*a8ea0c9dSJohn Levon /*
165*a8ea0c9dSJohn Levon * Show all xcall messages where the master is the given CPU.
166*a8ea0c9dSJohn Levon *
167*a8ea0c9dSJohn Levon * As non-free messages can be on the slave's ->xc_msgbox or ->xc_curmsg, we
168*a8ea0c9dSJohn Levon * need to walk across all of them to find each message where ->xc_master
169*a8ea0c9dSJohn Levon * is our CPU ID.
170*a8ea0c9dSJohn Levon */
171*a8ea0c9dSJohn Levon int
xcall_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)172*a8ea0c9dSJohn Levon xcall_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
173*a8ea0c9dSJohn Levon {
174*a8ea0c9dSJohn Levon mdb_xcall_cpu_t xcpu = { 0, };
175*a8ea0c9dSJohn Levon xcall_data_t data = { 0, };
176*a8ea0c9dSJohn Levon
177*a8ea0c9dSJohn Levon if (mdb_getopts(argc, argv, NULL) != argc)
178*a8ea0c9dSJohn Levon return (DCMD_USAGE);
179*a8ea0c9dSJohn Levon
180*a8ea0c9dSJohn Levon /*
181*a8ea0c9dSJohn Levon * Yep, this will re-collect all the messages each time. Shrug.
182*a8ea0c9dSJohn Levon */
183*a8ea0c9dSJohn Levon if (!(flags & DCMD_ADDRSPEC)) {
184*a8ea0c9dSJohn Levon if (mdb_pwalk_dcmd("cpu", "xcall", argc, argv, 0) == -1) {
185*a8ea0c9dSJohn Levon mdb_warn("can't walk CPUs");
186*a8ea0c9dSJohn Levon return (DCMD_ERR);
187*a8ea0c9dSJohn Levon }
188*a8ea0c9dSJohn Levon
189*a8ea0c9dSJohn Levon return (DCMD_OK);
190*a8ea0c9dSJohn Levon }
191*a8ea0c9dSJohn Levon
192*a8ea0c9dSJohn Levon if (addr < NCPU && cpu_id_to_addr((processorid_t)addr, &addr) != 0) {
193*a8ea0c9dSJohn Levon mdb_warn("invalid CPU ID %lu\n", addr);
194*a8ea0c9dSJohn Levon return (DCMD_ERR);
195*a8ea0c9dSJohn Levon }
196*a8ea0c9dSJohn Levon
197*a8ea0c9dSJohn Levon if (mdb_ctf_vread(&xcpu, "unix`cpu_t", "mdb_xcall_cpu_t",
198*a8ea0c9dSJohn Levon addr, MDB_CTF_VREAD_IGNORE_ABSENT) == -1) {
199*a8ea0c9dSJohn Levon mdb_warn("couldn't read cpu 0x%lx", addr);
200*a8ea0c9dSJohn Levon return (DCMD_ERR);
201*a8ea0c9dSJohn Levon }
202*a8ea0c9dSJohn Levon
203*a8ea0c9dSJohn Levon data.xd_cpu_id = xcpu.cpu_id;
204*a8ea0c9dSJohn Levon data.xd_flags = flags;
205*a8ea0c9dSJohn Levon
206*a8ea0c9dSJohn Levon if (mdb_pwalk("cpu", xcall_get_msgs, &data, (uintptr_t)NULL) == -1) {
207*a8ea0c9dSJohn Levon mdb_warn("can't walk CPUs");
208*a8ea0c9dSJohn Levon return (DCMD_ERR);
209*a8ea0c9dSJohn Levon }
210*a8ea0c9dSJohn Levon
211*a8ea0c9dSJohn Levon if (flags & DCMD_PIPE_OUT)
212*a8ea0c9dSJohn Levon return (DCMD_OK);
213*a8ea0c9dSJohn Levon
214*a8ea0c9dSJohn Levon if (DCMD_HDRSPEC(flags))
215*a8ea0c9dSJohn Levon mdb_printf("%<u>%3s %4s %s%</u>\n", "CPU", "PEND", "HANDLER");
216*a8ea0c9dSJohn Levon
217*a8ea0c9dSJohn Levon if (data.xd_msg_index == 0) {
218*a8ea0c9dSJohn Levon mdb_printf("%3d %4d -\n",
219*a8ea0c9dSJohn Levon xcpu.cpu_id, xcpu.cpu_m.xc_work_cnt);
220*a8ea0c9dSJohn Levon return (DCMD_OK);
221*a8ea0c9dSJohn Levon }
222*a8ea0c9dSJohn Levon
223*a8ea0c9dSJohn Levon mdb_printf("%3d %4d %a(%a, %a, %a)\n",
224*a8ea0c9dSJohn Levon xcpu.cpu_id, xcpu.cpu_m.xc_work_cnt,
225*a8ea0c9dSJohn Levon xcpu.cpu_m.xc_data.xc_func, xcpu.cpu_m.xc_data.xc_a1,
226*a8ea0c9dSJohn Levon xcpu.cpu_m.xc_data.xc_a2, xcpu.cpu_m.xc_data.xc_a3);
227*a8ea0c9dSJohn Levon
228*a8ea0c9dSJohn Levon if (!(flags & DCMD_PIPE_OUT))
229*a8ea0c9dSJohn Levon mdb_printf(" %<u>%-9s %-6s%</u>\n", "COMMAND", "SLAVE");
230*a8ea0c9dSJohn Levon
231*a8ea0c9dSJohn Levon for (size_t i = 0; i < data.xd_msg_index; i++) {
232*a8ea0c9dSJohn Levon if (print_xcall_msg(&data.xd_msgs[i]) != 0)
233*a8ea0c9dSJohn Levon return (DCMD_ERR);
234*a8ea0c9dSJohn Levon }
235*a8ea0c9dSJohn Levon
236*a8ea0c9dSJohn Levon return (DCMD_OK);
237*a8ea0c9dSJohn Levon }
238