From 75f789628a4260bb296dbdc64897a22132b1b637 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 | 156 ++++++++++++++++++++++++++++++++++++++++++++ drgn_tools/scsi.py | 30 ++++++++- tests/test_iscsi.py | 7 ++ 3 files changed, 191 insertions(+), 2 deletions(-) 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..e52643b --- /dev/null +++ b/drgn_tools/iscsi.py @@ -0,0 +1,156 @@ +# 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 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.module import ensure_debuginfo +from drgn_tools.scsi import for_each_scsi_host +from drgn_tools.scsi import for_each_scsi_host_device +from drgn_tools.scsi import host_module_name +from drgn_tools.scsi import scsi_device_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]: + """ + Iterate through all scsi host devices and returns an + iterator of hosts backed by iscsi drivers. + :returns: an iterator of ``struct Scsi_Host *`` + """ + + # additional iscsi drivers + ISCSI_MODS = [ + "qla4xxx", + "bnx2i", + "cxgb3i", + "cxgb4i", + "qedi", + "iscsi_tcp", + "be2iscsi", + ] + for shost in for_each_scsi_host(prog): + mod_name = host_module_name(shost).lower() + if mod_name in ISCSI_MODS: + yield shost + + +def for_each_iscsi_session(prog: Program) -> Iterator[Object]: + """ + Iterate 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 print_iscsi_sessions(prog: Program) -> None: + """ + Dump iscsi sessions. + + """ + msg = ensure_debuginfo(prog, ["libiscsi", "scsi_transport_iscsi"]) + if msg: + print(msg) + return + + 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 in for_each_scsi_host_device(prog, session.host): + name = scsi_device_name(prog, scsi_dev) + 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 Iscsi(CorelensModule): + """ + Print iscsi sessions. + """ + + name = "iscsi" + skip_unless_have_kmod = ["libiscsi", "scsi_transport_iscsi"] + + def run(self, prog: Program, args: argparse.Namespace) -> None: + print_iscsi_sessions(prog) diff --git a/drgn_tools/scsi.py b/drgn_tools/scsi.py index d30f81e..368c41b 100644 --- a/drgn_tools/scsi.py +++ b/drgn_tools/scsi.py @@ -8,6 +8,7 @@ import drgn from drgn import container_of +from drgn import FaultError from drgn import Object from drgn import Program from drgn.helpers.linux.list import list_for_each_entry @@ -20,9 +21,9 @@ def for_each_scsi_host(prog: Program) -> Iterator[Object]: """ - Iterates through all scsi hosts and returns a + Iterate through all scsi hosts and returns an iterator. - :returns: a iterator of ``struct Scsi_Host *`` + :returns: an iterator of ``struct Scsi_Host *`` """ class_in_private = prog.type("struct device_private").has_member( "knode_class" @@ -56,6 +57,31 @@ 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_device associated with a Scsi_Host. + :returns: an iterator of ``struct scsi_device *`` + """ + return list_for_each_entry( + "struct scsi_device", shost.__devices.address_of_(), "siblings" + ) + + +def 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") + try: + return dev.kobj.name.string_().decode() + except FaultError: + return "" + + 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..fa1247c --- /dev/null +++ b/tests/test_iscsi.py @@ -0,0 +1,7 @@ +# 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 + + +def test_iscsi(prog): + iscsi.print_iscsi_sessions(prog)