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 }