Skip to content

Commit b02e64e

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

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

drgn_tools/scsi.py

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

@@ -281,11 +441,18 @@ def add_args(self, parser: argparse.ArgumentParser) -> None:
281441
action="store_true",
282442
help="Print Scsi Devices",
283443
)
444+
parser.add_argument(
445+
"--queue",
446+
action="store_true",
447+
help="Print Inflight SCSI commands",
448+
)
284449

285450
def run(self, prog: Program, args: argparse.Namespace) -> None:
286451
if args.hosts:
287452
print_scsi_hosts(prog)
288453
elif args.devices:
289454
print_shost_devs(prog)
455+
elif args.queue:
456+
print_inflight_scsi_cmnds(prog)
290457
else:
291458
print_scsi_hosts(prog)

0 commit comments

Comments
 (0)