From e72eba2ba6553a995d757f8a49a75c2475062aca Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 30 Sep 2024 22:04:18 +0000 Subject: [PATCH] Add iscsi helpers to dump iscsi session Orabug: 37362180 Signed-off-by: Richard Li --- drgn_tools/iscsi.py | 149 ++++++++++++++++++++++++++++++++++++++++++++ drgn_tools/scsi.py | 22 +++++++ tests/test_iscsi.py | 11 ++++ 3 files changed, 182 insertions(+) create mode 100644 drgn_tools/iscsi.py create mode 100644 tests/test_iscsi.py diff --git a/drgn_tools/iscsi.py b/drgn_tools/iscsi.py new file mode 100644 index 0000000..d8df35a --- /dev/null +++ b/drgn_tools/iscsi.py @@ -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) diff --git a/drgn_tools/scsi.py b/drgn_tools/scsi.py index d30f81e..3fed40d 100644 --- a/drgn_tools/scsi.py +++ b/drgn_tools/scsi.py @@ -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 diff --git a/tests/test_iscsi.py b/tests/test_iscsi.py new file mode 100644 index 0000000..e1a46a9 --- /dev/null +++ b/tests/test_iscsi.py @@ -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)