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