Skip to content

Commit

Permalink
Add iscsi helpers to dump iscsi session
Browse files Browse the repository at this point in the history
Orabug: 37362180
Signed-off-by: Richard Li <[email protected]>
  • Loading branch information
richl9 committed Dec 11, 2024
1 parent 63cb679 commit e72eba2
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
149 changes: 149 additions & 0 deletions drgn_tools/iscsi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Copyright (c) 2024, Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
"""
Helper for iscsi
"""
import argparse
from typing import Iterator
from typing import Tuple

from drgn import Object
from drgn import Program
from drgn.helpers.common import escape_ascii_string
from drgn.helpers.common.type import enum_type_to_class
from drgn.helpers.linux.list import list_for_each_entry

from drgn_tools.corelens import CorelensModule
from drgn_tools.scsi import for_each_scsi_host
from drgn_tools.scsi import for_each_scsi_host_device
from drgn_tools.scsi import get_scsi_device_name
from drgn_tools.scsi import host_module_name
from drgn_tools.table import print_dictionary
from drgn_tools.util import enum_name_get

ISCSI_SESSION_STATES = ["LOGGED_IN", "FAILED", "FREE"]


def for_each_iscsi_host(prog: Program) -> Iterator[Object]:
"""
Iterates through all scsi host devices and returns a
iterator of hosts backed by iscsi drivers
:returns: an iterator of ``struct Scsi_Host *``
"""
for shost in for_each_scsi_host(prog):
if "iscsi" in host_module_name(shost).lower():
yield shost


def for_each_iscsi_session(prog: Program) -> Iterator[Object]:
"""
Iterates through all iscsi_cls_session and gets the associated iscsi_session
:returns: an iterator of ``struct iscsi_session *``
"""
for cls_session in list_for_each_entry(
"struct iscsi_cls_session", prog["sesslist"].address_of_(), "sess_list"
):
yield Object(
prog,
prog.type("struct iscsi_session", filename="libiscsi.h"),
address=cls_session.dd_data,
)


def for_each_attached_device(
prog: Program, shost: Object
) -> Iterator[Tuple[Object, str]]:
"""
Iterates through all Scsi_Host and gets their associated scsi device and name
:returns: an iterator of (``struct scsi_device *``, ``str``)
"""
for scsi_dev in for_each_scsi_host_device(prog, shost):
name = get_scsi_device_name(prog, scsi_dev)
yield scsi_dev, name


def print_iscsi_report(prog: Program) -> None:
"""
Prints a comprehensive report including iscsi table and session stats. More info can be added later if needed.
"""
output = {}

for session in reversed(list(for_each_iscsi_session(prog))):
print("**********")
conn = session.leadconn

persistent_address = escape_ascii_string(
conn.persistent_address.string_()
)
persistent_port = int(conn.persistent_port)
output[
"Scsi_Host"
] = f"host{session.host.host_no.value_()} ({hex(session.host.value_())})"
output["Session"] = hex(session.address_of_())
output["SID"] = str(int(session.cls_session.sid))
output["Persistent Portal"] = f"{persistent_address}:{persistent_port}"
output["Iface Name"] = escape_ascii_string(session.ifacename.string_())
output["Session State"] = ISCSI_SESSION_STATES[
session.cls_session.state
]
connstate = enum_type_to_class(
prog.type("enum iscsi_connection_state"), "connstate"
)
output["Connection State"] = str(
enum_name_get(
connstate,
conn.cls_conn.state,
"UNKNOWN",
)
)
output["Initiatorname"] = escape_ascii_string(
session.initiatorname.string_()
)
output["Targetname"] = escape_ascii_string(
session.targetname.string_()
)

print_dictionary(output)

print("Attached SCSI devices: ")
print("**********")
print(
"Host Number: {} STATE: {}".format(
session.host.host_no.value_(),
session.host.shost_state.format_(type_name=False),
)
)

for scsi_dev, name in for_each_attached_device(prog, session.host):
print(
"scsi{} Channel {} Id {} Lun: {}".format(
session.host.host_no.value_(),
int(scsi_dev.channel),
int(scsi_dev.id),
int(scsi_dev.lun),
)
)
sdev_state = enum_type_to_class(
prog.type("enum scsi_device_state"), "sdev_state"
)
state = enum_name_get(
sdev_state,
scsi_dev.sdev_state,
"UNKNOWN",
)
print(" Attached iscsi disk: {} State: {}".format(name, state))

print()


class IscsiDump(CorelensModule):
"""
Dump iscsi info
"""

name = "iscsi"

def run(self, prog: Program, args: argparse.Namespace) -> None:
print_iscsi_report(prog)
22 changes: 22 additions & 0 deletions drgn_tools/scsi.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,28 @@ def host_module_name(shost: Object) -> str:
return name


def for_each_scsi_host_device(
prog: Program, shost: Object
) -> Iterator[Object]:
"""
Get a list of scsi_devices asscociated with an Scsi_Host
:returns: a iterator of ``struct scsi_device``
"""
return list_for_each_entry(
"struct scsi_device", shost.__devices.address_of_(), "siblings"
)


def get_scsi_device_name(prog: Program, sdev: Object) -> str:
"""
Get the device name associated with scsi_device.
:return ``str``
"""
rq = sdev.request_queue
dev = container_of(rq.kobj.parent, "struct device", "kobj")
return dev.kobj.name.string_().decode()


def print_scsi_hosts(prog: Program) -> None:
"""
Prints scsi host information
Expand Down
11 changes: 11 additions & 0 deletions tests/test_iscsi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2024, Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
from drgn_tools import iscsi
from drgn_tools.module import KernelModule


def test_iscsi(prog):
# make sure iscsi module is loaded
km = KernelModule.find(prog, "scsi_transport_iscsi")
if km:
iscsi.print_iscsi_report(prog)

0 comments on commit e72eba2

Please sign in to comment.