Skip to content

Commit 20facfa

Browse files
committed
Add SCSI inflight commands sub option
Signed-off-by: Rajan <[email protected]>
1 parent 69cd476 commit 20facfa

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

drgn_tools/scsi.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,25 @@
88
import enum
99
from typing import Iterator
1010

11+
from drgn import cast
1112
from drgn import container_of
1213
from drgn import FaultError
1314
from drgn import Object
1415
from drgn import Program
16+
from drgn import sizeof
17+
from drgn.helpers.linux.block import for_each_disk
1518
from drgn.helpers.linux.list import list_for_each_entry
1619

20+
from drgn_tools.block import for_each_mq_pending_request
21+
from drgn_tools.block import for_each_sq_pending_request
22+
from drgn_tools.block import is_mq
23+
from drgn_tools.block import request_target
1724
from drgn_tools.corelens import CorelensModule
1825
from drgn_tools.device import class_to_subsys
1926
from drgn_tools.module import ensure_debuginfo
2027
from drgn_tools.table import print_table
2128
from drgn_tools.util import has_member
29+
from drgn_tools.util import timestamp_str
2230

2331

2432
class Opcode(enum.Enum):
@@ -102,6 +110,67 @@ def scsi_device_name(sdev: Object) -> str:
102110
return ""
103111

104112

