Skip to content

Commit fe64b49

Browse files
committed
Update scsi host and added scsi devices with scsiinfo module
Signed-off-by: Rajan Shanmugavelu <[email protected]
1 parent 936d1f3 commit fe64b49

File tree

2 files changed

+197
-17
lines changed

2 files changed

+197
-17
lines changed

drgn_tools/iscsi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def print_iscsi_sessions(prog: Program) -> None:
121121
)
122122
)
123123

124-
for scsi_dev in for_each_scsi_host_device(prog, session.host):
124+
for scsi_dev in for_each_scsi_host_device(prog):
125125
name = scsi_device_name(prog, scsi_dev)
126126
print(
127127
"scsi{} Channel {} Id {} Lun: {}".format(

drgn_tools/scsi.py

Lines changed: 196 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
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 IO substem useful information from the vmcore or
5+
live system.
56
"""
67
import argparse
78
from typing import Iterator
89

9-
import drgn
10+
from drgn import cast
1011
from drgn import container_of
1112
from drgn import FaultError
1213
from drgn import Object
1314
from drgn import Program
15+
from drgn.helpers.linux.block import _class_to_subsys
16+
from drgn.helpers.linux.block import for_each_disk
1417
from drgn.helpers.linux.list import list_for_each_entry
1518

1619
from drgn_tools.corelens import CorelensModule
17-
from drgn_tools.device import class_to_subsys
20+
from drgn_tools.module import ensure_debuginfo
1821
from drgn_tools.table import print_table
1922
from drgn_tools.util import has_member
2023

2124

25+
"""
26+
Dictionary of gendisks being used as hashmap with request_queue address as the key,
27+
this is need to lookup the disk names for UEK6 or older kernel where gendisk is not
28+
part of the request_queue structure.
29+
"""
30+
gendisk_map = {}
31+
32+
2233
def for_each_scsi_host(prog: Program) -> Iterator[Object]:
2334
"""
2435
Iterate through all scsi hosts and returns an
@@ -29,7 +40,7 @@ def for_each_scsi_host(prog: Program) -> Iterator[Object]:
2940
"knode_class"
3041
)
3142

32-
subsys_p = class_to_subsys(prog["shost_class"].address_of_())
43+
subsys_p = _class_to_subsys(prog["shost_class"].address_of_())
3344
devices = subsys_p.klist_devices.k_list.address_of_()
3445

3546
if class_in_private:
@@ -52,21 +63,20 @@ def host_module_name(shost: Object) -> str:
5263
"""
5364
try:
5465
name = shost.hostt.module.name.string_().decode()
55-
except drgn.FaultError:
66+
except FaultError:
5667
name = "unknown"
5768
return name
5869

5970

60-
def for_each_scsi_host_device(
61-
prog: Program, shost: Object
62-
) -> Iterator[Object]:
71+
def for_each_scsi_host_device(shost: Object) -> Iterator[Object]:
6372
"""
64-
Get a list of scsi_device associated with a Scsi_Host.
65-
:returns: an iterator of ``struct scsi_device *``
73+
Iterates thru all scsi device and returns a scsi_device address
6674
"""
67-
return list_for_each_entry(
68-
"struct scsi_device", shost.__devices.address_of_(), "siblings"
69-
)
75+
for scsi_dev in list_for_each_entry(
76+
"struct scsi_device",
77+
shost.__devices.address_of_(),
78+
"siblings"):
79+
yield scsi_dev
7080

7181

7282
def scsi_device_name(prog: Program, sdev: Object) -> str:
@@ -82,42 +92,212 @@ def scsi_device_name(prog: Program, sdev: Object) -> str:
8292
return ""
8393

8494

95+
def load_gendisk(prog: Program) -> None:
96+
"""
97+
This method loads the all the gendisk into the global hashmap.
98+
"""
99+
msg = ensure_debuginfo(prog, ["sd_mod"])
100+
if msg:
101+
print(msg)
102+
return
103+
104+
for disk in for_each_disk(prog):
105+
disk_rq = hex(disk.queue)
106+
gendisk_map[disk_rq] = disk
107+
return
108+
109+
110+
def scsi_id(scsi_dev: Object) -> str:
111+
"""
112+
Return Host:Controller:Target:Lun as a string.
113+
"""
114+
if not scsi_dev:
115+
return "<unknown>"
116+
hctl = "[" + str(scsi_dev.host.host_no.value_()) + ":" + \
117+
str(scsi_dev.channel.value_()) + ":" + \
118+
str(scsi_dev.id.value_()) + ":" + \
119+
str(scsi_dev.lun.value_()) + "]"
120+
return hctl
121+
122+
85123
def print_scsi_hosts(prog: Program) -> None:
86124
"""
87125
Prints scsi host information
88126
"""
89127
output = [
90-
["SCSI_HOST", "NAME", "DRIVER", "Busy", "Blocked", "Fail", "State"]
128+
[
129+
"SCSI_HOST",
130+
"NAME",
131+
"DRIVER",
132+
"Version",
133+
"Busy",
134+
"Blocked",
135+
"Fail",
136+
"State",
137+
"EH val",
138+
]
91139
]
140+
92141
for shost in for_each_scsi_host(prog):
93142
"""
94143
Since 6eb045e092ef ("scsi: core: avoid host-wide host_busy counter for scsi_mq"),
95144
host_busy is no longer a member of struct Scsi_Host.
96145
"""
146+
if host_module_name(shost) == "ahci":
147+
continue
148+
149+
if shost.hostt.module.version:
150+
modver = shost.hostt.module.version.string_().decode()
151+
else:
152+
modver = "n/a"
153+
97154
if has_member(shost, "host_busy"):
98155
host_busy = shost.host_busy.counter.value_()
99156
else:
100157
host_busy = "n/a"
158+
159+
if has_member(shost, "eh_deadline"):
160+
eh_deadline = shost.eh_deadline.value_()
161+
else:
162+
eh_deadline = "n/a"
163+
101164
output.append(
102165
[
103166
hex(shost.value_()),
104167
f"host{shost.host_no.value_()}",
105168
host_module_name(shost),
169+
modver,
106170
host_busy,
107171
shost.host_blocked.counter.value_(),
108172
shost.host_failed.value_(),
109173
shost.shost_state.format_(type_name=False),
174+
eh_deadline,
110175
]
111176
)
112177
print_table(output)
113178

114179

180+
def print_shost_header(shost: Object) -> None:
181+
"""
182+
print scsi host header.
183+
"""
184+
print(
185+
"--------------------------------------------------"
186+
"-------------------------------------------------"
187+
)
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(
211+
"--------------------------------------------------"
212+
"-------------------------------------------------"
213+
)
214+
return
215+
216+
217+
def print_shost_devs(prog: Program) -> None:
218+
"""
219+
print all scsi devices for a Scsi_Host
220+
"""
221+
msg = ensure_debuginfo(prog, ["sd_mod"])
222+
if msg:
223+
print(msg)
224+
return
225+
226+
for shost in for_each_scsi_host(prog):
227+
if host_module_name(shost) == "ahci":
228+
continue
229+
print_shost_header(shost)
230+
output = [
231+
[
232+
"Device",
233+
"H:C:T:L",
234+
"Scsi Device Addr",
235+
"Vendor",
236+
"State",
237+
"IO Req",
238+
"IO Done",
239+
"IO Error",
240+
]
241+
]
242+
243+
for scsi_dev in for_each_scsi_host_device(shost):
244+
if prog.type("struct request_queue").has_member("disk"):
245+
gendisk = cast("struct gendisk *", scsi_dev.request_queue.disk)
246+
if not gendisk:
247+
continue
248+
diskname = gendisk.disk_name.address_of_().string_().decode()
249+
else:
250+
diskname = gendisk_map[hex(scsi_dev.request_queue)].disk_name.string_().decode()
251+
252+
vendor = scsi_dev.vendor.string_().decode()
253+
devstate = str(scsi_dev.sdev_state.format_(type_name=False))
254+
255+
output.append(
256+
[
257+
str(diskname),
258+
scsi_id(scsi_dev),
259+
hex(scsi_dev),
260+
str(vendor),
261+
devstate,
262+
scsi_dev.iorequest_cnt.counter.value_(),
263+
scsi_dev.iodone_cnt.counter.value_(),
264+
scsi_dev.ioerr_cnt.counter.value_(),
265+
]
266+
)
267+
print_table(output)
268+
269+
115270
class ScsiInfo(CorelensModule):
116271
"""
117272
Corelens Module for scsi device information
118273
"""
119274

120275
name = "scsiinfo"
121276

277+
debuginfo_kmods = ["sd_mod"]
278+
279+
default_args = [
280+
[
281+
"--hosts",
282+
"--devices",
283+
]
284+
]
285+
286+
def add_args(self, parser: argparse.ArgumentParser) -> None:
287+
parser.add_argument(
288+
"--hosts",
289+
action="store_true",
290+
help="Print Scsi Hosts",
291+
)
292+
parser.add_argument(
293+
"--devices",
294+
action="store_true",
295+
help="Print Scsi Devices",
296+
)
297+
122298
def run(self, prog: Program, args: argparse.Namespace) -> None:
123-
print_scsi_hosts(prog)
299+
if args.hosts:
300+
print_scsi_hosts(prog)
301+
if args.devices:
302+
load_gendisk(prog)
303+
print_shost_devs(prog)

0 commit comments

Comments
 (0)