1
- # Copyright (c) 2023 , Oracle and/or its affiliates.
1
+ # Copyright (c) 2025 , Oracle and/or its affiliates.
2
2
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3
3
"""
4
- Helper to print scsi hosts
4
+ Helper to print IO substem useful information from the vmcore or
5
+ live system.
5
6
"""
6
7
import argparse
7
8
from typing import Iterator
8
9
9
- import drgn
10
+ from drgn import cast
10
11
from drgn import container_of
11
- from drgn import FaultError
12
12
from drgn import Object
13
13
from drgn import Program
14
- from drgn .helpers .linux .list import list_for_each_entry
15
14
16
- from drgn_tools .corelens import CorelensModule
17
- from drgn_tools .device import class_to_subsys
18
- from drgn_tools .table import print_table
19
15
from drgn_tools .util import has_member
16
+ from drgn_tools .table import print_table
17
+ from drgn_tools .module import KernelModule
18
+ from drgn_tools .module import ensure_debuginfo
19
+ from drgn_tools .corelens import CorelensModule
20
+ #from drgn_tools.lpfcinfo import print_lpfc_shost_info
21
+ #from drgn_tools.qla2info import print_qla2xxx_shost_info
20
22
23
+ from drgn .helpers .linux .list import list_for_each_entry
24
+ from drgn .helpers .linux .block import for_each_disk
25
+ from drgn .helpers .linux .block import _class_to_subsys
26
+
27
+ """
28
+ Dictionary of gendisks being used as hashmap with request_queue address as the key,
29
+ this is need to lookup the disk names for UEK6 or older kernel where gendisk is not
30
+ part of the request_queue structure.
31
+ """
32
+ gendisk_map = {}
21
33
22
34
def for_each_scsi_host (prog : Program ) -> Iterator [Object ]:
23
35
"""
@@ -29,7 +41,7 @@ def for_each_scsi_host(prog: Program) -> Iterator[Object]:
29
41
"knode_class"
30
42
)
31
43
32
- subsys_p = class_to_subsys (prog ["shost_class" ].address_of_ ())
44
+ subsys_p = _class_to_subsys (prog ["shost_class" ].address_of_ ())
33
45
devices = subsys_p .klist_devices .k_list .address_of_ ()
34
46
35
47
if class_in_private :
@@ -44,7 +56,6 @@ def for_each_scsi_host(prog: Program) -> Iterator[Object]:
44
56
):
45
57
yield container_of (dev , "struct Scsi_Host" , "shost_dev" )
46
58
47
-
48
59
def host_module_name (shost : Object ) -> str :
49
60
"""
50
61
Fetch the module name associated with the scsi host.
@@ -56,18 +67,13 @@ def host_module_name(shost: Object) -> str:
56
67
name = "unknown"
57
68
return name
58
69
59
-
60
- def for_each_scsi_host_device (
61
- prog : Program , shost : Object
62
- ) -> Iterator [Object ]:
70
+ def for_each_scsi_host_device (shost : Object ) -> Iterator [Object ]:
63
71
"""
64
- Get a list of scsi_device associated with a Scsi_Host.
65
- :returns: an iterator of ``struct scsi_device *``
72
+ Iterates thru all scsi device and returns a scsi_device address
66
73
"""
67
- return list_for_each_entry (
68
- "struct scsi_device" , shost .__devices .address_of_ (), "siblings"
69
- )
70
-
74
+ for scsi_dev in list_for_each_entry ("struct scsi_device" ,
75
+ shost .__devices .address_of_ (), "siblings" ):
76
+ yield scsi_dev
71
77
72
78
def scsi_device_name (prog : Program , sdev : Object ) -> str :
73
79
"""
@@ -81,36 +87,195 @@ def scsi_device_name(prog: Program, sdev: Object) -> str:
81
87
except FaultError :
82
88
return ""
83
89
90
+ def load_gendisk (prog : Program ) -> None :
91
+ """
92
+ This method loads the all the gendisk into the global hashmap.
93
+ """
94
+ msg = ensure_debuginfo (prog , ["sd_mod" ])
95
+ if msg :
96
+ print (msg )
97
+ return
98
+
99
+ for disk in for_each_disk (prog ):
100
+ disk_rq = hex (disk .queue )
101
+ gendisk_map [disk_rq ] = disk
102
+ return
103
+
104
+ def for_each_scsi_cmnd (scsi_dev : Object ) -> Iterator [Object ]:
105
+ """
106
+ Iterates thru all scsi commands for a given scsi device.
107
+ Return each scsi_cmnd for a given scsi device.
108
+ """
109
+ if prog .type ("struct request_queue" ).has_member ("mq_ops" ):
110
+ yield for_each_scsi_cmd_mq (scsi_dev .request_queue )
111
+ else :
112
+ yield for_each_scsi_cmd_sq (scsi_dev )
113
+
114
+ def for_each_scsi_cmd_sq (scsi_dev : Object ) -> Iterator [Object ]:
115
+ """
116
+ Return each scsi_cmnd for single queue scsi device.
117
+ """
118
+ for cmnd in list_for_each_entry ("struct scsi_cmnd" ,
119
+ scsi_dev .cmd_list , scsi_dev .list , "list" ):
120
+ yield cmnd
121
+
122
+ def scsi_id (scsi_dev : Object ) -> str :
123
+ """
124
+ Return Host:Controller:Target:Lun as a string.
125
+ """
126
+ if not scsi_dev :
127
+ return "<unknown>"
128
+ hctl = "[" + str (scsi_dev .host .host_no .value_ ()) + ":" + \
129
+ str (scsi_dev .channel .value_ ()) + ":" + \
130
+ str (scsi_dev .id .value_ ()) + ":" + \
131
+ str (scsi_dev .lun .value_ ()) + "]"
132
+ return hctl
133
+
84
134
85
135
def print_scsi_hosts (prog : Program ) -> None :
86
136
"""
87
137
Prints scsi host information
88
138
"""
89
139
output = [
90
- ["SCSI_HOST" , "NAME" , "DRIVER" , "Busy" , "Blocked" , "Fail" , "State" ]
140
+ [
141
+ "SCSI_HOST" ,
142
+ "NAME" ,
143
+ "DRIVER" ,
144
+ "Version" ,
145
+ "Busy" ,
146
+ "Blocked" ,
147
+ "Fail" ,
148
+ "State" ,
149
+ "EH val" ,
150
+ ]
91
151
]
152
+
92
153
for shost in for_each_scsi_host (prog ):
93
154
"""
94
155
Since 6eb045e092ef ("scsi: core: avoid host-wide host_busy counter for scsi_mq"),
95
156
host_busy is no longer a member of struct Scsi_Host.
96
157
"""
158
+ if host_module_name (shost ) == "ahci" :
159
+ continue
160
+
161
+ if shost .hostt .module .version :
162
+ modver = shost .hostt .module .version .string_ ().decode ()
163
+ else :
164
+ modver = "n/a"
165
+
97
166
if has_member (shost , "host_busy" ):
98
167
host_busy = shost .host_busy .counter .value_ ()
99
168
else :
100
169
host_busy = "n/a"
170
+
171
+ if has_member (shost , "eh_deadline" ):
172
+ eh_deadline = shost .eh_deadline .value_ ()
173
+ else :
174
+ eh_deadline = "n/a"
175
+
101
176
output .append (
102
177
[
103
178
hex (shost .value_ ()),
104
179
f"host{ shost .host_no .value_ ()} " ,
105
180
host_module_name (shost ),
181
+ modver ,
106
182
host_busy ,
107
183
shost .host_blocked .counter .value_ (),
108
184
shost .host_failed .value_ (),
109
185
shost .shost_state .format_ (type_name = False ),
186
+ eh_deadline ,
110
187
]
111
188
)
112
189
print_table (output )
113
190
191
+ def print_shost_header (shost : Object ) -> None :
192
+ """
193
+ print scsi host header.
194
+ """
195
+ print (
196
+ "--------------------------------------------------"
197
+ "-------------------------------------------------"
198
+ )
199
+ output = [
200
+ [
201
+ "HOST" ,
202
+ "DRIVER" ,
203
+ "Scsi_Host" ,
204
+ "shost_data" ,
205
+ "hostdata" ,
206
+ ]
207
+ ]
208
+
209
+ shostdata = hex (shost .shost_data .address_of_ ().value_ ())
210
+ hostdata = hex (shost .hostdata .address_of_ ().value_ ())
211
+ output .append (
212
+ [
213
+ shost .shost_gendev .kobj .name .string_ ().decode (),
214
+ host_module_name (shost ),
215
+ hex (shost ),
216
+ shostdata ,
217
+ hostdata ,
218
+ ]
219
+ )
220
+ print_table (output )
221
+ print (
222
+ "--------------------------------------------------"
223
+ "-------------------------------------------------"
224
+ )
225
+ return
226
+
227
+
228
+ def print_shost_devs (prog : Program ) -> None :
229
+ """
230
+ print all scsi devices for a Scsi_Host
231
+ """
232
+ msg = ensure_debuginfo (prog , ["sd_mod" ])
233
+ if msg :
234
+ print (msg )
235
+ return
236
+
237
+ for shost in for_each_scsi_host (prog ):
238
+ if host_module_name (shost ) == "ahci" :
239
+ continue
240
+ print_shost_header (shost )
241
+ output = [
242
+ [
243
+ "Device" ,
244
+ "H:C:T:L" ,
245
+ "Scsi Device Addr" ,
246
+ "Vendor" ,
247
+ "State" ,
248
+ "IO Req" ,
249
+ "IO Done" ,
250
+ "IO Error" ,
251
+ ]
252
+ ]
253
+
254
+ for scsi_dev in for_each_scsi_host_device (shost ):
255
+ if prog .type ("struct request_queue" ).has_member ("disk" ):
256
+ gendisk = cast ("struct gendisk *" , scsi_dev .request_queue .disk )
257
+ if not gendisk :
258
+ continue
259
+ diskname = gendisk .disk_name .address_of_ ().string_ ().decode ()
260
+ else :
261
+ diskname = gendisk_map [hex (scsi_dev .request_queue )].disk_name .string_ ().decode ()
262
+
263
+ vendor = scsi_dev .vendor .string_ ().decode ()
264
+ devstate = str (scsi_dev .sdev_state .format_ (type_name = False ))
265
+
266
+ output .append (
267
+ [
268
+ str (diskname ),
269
+ scsi_id (scsi_dev ),
270
+ hex (scsi_dev ),
271
+ str (vendor ),
272
+ devstate ,
273
+ scsi_dev .iorequest_cnt .counter .value_ (),
274
+ scsi_dev .iodone_cnt .counter .value_ (),
275
+ scsi_dev .ioerr_cnt .counter .value_ (),
276
+ ]
277
+ )
278
+ print_table (output )
114
279
115
280
class ScsiInfo (CorelensModule ):
116
281
"""
@@ -119,5 +284,30 @@ class ScsiInfo(CorelensModule):
119
284
120
285
name = "scsiinfo"
121
286
287
+ debuginfo_kmods = ["sd_mod" ]
288
+
289
+ default_args = [
290
+ [
291
+ "--hosts" ,
292
+ "--devices" ,
293
+ ]
294
+ ]
295
+
296
+ def add_args (self , parser : argparse .ArgumentParser ) -> None :
297
+ parser .add_argument (
298
+ "--hosts" ,
299
+ action = "store_true" ,
300
+ help = "Print Scsi Hosts" ,
301
+ )
302
+ parser .add_argument (
303
+ "--devices" ,
304
+ action = "store_true" ,
305
+ help = "Print Scsi Devices" ,
306
+ )
307
+
122
308
def run (self , prog : Program , args : argparse .Namespace ) -> None :
123
- print_scsi_hosts (prog )
309
+ if args .hosts :
310
+ print_scsi_hosts (prog )
311
+ if args .devices :
312
+ load_gendisk (prog )
313
+ print_shost_devs (prog )
0 commit comments