1- # Copyright (c) 2023 , Oracle and/or its affiliates.
1+ # Copyright (c) 2025 , Oracle and/or its affiliates.
22# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
33"""
4- Helper to print scsi hosts
4+ Helper to print SCSI subsytem useful information from the vmcore or
5+ live system.
56"""
67import argparse
8+ import enum
79from typing import Iterator
810
9- import drgn
1011from drgn import container_of
1112from drgn import FaultError
1213from drgn import Object
1516
1617from drgn_tools .corelens import CorelensModule
1718from drgn_tools .device import class_to_subsys
19+ from drgn_tools .module import ensure_debuginfo
1820from drgn_tools .table import print_table
1921from drgn_tools .util import has_member
2022
2123
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+
2233def for_each_scsi_host (prog : Program ) -> Iterator [Object ]:
2334 """
24- Iterate through all scsi hosts and returns an
25- iterator.
35+ Iterate through all scsi hosts and returns an iterator.
36+
2637 :returns: an iterator of ``struct Scsi_Host *``
2738 """
2839 class_in_private = prog .type ("struct device_private" ).has_member (
@@ -48,48 +59,96 @@ def for_each_scsi_host(prog: Program) -> Iterator[Object]:
4859def host_module_name (shost : Object ) -> str :
4960 """
5061 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.
5265 """
5366 try :
5467 name = shost .hostt .module .name .string_ ().decode ()
55- except drgn . FaultError :
68+ except FaultError :
5669 name = "unknown"
5770 return name
5871
5972
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 ]:
6374 """
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 *``
6578 :returns: an iterator of ``struct scsi_device *``
6679 """
67- return list_for_each_entry (
80+ for scsi_dev in list_for_each_entry (
6881 "struct scsi_device" , shost .__devices .address_of_ (), "siblings"
69- )
82+ ):
83+ yield scsi_dev
7084
7185
72- def scsi_device_name (prog : Program , sdev : Object ) -> str :
86+ def scsi_device_name (sdev : Object ) -> str :
7387 """
7488 Get the device name associated with scsi_device.
75- :return ``str``
89+
90+ :param sdev: ``struct scsi_device *``
91+ :returns: ``str``
7692 """
7793 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" )
7999 try :
80100 return dev .kobj .name .string_ ().decode ()
81101 except FaultError :
82102 return ""
83103
84104
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+
85128def print_scsi_hosts (prog : Program ) -> None :
86129 """
87130 Prints scsi host information
88131 """
89132 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+ ]
91144 ]
145+
92146 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+
93152 """
94153 Since 6eb045e092ef ("scsi: core: avoid host-wide host_busy counter for scsi_mq"),
95154 host_busy is no longer a member of struct Scsi_Host.
@@ -98,18 +157,101 @@ def print_scsi_hosts(prog: Program) -> None:
98157 host_busy = shost .host_busy .counter .value_ ()
99158 else :
100159 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+
101166 output .append (
102167 [
103168 hex (shost .value_ ()),
104- f"host{ shost .host_no .value_ ()} " ,
169+ f"host{ shost .host_no .value_ ():> } " ,
105170 host_module_name (shost ),
171+ modver ,
106172 host_busy ,
107173 shost .host_blocked .counter .value_ (),
108174 shost .host_failed .value_ (),
109175 shost .shost_state .format_ (type_name = False ),
176+ eh_deadline ,
110177 ]
111178 )
112179 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 )
113255
114256
115257class ScsiInfo (CorelensModule ):
@@ -119,5 +261,31 @@ class ScsiInfo(CorelensModule):
119261
120262 name = "scsiinfo"
121263
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+
122285 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