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 2021 Tintri by DDN, Inc. All rights reserved.
13 */
14
15 #include <sys/mdb_modapi.h>
16 #include <mdb/mdb_ctf.h>
17 #include <sys/types.h>
18 #include <sys/mutex_impl.h>
19 #include <sys/rwlock_impl.h>
20 #include <sys/zone.h>
21 #include <sys/socket.h>
22
23 #include "nfssrv.h"
24 #include "common.h"
25
26 struct common_zsd_cb_data {
27 zone_key_t key; /* Key of ZSD for which we're looking */
28 uint_t found; /* Was the specific ZSD entry found? */
29 uintptr_t zsd_data; /* Result */
30 };
31
32 int
zoned_find_zsd_cb(uintptr_t addr,const void * data,void * cb_data)33 zoned_find_zsd_cb(uintptr_t addr, const void *data, void *cb_data)
34 {
35 const struct zsd_entry *entry = data;
36 struct common_zsd_cb_data *cbd = cb_data;
37
38 if (cbd->key != entry->zsd_key)
39 return (WALK_NEXT);
40
41 /* Match */
42 cbd->zsd_data = (uintptr_t)entry->zsd_data;
43 cbd->found = TRUE;
44 return (WALK_DONE);
45 }
46
47 int
zoned_get_nfs_globals(uintptr_t zonep,uintptr_t * result)48 zoned_get_nfs_globals(uintptr_t zonep, uintptr_t *result)
49 {
50 return (zoned_get_zsd(zonep, "nfssrv_zone_key", result));
51 }
52
53 int
zoned_get_zsd(uintptr_t zonep,char * key_str,uintptr_t * result)54 zoned_get_zsd(uintptr_t zonep, char *key_str, uintptr_t *result)
55 {
56 zone_key_t key;
57 struct common_zsd_cb_data cbd;
58
59 if (mdb_readsym(&key, sizeof (zone_key_t), key_str) == -1) {
60 mdb_warn("failed to get %s", key_str);
61 return (DCMD_ERR);
62 }
63
64 cbd.key = key;
65 cbd.found = FALSE;
66
67 if (mdb_pwalk("zsd", zoned_find_zsd_cb, &cbd, zonep) != 0) {
68 mdb_warn("failed to walk zsd");
69 return (DCMD_ERR);
70 }
71
72 if (cbd.found == FALSE) {
73 mdb_warn("no ZSD entry found");
74 return (DCMD_ERR);
75 }
76
77 *result = cbd.zsd_data;
78 return (DCMD_OK);
79 }
80
81
82 const char *
common_mutex(kmutex_t * mp)83 common_mutex(kmutex_t *mp)
84 {
85 char *s;
86 size_t sz;
87 mutex_impl_t *lp = (mutex_impl_t *)mp;
88
89 if (MUTEX_TYPE_SPIN(lp)) {
90 const char *fmt = "spin - lock(%x)/oldspl(%x)/minspl(%x)";
91
92 sz = 1 + mdb_snprintf(NULL, 0, fmt, lp->m_spin.m_spinlock,
93 lp->m_spin.m_oldspl, lp->m_spin.m_minspl);
94 s = mdb_alloc(sz, UM_SLEEP | UM_GC);
95 (void) mdb_snprintf(s, sz, fmt, lp->m_spin.m_spinlock,
96 lp->m_spin.m_oldspl, lp->m_spin.m_minspl);
97
98 return (s);
99 }
100
101 if (MUTEX_TYPE_ADAPTIVE(lp)) {
102 const char *fmt = "adaptive - owner %p%s";
103
104 if ((MUTEX_OWNER(lp) == NULL) && !MUTEX_HAS_WAITERS(lp))
105 return ("mutex not held");
106
107 sz = 1 + mdb_snprintf(NULL, 0, fmt, MUTEX_OWNER(lp),
108 MUTEX_HAS_WAITERS(lp) ? " has waiters" : "");
109 s = mdb_alloc(sz, UM_SLEEP | UM_GC);
110 (void) mdb_snprintf(s, sz, fmt, MUTEX_OWNER(lp),
111 MUTEX_HAS_WAITERS(lp) ? " has waiters" : "");
112
113 return (s);
114 }
115
116 return ("mutex dead");
117 }
118
119 const char *
common_rwlock(krwlock_t * lp)120 common_rwlock(krwlock_t *lp)
121 {
122 char *s;
123 size_t sz;
124 uintptr_t w = ((rwlock_impl_t *)lp)->rw_wwwh;
125 uintptr_t o = w & RW_OWNER;
126 const char *hw = (w & RW_HAS_WAITERS) ? " has_waiters" : "";
127 const char *ww = (w & RW_WRITE_WANTED) ? " write_wanted" : "";
128 const char *wl = (w & RW_WRITE_LOCKED) ? " write_locked" : "";
129
130 sz = 1 + mdb_snprintf(NULL, 0, "owner %p%s%s%s", o, hw, ww, wl);
131 s = mdb_alloc(sz, UM_SLEEP | UM_GC);
132 (void) mdb_snprintf(s, sz, "owner %p%s%s%s", o, hw, ww, wl);
133
134 return (s);
135 }
136
137 const char *
common_netbuf_str(struct netbuf * nb)138 common_netbuf_str(struct netbuf *nb)
139 {
140 struct sockaddr_in *in;
141
142 in = mdb_alloc(nb->len + 1, UM_SLEEP | UM_GC);
143 if (mdb_vread(in, nb->len, (uintptr_t)nb->buf) == -1)
144 return ("");
145
146 if (nb->len < sizeof (struct sockaddr_in)) {
147 ((char *)in)[nb->len] = '\0';
148 return ((char *)in);
149 }
150
151 if (in->sin_family == AF_INET) {
152 char *s;
153 ssize_t sz;
154
155 mdb_nhconvert(&in->sin_port, &in->sin_port,
156 sizeof (in->sin_port));
157
158 sz = 1 + mdb_snprintf(NULL, 0, "%I:%d", in->sin_addr.s_addr,
159 in->sin_port);
160 s = mdb_alloc(sz, UM_SLEEP | UM_GC);
161 (void) mdb_snprintf(s, sz, "%I:%d", in->sin_addr.s_addr,
162 in->sin_port);
163
164 return (s);
165 } else if ((in->sin_family == AF_INET6) &&
166 (nb->len >= sizeof (struct sockaddr_in6))) {
167 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)in;
168 char *s;
169 size_t sz;
170
171 mdb_nhconvert(&in6->sin6_port, &in6->sin6_port,
172 sizeof (in6->sin6_port));
173
174 sz = 1 + mdb_snprintf(NULL, 0, "[%N]:%d",
175 in6->sin6_addr.s6_addr, in6->sin6_port);
176 s = mdb_alloc(sz, UM_SLEEP | UM_GC);
177 (void) mdb_snprintf(s, sz, "[%N]:%d", in6->sin6_addr.s6_addr,
178 in6->sin6_port);
179
180 return (s);
181 } else {
182 ((char *)in)[nb->len] = '\0';
183 return ((char *)in);
184 }
185 }
186
187 /*
188 * Generic hash table walker
189 *
190 */
191
192 typedef struct hash_table_walk_data {
193 uintptr_t table; /* current table pointer */
194 int count; /* number of entries to process */
195 void *member; /* copy of the current member structure */
196 } hash_table_walk_data_t;
197
198 int
hash_table_walk_init(mdb_walk_state_t * wsp)199 hash_table_walk_init(mdb_walk_state_t *wsp)
200 {
201 hash_table_walk_arg_t *arg = wsp->walk_arg;
202 hash_table_walk_data_t *wd = mdb_alloc(sizeof (*wd), UM_SLEEP);
203
204 wd->table = arg->array_addr;
205 wd->count = arg->array_len;
206
207 wd->member = mdb_alloc(arg->member_size, UM_SLEEP);
208
209 wsp->walk_addr = 0;
210 wsp->walk_data = wd;
211
212 return (WALK_NEXT);
213 }
214
215 int
hash_table_walk_step(mdb_walk_state_t * wsp)216 hash_table_walk_step(mdb_walk_state_t *wsp)
217 {
218 hash_table_walk_arg_t *arg = wsp->walk_arg;
219 hash_table_walk_data_t *wd = wsp->walk_data;
220 uintptr_t addr = wsp->walk_addr;
221
222 if (wd->count == 0)
223 return (WALK_DONE);
224
225 if (addr == 0) {
226 if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
227 wd->table + arg->first_offset) == -1) {
228 mdb_warn("can't read %s", arg->first_name);
229 return (WALK_ERR);
230 }
231 if (wsp->walk_addr == 0)
232 wsp->walk_addr = wd->table;
233
234 return (WALK_NEXT);
235 }
236
237 if (addr == wd->table) {
238 wd->count--;
239 wd->table += arg->head_size;
240 wsp->walk_addr = 0;
241
242 return (WALK_NEXT);
243 }
244
245 if (mdb_vread(wd->member, arg->member_size, addr) == -1) {
246 mdb_warn("unable to read %s", arg->member_type_name);
247 return (WALK_ERR);
248 }
249
250 wsp->walk_addr = *(uintptr_t *)((uintptr_t)wd->member
251 + arg->next_offset);
252 if (wsp->walk_addr == 0)
253 wsp->walk_addr = wd->table;
254
255 return (wsp->walk_callback(addr, wd->member, wsp->walk_cbdata));
256 }
257
258 void
hash_table_walk_fini(mdb_walk_state_t * wsp)259 hash_table_walk_fini(mdb_walk_state_t *wsp)
260 {
261 hash_table_walk_arg_t *arg = wsp->walk_arg;
262 hash_table_walk_data_t *wd = wsp->walk_data;
263
264 mdb_free(wd->member, arg->member_size);
265 mdb_free(wd, sizeof (*wd));
266 }
267