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,96 @@ 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
+ 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 \t SCSI Device Addr : { hex (scsi_device .value_ ())} "
359
+ )
360
+ print (f" Vendor : { vendor } \t Device 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
+
257
416
class ScsiInfo (CorelensModule ):
258
417
"""
259
418
Corelens Module for scsi device information
@@ -267,6 +426,7 @@ class ScsiInfo(CorelensModule):
267
426
[
268
427
"--hosts" ,
269
428
"--devices" ,
429
+ "--queue" ,
270
430
]
271
431
]
272
432
@@ -281,11 +441,18 @@ def add_args(self, parser: argparse.ArgumentParser) -> None:
281
441
action = "store_true" ,
282
442
help = "Print Scsi Devices" ,
283
443
)
444
+ parser .add_argument (
445
+ "--queue" ,
446
+ action = "store_true" ,
447
+ help = "Print Inflight SCSI commands" ,
448
+ )
284
449
285
450
def run (self , prog : Program , args : argparse .Namespace ) -> None :
286
451
if args .hosts :
287
452
print_scsi_hosts (prog )
288
453
elif args .devices :
289
454
print_shost_devs (prog )
455
+ elif args .queue :
456
+ print_inflight_scsi_cmnds (prog )
290
457
else :
291
458
print_scsi_hosts (prog )
0 commit comments