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 SCSI subsytem useful information from the vmcore or
5
+ live system.
5
6
"""
6
7
import argparse
8
+ import enum
7
9
from typing import Iterator
8
10
9
- import drgn
10
11
from drgn import container_of
11
12
from drgn import FaultError
12
13
from drgn import Object
15
16
16
17
from drgn_tools .corelens import CorelensModule
17
18
from drgn_tools .device import class_to_subsys
19
+ from drgn_tools .module import ensure_debuginfo
18
20
from drgn_tools .table import print_table
19
21
from drgn_tools .util import has_member
20
22
21
23
24
+ class Opcode (enum .Enum ):
25
+ TUR = 0x00
26
+ READ_6 = 0x8
27
+ WRITE_6 = 0xA
28
+ INQUIRY = 0x12
29
+ READ_10 = 0x28
30
+ WRITE_10 = 0x2A
31
+
32
+
22
33
def for_each_scsi_host (prog : Program ) -> Iterator [Object ]:
23
34
"""
24
- Iterate through all scsi hosts and returns an
25
- iterator.
35
+ Iterate through all scsi hosts and returns an iterator.
36
+
26
37
:returns: an iterator of ``struct Scsi_Host *``
27
38
"""
28
39
class_in_private = prog .type ("struct device_private" ).has_member (
@@ -48,48 +59,96 @@ def for_each_scsi_host(prog: Program) -> Iterator[Object]:
48
59
def host_module_name (shost : Object ) -> str :
49
60
"""
50
61
Fetch the module name associated with the scsi host.
51
- returns: the module name string.
62
+
63
+ :param shost: ``struct Scsi_Host *``
64
+ :returns: the module name string.
52
65
"""
53
66
try :
54
67
name = shost .hostt .module .name .string_ ().decode ()
55
- except drgn . FaultError :
68
+ except FaultError :
56
69
name = "unknown"
57
70
return name
58
71
59
72
60
- def for_each_scsi_host_device (
61
- prog : Program , shost : Object
62
- ) -> Iterator [Object ]:
73
+ def for_each_scsi_host_device (shost : Object ) -> Iterator [Object ]:
63
74
"""
64
- Get a list of scsi_device associated with a Scsi_Host.
75
+ Iterates thru all scsi device and returns a scsi_device address
76
+
77
+ :param shost: ``struct Scsi_Host *``
65
78
:returns: an iterator of ``struct scsi_device *``
66
79
"""
67
- return list_for_each_entry (
80
+ for scsi_dev in list_for_each_entry (
68
81
"struct scsi_device" , shost .__devices .address_of_ (), "siblings"
69
- )
82
+ ):
83
+ yield scsi_dev
70
84
71
85
72
- def scsi_device_name (prog : Program , sdev : Object ) -> str :
86
+ def scsi_device_name (sdev : Object ) -> str :
73
87
"""
74
88
Get the device name associated with scsi_device.
75
- :return ``str``
89
+
90
+ :param sdev: ``struct scsi_device *``
91
+ :returns: ``str``
76
92
"""
77
93
rq = sdev .request_queue
78
- dev = container_of (rq .kobj .parent , "struct device" , "kobj" )
94
+ if has_member (rq , "mq_kobj" ):
95
+ # uek5 thru uek8 has mq_obj with upstream commit id 320ae51fee
96
+ dev = container_of (rq .mq_kobj .parent , "struct device" , "kobj" )
97
+ if has_member (rq , "kobj" ):
98
+ dev = container_of (rq .kobj .parent , "struct device" , "kobj" )
79
99
try :
80
100
return dev .kobj .name .string_ ().decode ()
81
101
except FaultError :
82
102
return ""
83
103
84
104
105
+ def scsi_id (scsi_dev : Object ) -> str :
106
+ """
107
+ Fetch SCSI id of the device.
108
+
109
+ :param scsi_dev: ``struct scsi_device *``
110
+ :returns: ``str``
111
+ """
112
+ if not scsi_dev :
113
+ return "<unknown>"
114
+ hctl = (
115
+ "["
116
+ + str (scsi_dev .host .host_no .value_ ())
117
+ + ":"
118
+ + str (scsi_dev .channel .value_ ())
119
+ + ":"
120
+ + str (scsi_dev .id .value_ ())
121
+ + ":"
122
+ + str (scsi_dev .lun .value_ ())
123
+ + "]"
124
+ )
125
+ return hctl
126
+
127
+
85
128
def print_scsi_hosts (prog : Program ) -> None :
86
129
"""
87
130
Prints scsi host information
88
131
"""
89
132
output = [
90
- ["SCSI_HOST" , "NAME" , "DRIVER" , "Busy" , "Blocked" , "Fail" , "State" ]
133
+ [
134
+ "SCSI_HOST" ,
135
+ "NAME" ,
136
+ "DRIVER" ,
137
+ "Version" ,
138
+ "Busy" ,
139
+ "Blocked" ,
140
+ "Fail" ,
141
+ "State" ,
142
+ "EH val" ,
143
+ ]
91
144
]
145
+
92
146
for shost in for_each_scsi_host (prog ):
147
+ if shost .hostt .module .version :
148
+ modver = shost .hostt .module .version .string_ ().decode ()
149
+ else :
150
+ modver = "n/a"
151
+
93
152
"""
94
153
Since 6eb045e092ef ("scsi: core: avoid host-wide host_busy counter for scsi_mq"),
95
154
host_busy is no longer a member of struct Scsi_Host.
@@ -98,18 +157,101 @@ def print_scsi_hosts(prog: Program) -> None:
98
157
host_busy = shost .host_busy .counter .value_ ()
99
158
else :
100
159
host_busy = "n/a"
160
+
161
+ if has_member (shost , "eh_deadline" ):
162
+ eh_deadline = shost .eh_deadline .value_ ()
163
+ else :
164
+ eh_deadline = "n/a"
165
+
101
166
output .append (
102
167
[
103
168
hex (shost .value_ ()),
104
- f"host{ shost .host_no .value_ ()} " ,
169
+ f"host{ shost .host_no .value_ ():> } " ,
105
170
host_module_name (shost ),
171
+ modver ,
106
172
host_busy ,
107
173
shost .host_blocked .counter .value_ (),
108
174
shost .host_failed .value_ (),
109
175
shost .shost_state .format_ (type_name = False ),
176
+ eh_deadline ,
110
177
]
111
178
)
112
179
print_table (output )
180
+ return
181
+
182
+
183
+ def print_shost_header (shost : Object ) -> None :
184
+ """
185
+ print scsi host header.
186
+ """
187
+ print ("-" * 110 )
188
+ output = [
189
+ [
190
+ "HOST" ,
191
+ "DRIVER" ,
192
+ "Scsi_Host" ,
193
+ "shost_data" ,
194
+ "hostdata" ,
195
+ ]
196
+ ]
197
+
198
+ shostdata = hex (shost .shost_data .address_of_ ().value_ ())
199
+ hostdata = hex (shost .hostdata .address_of_ ().value_ ())
200
+ output .append (
201
+ [
202
+ shost .shost_gendev .kobj .name .string_ ().decode (),
203
+ host_module_name (shost ),
204
+ hex (shost ),
205
+ shostdata ,
206
+ hostdata ,
207
+ ]
208
+ )
209
+ print_table (output )
210
+ print ("-" * 110 )
211
+ return
212
+
213
+
214
+ def print_shost_devs (prog : Program ) -> None :
215
+ """
216
+ print all scsi devices for a Scsi_Host
217
+ """
218
+ msg = ensure_debuginfo (prog , ["sd_mod" ])
219
+ if msg :
220
+ print (msg )
221
+ return
222
+
223
+ for shost in for_each_scsi_host (prog ):
224
+ print_shost_header (shost )
225
+ output = [
226
+ [
227
+ "Device" ,
228
+ "H:C:T:L" ,
229
+ "Scsi Device Addr" ,
230
+ "Vendor" ,
231
+ "State" ,
232
+ "IO Req" ,
233
+ "IO Done" ,
234
+ "IO Error" ,
235
+ ]
236
+ ]
237
+
238
+ for scsi_dev in for_each_scsi_host_device (shost ):
239
+ vendor = scsi_dev .vendor .string_ ().decode ()
240
+ devstate = str (scsi_dev .sdev_state .format_ (type_name = False ))
241
+
242
+ output .append (
243
+ [
244
+ scsi_device_name (scsi_dev ),
245
+ scsi_id (scsi_dev ),
246
+ hex (scsi_dev ),
247
+ str (vendor ),
248
+ devstate ,
249
+ f"{ scsi_dev .iorequest_cnt .counter .value_ ():>7} " ,
250
+ f"{ scsi_dev .iodone_cnt .counter .value_ ():>7} " ,
251
+ f"{ scsi_dev .ioerr_cnt .counter .value_ ():>4} " ,
252
+ ]
253
+ )
254
+ print_table (output )
113
255
114
256
115
257
class ScsiInfo (CorelensModule ):
@@ -119,5 +261,31 @@ class ScsiInfo(CorelensModule):
119
261
120
262
name = "scsiinfo"
121
263
264
+ debuginfo_kmods = ["sd_mod" ]
265
+
266
+ default_args = [
267
+ [
268
+ "--hosts" ,
269
+ "--devices" ,
270
+ ]
271
+ ]
272
+
273
+ def add_args (self , parser : argparse .ArgumentParser ) -> None :
274
+ parser .add_argument (
275
+ "--hosts" ,
276
+ action = "store_true" ,
277
+ help = "Print Scsi Hosts" ,
278
+ )
279
+ parser .add_argument (
280
+ "--devices" ,
281
+ action = "store_true" ,
282
+ help = "Print Scsi Devices" ,
283
+ )
284
+
122
285
def run (self , prog : Program , args : argparse .Namespace ) -> None :
123
- print_scsi_hosts (prog )
286
+ if args .hosts :
287
+ print_scsi_hosts (prog )
288
+ elif args .devices :
289
+ print_shost_devs (prog )
290
+ else :
291
+ print_scsi_hosts (prog )
0 commit comments