113+
def for_each_scsi_cmnd(prog: Program, scsi_disk: Object) -> Iterator[Object]:
114+
"""
115+
Iterates thru all scsi commands for a given SCSI device.
116+
117+
:param scsi_disk: ``struct scsi_disk *``
118+
:returns: an iterator of ``struct scsi_cmnd *``
119+
"""
120+
q = scsi_disk.queue
121+
if is_mq(q):
122+
for scmnd in for_each_scsi_cmd_mq(prog, scsi_disk):
123+
yield scmnd
124+
else:
125+
for scmnd in for_each_scsi_cmd_sq(prog, scsi_disk):
126+
yield scmnd
127+
128+
129+
def rq_to_scmnd(prog: Program, rq: Object) -> Object:
130+
"""
131+
Fetch the scsi_cmnd from request address
132+
133+
:param rq: ``struct request_queue *``
134+
:returns: Object of ``struct scsi_cmnd *``
135+
"""
136+
scmnd = rq.value_() + sizeof(prog.type("struct request"))
137+
return Object(prog, "struct scsi_cmnd *", value=scmnd)
138+
139+
140+
def for_each_scsi_cmd_sq(prog: Program, disk: Object) -> Iterator[Object]:
141+
"""
142+
Iterates thru all SCSI commands from the block layer pending requests.
143+
144+
:param disk: ``strcut scsi_disk *``
145+
:returns: an iterator of ``struct scsi_cmnd *``
146+
"""
147+
q = disk.queue
148+
for rq in for_each_sq_pending_request(q):
149+
yield rq_to_scmnd(prog, rq)
150+
151+
152+
def for_each_scsi_cmd_mq(prog: Program, disk: Object) -> Iterator[Object]:
153+
"""
154+
Iterates thru all SCSI commands in all multi hardware queue.
155+
156+
:param disk: ``strcut scsi_disk *``
157+
:returns: an iterator of ``struct scsi_cmnd *``
158+
"""
159+
try:
160+
BLK_MQ_F_TAG_SHARED = prog.constant("BLK_MQ_F_TAG_SHARED")
161+
except LookupError:
162+
BLK_MQ_F_TAG_SHARED = prog.constant("BLK_MQ_F_TAG_QUEUE_SHARED")
163+
164+
q = disk.queue
165+
for hwq, rq in for_each_mq_pending_request(q):
166+
if (hwq.flags & BLK_MQ_F_TAG_SHARED) != 0 and request_target(
167+
rq
168+
).value_() != disk.value_():
169+
continue
170+
171+
yield rq_to_scmnd(prog, rq)
172+
173+
105174
def scsi_id(scsi_dev: Object) -> str:
106175
"""
107176
Fetch SCSI id of the device.
@@ -254,6 +323,99 @@ def print_shost_devs(prog: Program) -> None:
254323
print_table(output)
255324

256325

326+
def print_inflight_scsi_cmnds(prog: Program):
327+
"""
328+
print all inflight SCSI commands for all SCSI devices.
329+
"""
330+
TotalInflight = 0
331+
for disk in for_each_disk(prog):
332+
diskname = disk.disk_name.string_().decode()
333+
if diskname.startswith("nvme") or diskname.startswith("dm"):
334+
# skip non-SCSI disks like NVMe and dm
335+
continue
336+
337+
scsi_disk = cast("struct scsi_disk *", disk.private_data)
338+
scsi_device = cast("struct scsi_device *", scsi_disk.device)
339+
counter = 0
340+
output = [
341+
[
342+
"Count",
343+
"Request",
344+
"Bio",
345+
"SCSI Cmnd",
346+
"Opcode",
347+
"Length",
348+
"Age",
349+
"Sector",
350+
]
351+
]
352+
353+
for scsi_cmnd in for_each_scsi_cmnd(prog, disk):
354+
if counter == 0:
355+
vendor = scsi_device.vendor.string_().decode()
356+
devstate = str(scsi_device.sdev_state.format_(type_name=False))
357+
diskname = disk.disk_name.string_().decode()
358+
scsiid = scsi_id(scsi_device)
359+
360+
print(
361+
f" Diskname : {diskname} {scsiid}\t\t\tSCSI Device Addr : {hex(scsi_device.value_())}"
362+
)
363+
print(f" Vendor : {vendor} \tDevice State\t : {devstate}")
364+
print("-" * 115)
365+
366+
if has_member(scsi_cmnd, "request"):
367+
req = scsi_cmnd.request
368+
else:
369+
reqp = scsi_cmnd.value_() - sizeof(prog.type("struct request"))
370+
req = Object(prog, "struct request *", value=reqp)
371+
372+
try:
373+
opcode = Opcode(scsi_cmnd.cmnd[0].value_()).name
374+
except ValueError:
375+
opcode = str(hex(scsi_cmnd.cmnd[0].value_()))
376+
377+
if scsi_cmnd.cmnd[0] == 0x2A or scsi_cmnd.cmnd[0] == 0x28:
378+
xfer_len = (
379+
scsi_cmnd.cmnd[7] << 8 | scsi_cmnd.cmnd[8]
380+
) * scsi_cmnd.transfersize
381+
else:
382+
xfer_len = 0
383+
384+
if req.bio:
385+
if has_member(req.bio, "bi_sector"):
386+
sector = req.bio.bi_sector
387+
else:
388+
sector = req.bio.bi_iter.bi_sector
389+
else:
390+
sector = 0
391+
392+
age = (
393+
prog["jiffies"] - scsi_cmnd.jiffies_at_alloc
394+
).value_() * 1000000
395+
counter += 1
396+
397+
output.append(
398+
[
399+
f"{counter:>4}",
400+
hex(req.value_()),
401+
hex(req.bio.value_()),
402+
hex(scsi_cmnd.value_()),
403+
opcode,
404+
f"{int(xfer_len):>7}",
405+
timestamp_str(age),
406+
f"{int(sector):>11}",
407+
]
408+
)
409+
410+
if counter > 1:
411+
TotalInflight += counter
412+
print_table(output)
413+
print("-" * 115)
414+
print(f" Total inflight commands across all disks : {TotalInflight}")
415+
print("-" * 115)
416+
return
417+
418+
257419
class ScsiInfo(CorelensModule):
258420
"""
259421
Corelens Module for scsi device information
@@ -267,6 +429,7 @@ class ScsiInfo(CorelensModule):
267429
[
268430
"--hosts",
269431
"--devices",
432+
"--queue",
270433
]
271434
]
272435

@@ -281,11 +444,18 @@ def add_args(self, parser: argparse.ArgumentParser) -> None:
281444
action="store_true",
282445
help="Print Scsi Devices",
283446
)
447+
parser.add_argument(
448+
"--queue",
449+
action="store_true",
450+
help="Print Inflight SCSI commands",
451+
)
284452

285453
def run(self, prog: Program, args: argparse.Namespace) -> None:
286454
if args.hosts:
287455
print_scsi_hosts(prog)
288456
elif args.devices:
289457
print_shost_devs(prog)
458+
elif args.queue:
459+
print_inflight_scsi_cmnds(prog)
290460
else:
291461
print_scsi_hosts(prog)

0 commit comments

Comments
 (0)