8
8
import enum
9
9
from typing import Iterator
10
10
11
+ from drgn import cast
11
12
from drgn import container_of
12
13
from drgn import FaultError
13
14
from drgn import Object
14
15
from drgn import Program
16
+ from drgn import sizeof
17
+ from drgn .helpers .linux .block import for_each_disk
15
18
from drgn .helpers .linux .list import list_for_each_entry
16
19
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
17
24
from drgn_tools .corelens import CorelensModule
18
25
from drgn_tools .device import class_to_subsys
19
26
from drgn_tools .module import ensure_debuginfo
20
27
from drgn_tools .table import print_table
21
28
from drgn_tools .util import has_member
29
+ from drgn_tools .util import timestamp_str
22
30
23
31
24
32
class Opcode (enum .Enum ):
@@ -102,6 +110,67 @@ def scsi_device_name(sdev: Object) -> str:
102
110
return ""
103
111
104
112
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
+
105
174
def scsi_id (scsi_dev : Object ) -> str :
106
175
"""
107
176
Fetch SCSI id of the device.
@@ -254,6 +323,99 @@ def print_shost_devs(prog: Program) -> None:
254
323
print_table (output )
255
324
256
325
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 \t SCSI Device Addr : { hex (scsi_device .value_ ())} "
362
+ )
363
+ print (f" Vendor : { vendor } \t Device 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
+
257
419
class ScsiInfo (CorelensModule ):
258
420
"""
259
421
Corelens Module for scsi device information
@@ -267,6 +429,7 @@ class ScsiInfo(CorelensModule):
267
429
[
268
430
"--hosts" ,
269
431
"--devices" ,
432
+ "--queue" ,
270
433
]
271
434
]
272
435
@@ -281,11 +444,18 @@ def add_args(self, parser: argparse.ArgumentParser) -> None:
281
444
action = "store_true" ,
282
445
help = "Print Scsi Devices" ,
283
446
)
447
+ parser .add_argument (
448
+ "--queue" ,
449
+ action = "store_true" ,
450
+ help = "Print Inflight SCSI commands" ,
451
+ )
284
452
285
453
def run (self , prog : Program , args : argparse .Namespace ) -> None :
286
454
if args .hosts :
287
455
print_scsi_hosts (prog )
288
456
elif args .devices :
289
457
print_shost_devs (prog )
458
+ elif args .queue :
459
+ print_inflight_scsi_cmnds (prog )
290
460
else :
291
461
print_scsi_hosts (prog )
0 commit comments